#include "main.h"


tetromino playfield[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");


  /* Polling */

  struct pollfd polling[1];

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


  /* Init */

  init_field();

  bool blink[FIELD_HEIGHT] = {};

  bool is_piece = false;

  piece current_piece = {};


  /* Game Loop */

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


  uint8_t i;
  bool is_tetris = false;

  while (true) {

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

    clock_gettime(CLOCK_MONOTONIC_RAW, &current);

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


    /* Take care of the input */

    int is_poll = poll(polling, 1, 17);

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

      char buf[10] = {};

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

        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) {

      if (time_diff >= FRAME * BLINK && i < 4) { 

        time_diff -= time_diff % (FRAME * BLINK);
        start.tv_sec += time_diff / 1000000000ULL;
        start.tv_nsec += time_diff % 1000000000ULL;
 
        draw_board(current_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];
              }
            }
          }
        }

        i = 0;
        is_tetris = false;
      }

      continue;
    }



    /* Creates new piece */

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


    /* 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 */
    
    if (rotate && current_piece.positions > 1) {
      
      piece rotated = current_piece;

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

    /* Piece falls */

    if (time_diff >= (FRAME * FALL) || down) {
      current_piece.y++;
      
      time_diff -= time_diff % (FRAME * FALL);
      start.tv_sec += time_diff / 1000000000ULL;
      start.tv_nsec += time_diff % 1000000000ULL;
    

      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, NULL, is_piece);
        sleep(2);
        printf("\033[2J\033[H");
        printf("HAHA LOSER\n");
        sleep(1);
        goto end;
      }
    }


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

end:

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

  tcsetattr(STDIN_FILENO, TCSANOW, &old);

  return 0;
}
