/*
 * YAPong.c 0.02 - Yet Another pong clone, made by //YorHel
 * 
 * Copyright 2006 Y.Heling.
 * 2006-02-23
 * License: MIT
 */

#include <stdio.h>
#include <stdlib.h>

#include <curses.h>

#define DI_UP 1
#define DI_DOWN 2

#define DI_RIGHT 3
#define DI_LEFT 4

void movepadel(char, char);
void drawpadels();
void updatescore();
void drawframe();
void updatedot();
void movedot();
int print_help(char *);

int winrows, wincols, padsize, padposL, padposR, scoreL,
    scoreR, dotX, dotY, delay, padstep, dotstep;
char direX, direY, started, paused, scored, autosize, autopsize;
WINDOW *win;

int main(int argc, char *argv[]) {
 /* defaults */
  autosize = 1;
  autopsize = 1;
  padstep = 1;
  dotstep = 1;
  winrows = 25; wincols = 80;
  padsize = 0;
  delay = 50;
 /* args */
  {
    char *name = *argv++;
    char *ch;
    while(ch = *argv++) {
      if(!strcmp(ch, "-h"))
        return print_help(name);
      else if(!strcmp(ch, "-l")) {
        int level = atoi(*argv++);
        switch(level) {
          case 1 :  delay = 100; padsize = 6; padstep = 2; dotstep = 1;  break;
          case 2 :  delay =  80; padsize = 6; padstep = 2; dotstep = 1;  break;
          case 3 :  delay =  65; padsize = 5; padstep = 1; dotstep = 1;  break;
          case 4 :  delay =  50; padsize = 3; padstep = 1; dotstep = 1;  break;
          case 5 :  delay =  80; padsize = 3; padstep = 1; dotstep = 2;  break;
          default :
            printf("ERROR: unknown level %d\n", level);
            return 0; break;
        }
      }
      else if(!strcmp(ch, "-d")) {
        delay = atoi(*argv++);
        if(delay >= 1000 || delay == 0) {
          printf("ERROR: delay should be an integer between 0 and 1000\n");
          return 1;
        }
      }
      else if(!strcmp(ch, "-p")) {
        if(!strcmp(*argv, "auto")) {
          autopsize = 1; *argv++;
        } else {
          autopsize = 0;
          padsize = atoi(*argv++);
          if(padsize > wincols-3 || padsize == 0) {
            printf("ERROR: padel size should at least be 1 and should not be too high\n");
            return 1;
          }
        }
      }
      else if(!strcmp(ch, "-s")) {
        if(!strcmp(*argv, "auto")) {
          autosize = 1; *argv++;
        } else {
          wincols = atoi(*argv++);
          winrows = atoi(*argv++);
          autosize = 0;
          if(wincols < 20 || winrows < 5) {
            printf("ERROR: Window size should at least be 20x5\n");
            return 1;
          }
        }
      }
      else if(!strcmp(ch, "-ps")) {
        padstep = atoi(*argv++);
        if(padstep <= 0) {
          printf("ERROR: padstep should at least be 1\n");
          return 1;
        }
      }
      else if(!strcmp(ch, "-ds")) {
        dotstep = atoi(*argv++);
        if(dotstep <= 0) {
          printf("ERROR: dotstep should be a positive integer\n");
          return 1;
        }
      }
      else {
        printf("ERROR: no such option: %s\n", ch);
        return print_help(name);
      }
    }
  }
 
 /* initialize */
  if( (win = initscr()) == NULL) {
    fprintf(stderr, "ERROR: Can't initialize ncurses\n");
    return(1);
  }
  cbreak(); /* imidiatly get input chars */
  noecho(); /* don't print input chars */
  keypad(stdscr, TRUE); /* also get other keys */
  curs_set(0); /* hide cursor */
  nodelay(stdscr, TRUE);
 
  if(autosize)
    getmaxyx(stdscr, winrows, wincols);
  if(autopsize)
    padsize = ((winrows-3)/10)*2;
  
 /* non-changable stuff */
  padposL = padposR = ((winrows/2)-(padsize/2));
  scoreL = scoreR = 0;
  dotX = (wincols/2);
  dotY = (winrows/2)-1;
  direX = DI_RIGHT;
  direY = DI_DOWN;
  started = paused = 0;
  
 /* draw stuff */
  drawframe();
  drawpadels();
  mvaddch(dotY, dotX, '0');
  refresh();

 /* main loop */
  delay = delay/15; /* assume napms() takes 50% more time */
  int ch; int d = 0;
  while(1) {
    ch = getch();
    switch(ch) {
      case ERR : break;
      case KEY_UP :
        movepadel('r', 'u'); break;
      case KEY_DOWN :
        movepadel('r', 'd'); break;
      case 'a' :
        movepadel('l', 'u'); break;
      case 'z' :
        movepadel('l', 'd'); break;
      case ' ' :
        if(!started)
          started = 1;
        else if(scored) {
          scored = 0;
          mvaddch(dotY, dotX, ' ');
          dotX = (wincols/2);
          dotY = (winrows/2)-1;
          refresh();
        } else if(!paused)
          paused = 1;
        else if(paused)
          paused = 0;
        break;
      case 'q' :
        goto end;
        break;
    }
    if(++d == delay) {
      d = 0;
      movedot();
    }
    napms(10);
  }
  
 /* cleanup */
  end:
  erase();
  mvaddstr(0, 0, "Thank you for playing Yet Another pong game. :)\nPlease visit http://yorhel.nl/ when your even more bored...\n");
  refresh();
  delwin(win);
  endwin();
  refresh(); 

  return 0;
}

void movepadel(char which, char direct) {
  if(paused || scored || !started)
    return;
  int *pos = which == 'r' ? &padposR : &padposL;
  *pos += direct == 'u' ? -1*padstep : padstep;
  if(*pos < 2)
    *pos = 2;
  if(*pos > winrows-padsize-2)
    *pos = winrows-padsize-2;
  drawpadels();
  move(wincols-1, 0);    
  refresh();
}

void drawframe() {
  mvaddstr(0, 3, "left");
  mvaddstr(0, wincols-8, "right");
  mvaddstr(0, ((wincols/2)-7), "YAPong by //YorHel");
  move(1, 0);
  hline(ACS_HLINE, wincols);
  move(winrows-1, 0);
  hline(ACS_HLINE, wincols);
  updatescore();
}

void updatescore() {
  char scl[3], scr[3];
  sprintf(scl, "%02d", scoreL);
  sprintf(scr, "%02d", scoreR);
  mvaddstr(0, 0, scl);
  mvaddstr(0, wincols-2, scr);
}

void drawpadels() {
  int c;
  for(c=winrows; c--; ) {
    if(c < 2 || c >= winrows-1)
      continue;
   /* left paddel */
    if(c >= padposL && c <= padposL+padsize)
      mvaddch(c, 0, ' ' | A_REVERSE);
    else 
      mvaddch(c, 0, ' ');
   /* right paddel */
    if(c >= padposR && c <= padposR+padsize)
      mvaddch(c, wincols-1, ' ' | A_REVERSE);
    else
      mvaddch(c, wincols-1, ' ');
  }
}

void movedot() {
  if(!started || paused || scored)
    return;
  
  mvaddch(dotY, dotX, ' ');
  dotX += direX == DI_LEFT ? -1*dotstep : dotstep;
  dotY += direY == DI_UP   ? -1*dotstep : dotstep;
  
  if(direY == DI_UP && dotY <= 2) {
    direY = DI_DOWN;
    dotY = 2;
  }
  if(direY == DI_DOWN && dotY >= winrows-2) {
    direY = DI_UP;
    dotY = winrows-2;
  }
  
  if(direX == DI_RIGHT && dotX >= wincols-2) {
    direX = DI_LEFT;
    dotX = wincols-2;
    if(dotY < padposR || dotY > padposR + padsize) {
      scored = 1;
      scoreL++;
      direX = DI_LEFT;
      updatescore();
    }
  }
  if(direX == DI_LEFT && dotX <= 1) {
    direX = DI_RIGHT;
    dotX = 1;
    if(dotY < padposL || dotY > padposL + padsize) {
      scored = 1;
      scoreR++;
      direX = DI_RIGHT;
      updatescore();
    }
  }

  mvaddch(dotY, dotX, '0');
  move(wincols-1, 0);  
  refresh();
}

int print_help(char *name) {
  printf("Usage: %s [options]\n\n", name);
  printf("Options:\n");
  printf("  -h        This help message.\n");
  printf("  -d int    Specifies the interval between every move\n");
  printf("            of the dot. You can speed up the game by\n");
  printf("            setting this to a lower value.\n");
  printf("  -p int    Specifies the size of each padel in pixels,\n");
  printf("            set to auto to automatically calculate the\n");
  printf("            size using the window size.\n");
  printf("  -s x y    Specifies the size of the window, or set\n");
  printf("            to 'auto' to use the size of your current\n");
  printf("            terminal. Recommended size: 80x25, default:\n");
  printf("            auto.\n");
  printf("  -ps int   Specifies the amount of pixels the padels\n");
  printf("            move each time\n");
  printf("  -ds int   The amount of pixels the dot moves each\n");
  printf("            time, setting this to a larger value\n");
  printf("            increases game speed a lot.\n");
  printf("  -l level  The game-level, higher levels are more\n");
  printf("            difficult. See below for more information\n\n");

  printf("Levels:\n");
  printf(" option     equals to\n");
  printf("  -l 1      -d 100 -p 6 -ps 2 -ds 1\n");
  printf("  -l 2      -d 80 -p 6 -ps 2 -ds 1\n");
  printf("  -l 3      -d 65 -p 5 -ps 1 -ds 1\n");
  printf("  -l 4      -d 50 -p 3 -ps 1 -ds 1\n");
  printf("  -l 5      -d 80 -p 3 -ps 1 -ds 2\n");
  printf("  Please note that all the levels are meant for a 80x25\n");
  printf("  window size, level 5 might be very easy with a higher\n");
  printf("  window size. In that case you should change the\n");
  printf("  the options yourself.\n\n");

  printf("Ingame keys:\n");
  printf("  space     Start/pause/resume game\n");
  printf("  key_up    Move right padel up\n");
  printf("  key_down  Move right padel down\n");
  printf("  a         Move left padel up\n");
  printf("  z         Move left padel down\n");
  printf("  q         Quit game\n");
  return 0;
}

