#include "main.h"


tetromino playfield[FULL_HEIGHT][FULL_WIDTH] = {};

uint8_t graying[FULL_HEIGHT][FULL_WIDTH] = {};


int main () {
  

  /* Setting up the seed */
  
  srand(time(NULL));


  /* Setting up terminal with termios */
  
  struct termios old, new;

  tcgetattr(STDIN_FILENO, &old);
  new = old;

  new.c_lflag &= ~(ECHO | ICANON);
  new.c_cc[VMIN] = 0;
  new.c_cc[VTIME] = 0;

  tcsetattr(STDIN_FILENO, TCSANOW, &new);

  struct winsize init_size, current_size;
  ioctl(STDOUT_FILENO, TIOCGWINSZ, &init_size);
  current_size = init_size;

  printf("\033[?25l\033[?1049h\033[2J\033[H");


  /* Polling */

  struct pollfd polling[1];

  polling[0].fd = 0;
  polling[0].events = POLLIN;


  /* Init */

  init_field();

  bool blink[FIELD_HEIGHT] = {};

  bool is_piece = false;

  bool paused = false;

  uint64_t paused_diff;

  piece current_piece = {};
  piece next_piece = init_piece((rand() % Z_PIECE) + 1);


  /* Time keeping */

  struct timespec start, current;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);

  uint64_t time_acc = 0;

  uint16_t tick_fall = 0;
  uint16_t tick_blink = 0;
  uint16_t tick_spin = 0;


  uint8_t i, j;
  bool is_tetris = false;

  /* Game Loop */

  while (true) {

    int8_t move = 0;
    bool down = false;
    bool draw = false;
    bool rotate = false;


    /* Polling for input */

    int is_poll = poll(polling, 1, paused ? -1 : 17);


    /* Dealing with Ticks */

    uint16_t ticks = 0;

    {

      clock_gettime(CLOCK_MONOTONIC_RAW, &current);

      uint64_t time_diff = 
        (current.tv_sec - start.tv_sec) * 1000000000ULL + 
        (current.tv_nsec - start.tv_nsec) + time_acc;

      if (!paused) {

        while (time_diff >= FRAME) {

          time_diff -= FRAME;
          ticks++;
        }

        time_acc = time_diff;
      }

      start = current;
    }

    /* Take care of the input */


    if (is_poll > 0 && polling[0].revents & POLLIN) {

      char buf[10] = {};

      if (read(STDIN_FILENO, buf, sizeof(buf)) > 0) {
        
        if (tolower(buf[0]) == 'q') {
          break;
        }

        if (tolower(buf[0]) == 'p') {
          if (paused) {


            paused = false;
          } else {


            draw_pause();

            paused = true;
          }
        }


        if (buf[0] == '\033' && buf[1] == '[') {
          
          switch (buf[2]) {
            case 'A':
              rotate = true;
              break;
            case 'B':
              down = true;
              break;
            case 'C':
              move = 1;
              break;
            case 'D':
              move = -1;
              break;
            default:
              break;
          }
        }
      }
    }


    if (is_tetris) {

      tick_blink += ticks;

      while (tick_blink >= BLINK && i < 4) { 
 
        tick_blink -= BLINK;

        draw_board(current_piece, next_piece, (i % 2 == 0) ? NULL : blink, is_piece);
        i++;
      }
      

      if (i == 4) {
      
        int8_t j, k;

        for (j = 0; j < FIELD_HEIGHT; j++) {
          if (blink[j]) {

            for (k = j; k > 0; k--) {
              int8_t l;
          
              for (l = 0; l < FIELD_WIDTH; l++) {
                playfield[k][l + BORDER_WIDTH] = playfield[k - 1][l + BORDER_WIDTH];
                graying[k][l + BORDER_WIDTH] = graying[k - 1][l + BORDER_WIDTH];

              }
            }
          }
        }

        i = 0;
        is_tetris = false;
      }

      continue;
    }


    if (paused)
      continue;


    /* Creates new piece */

    
    if (!is_piece) {
      current_piece = next_piece;
      next_piece = init_piece((rand() % Z_PIECE) + 1);
      is_piece = true;
      draw = true;

      tick_fall = tick_blink = tick_spin = 0;
    }


    /* Move piece */

    if (move != 0) {
      if (
        current_piece.x + move >= 0 && 
        current_piece.x + move + current_piece.size <= FULL_WIDTH
      ) {
       
        piece moved_piece = current_piece;
        moved_piece.x += move;

        if (valid_position(moved_piece)) {
          current_piece.x += move;
          draw = true;
        }
      }
    }


    /* Piece rotates */
   
    tick_spin += ticks;

    if (rotate && current_piece.positions > 1 && tick_spin >= SPIN) {
      
      piece rotated = current_piece;

      rotated.rotation = (current_piece.rotation + 1) % (current_piece.positions);
      
      if (valid_position(rotated)) {
        current_piece = rotated;
        draw = true;
      }

      tick_spin = 0;
    } 
    

    /* Piece falls */

    tick_fall += ticks;

    while (tick_fall >= FALL || down) {
      current_piece.y++;
      

      uint8_t j, k;

      for (j = 0; j < FULL_HEIGHT; j++) {
        for (k = 0; k < FULL_WIDTH; k++) {
          if (playfield[j][k] != EMPTY) {
            
            if (graying[j][k] < 255) {
              graying[j][k] += 1;
            }

          } else {
            graying[j][k] = 0;
          }

        }
      }

      if (tick_fall >= FALL)
        tick_fall -= FALL;
      
      down = false;

      draw = true;
    }
    


    /* When a piece is stuck */

    if (!valid_position(current_piece)) {

      int8_t j, k;

      for (j = 0; j < current_piece.size; j++) {
        for (k = 0; k < current_piece.size; k++) {
          
          if (current_piece.piece[j][k]) {
            
            coord solid = find_direction(current_piece, k, j);

            playfield[current_piece.y - 1 + solid.y][current_piece.x + solid.x] = current_piece.name;
          }
        }
      }
      is_piece = false;
      draw = true;
    }


    /* Looks if there is a tetris */

    int8_t j, k;

    for (j = FIELD_HEIGHT - 1; j >= 0; j--) {

      uint8_t blocked = 0;

      for (k = 0; k < FIELD_WIDTH; k++) {
        if (playfield[j][k + BORDER_WIDTH] != EMPTY)
          blocked++;
      }

      if (blocked == 10) {
        
        blink[j] = true;
        is_tetris = true;  
      } else {
        blink[j] = false;
      }
    }


    /* Looks for game over */

    for (j = 0; j < FIELD_WIDTH; j++) {
      if (playfield[TOP_GAP][j + BORDER_WIDTH] > 0) {
        draw_board(current_piece, next_piece, NULL, is_piece);
        sleep(2);
        printf("\033[2J\033[H");
        printf("HAHA LOSER\n");
        sleep(1);
        goto end;
      }
    }


    if (draw)
      draw_board(current_piece, next_piece, NULL, is_piece);
  }

end:

  printf("\033[?1049l\033[?25h");

  tcsetattr(STDIN_FILENO, TCSANOW, &old);

  return 0;
}
