#include "snake.h"


bool draw_menu(main_menu menu, struct winsize window_size) {

  char *menu_string[] = {"NEW GAME", "SETTINGS", "HIGH SCORE", "EXIT"};

  const uint8_t width_small = 10;
  const uint8_t height_small = 5;

  char *menu_string_big[][6] = {
      {"███╗   ██╗███████╗██╗    ██╗     ██████╗  █████╗ ███╗   ███╗███████╗",
       "████╗  ██║██╔════╝██║    ██║    ██╔════╝ ██╔══██╗████╗ ████║██╔════╝",
       "██╔██╗ ██║█████╗  ██║ █╗ ██║    ██║  ███╗███████║██╔████╔██║█████╗  ",
       "██║╚██╗██║██╔══╝  ██║███╗██║    ██║   ██║██╔══██║██║╚██╔╝██║██╔══╝  ",
       "██║ ╚████║███████╗╚███╔███╔╝    ╚██████╔╝██║  ██║██║ ╚═╝ ██║███████╗",
       "╚═╝  ╚═══╝╚══════╝ ╚══╝╚══╝      ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝"},
      {"███████╗███████╗████████╗████████╗██╗███╗   ██╗ ██████╗ ███████╗",
       "██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██║████╗  ██║██╔════╝ ██╔════╝",
       "███████╗█████╗     ██║      ██║   ██║██╔██╗ ██║██║  ███╗███████╗",
       "╚════██║██╔══╝     ██║      ██║   ██║██║╚██╗██║██║   ██║╚════██║",
       "███████║███████╗   ██║      ██║   ██║██║ ╚████║╚██████╔╝███████║",
       "╚══════╝╚══════╝   ╚═╝      ╚═╝   ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝"},
      {"██╗  ██╗██╗ ██████╗ ██╗  ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗",
       "██║  ██║██║██╔════╝ ██║  ██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝",
       "███████║██║██║  ███╗███████║███████╗██║     ██║   ██║██████╔╝█████╗  ",
       "██╔══██║██║██║   ██║██╔══██║╚════██║██║     ██║   ██║██╔══██╗██╔══╝  ",
       "██║  ██║██║╚██████╔╝██║  ██║███████║╚██████╗╚██████╔╝██║  ██║███████╗",
       "╚═╝  ╚═╝╚═╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝"},
      {"███████╗██╗  ██╗██╗████████╗", 
       "██╔════╝╚██╗██╔╝██║╚══██╔══╝",
       "█████╗   ╚███╔╝ ██║   ██║   ", 
       "██╔══╝   ██╔██╗ ██║   ██║   ",
       "███████╗██╔╝ ██╗██║   ██║   ", 
       "╚══════╝╚═╝  ╚═╝╚═╝   ╚═╝   "}
  };

  const uint8_t height_line = 6;
  const uint8_t height_space = 2;

  const uint8_t width_text[] = {68, 64, 69, 28};

  const uint8_t width_big = width_text[2] /*strlen(menu_string_big[0][0])*/;
  const uint8_t height_big =
      height_line * MAIN_MENU + height_space * (MAIN_MENU - 1);

  printf("\033[2J\033[H");

  if (window_size.ws_row < height_small || window_size.ws_col < width_small) {
    printf("Too small\n");
    return true;
  }

  uint8_t i, j, k;

  if (window_size.ws_row >= height_big && window_size.ws_col >= width_big) {

    uint8_t vertical_buffer = (window_size.ws_row - height_big) >> 1;

    for (i = 0; i < vertical_buffer; i++)
      printf("\n");

    for (i = 0; i < MAIN_MENU; i++) {

      if (i == menu)
        printf("\033[32m");

      uint8_t horizontal_buffer = (window_size.ws_col - width_text[i]) >> 1;

      for (j = 0; j < height_line; j++) {

        for (k = 0; k < horizontal_buffer; k++)
          printf(" ");

        printf("%s", menu_string_big[i][j]);

        if (j != height_line - 1)
          printf("\n");
      }

      printf("\033[0m");

      if (i != MAIN_MENU - 1)
        printf("\n\n");
    }

  } else {

    uint8_t vertical_buffer = (window_size.ws_row - height_small) >> 1;

    for (i = 0; i < vertical_buffer; i++)
      printf("\n");

    for (i = 0; i < MAIN_MENU; i++) {
      if (i == menu)
        printf("\033[32m");

      uint8_t horizontal_buffer =
          (window_size.ws_col - strlen(menu_string[i])) >> 1;

      for (j = 0; j < horizontal_buffer; j++)
        printf(" ");

      printf("%s\033[0m", menu_string[i]);

      if (i != MAIN_MENU - 1)
        printf("\n\n");
    }
  }

  fflush(stdout);
  return false;
}


void draw_settings(settings selected_setting, speed game_speed, selected game_size, struct winsize window_size) {

  char *title[] = {
    " ___ ___ _____ _____ ___ _  _  ___ ___ ",
    "/ __| __|_   _|_   _|_ _| \\| |/ __/ __|",
    "\\__ \\ _|  | |   | |  | || .` | (_ \\__ \\",
    "|___/___| |_|   |_| |___|_|\\_|\\___|___/",
                                        
  };

  char *setting_string[] = {
    "HEIGHT",
    "WIDTH",
    "SPEED",
    "BACK"
  };

  char *speed_char[] = {
    "VERY FAST",
    "FAST",
    "AVERAGE",
    "SLOW",
    "VERY SLOW"
  };


  uint8_t title_height = 4;
  uint8_t title_width = strlen(title[0]);

  
  uint8_t setting_width;
  if (title_width % 2 == 0) {
    setting_width = (title_width + 2) >> 1;
  } else {
    setting_width = (title_width + 1) >> 1;
  }

  uint8_t settings_height = SETTINGS_MENU * 2 + 1;

  uint16_t full_height = title_height + settings_height + 2;
  uint16_t full_width = setting_width * 2 + 3;

  uint8_t i, j;

  printf("\033[2J\033[H");

  if (window_size.ws_col < full_width || window_size.ws_row < full_height) {
    printf("Too small!\n");
    printf("Min: x: %d y: %d", full_width, full_height);
    fflush(stdout);
    return;
  }

  uint8_t vertical_buffer = (window_size.ws_row - full_height) >> 1;
  uint8_t horizontal_buffer = (window_size.ws_col - full_width) >> 1;

  for(i = 0; i < vertical_buffer; i++)
    printf("\n");


  for(i = 0; i < title_height + 2; i++) {
    for (j = 0; j < horizontal_buffer; j++)
      printf(" ");
    if (!i) {
      printf("┏");
      for(j = 0; j < full_width - 2; j++)
        printf("━");
      printf("┓\n");
    } else {
      printf("┃ ");
      if (i == title_height + 1) {
        for(j = 0; j < title_width; j++)
          printf(" ");
      } else {
        printf("\033[31;1m%s\033[0m", title[i - 1]);
      }
      if (title_width % 2 == 0)
        printf(" ");
      printf(" ┃\n");
    }
  }
   
  for (i = 0; i < settings_height; i++) {

    for (j = 0; j < horizontal_buffer; j++)
      printf(" ");

    if (i % 2 == 0) {

      if (i == 0) {
        printf("┣");
        for(j = 0; j < setting_width; j++)
          printf("━");
        printf("┯");
        for(j = 0; j < setting_width; j++)
          printf("━");
        printf("┫\n");

      } else if (i == settings_height - 1) {
        printf("┗");
        for(j = 0; j < setting_width * 2 + 1; j++)
          printf("━");
        printf("┛");

      } else {
      
        printf("┠");
        for(j = 0; j < setting_width; j++)
          printf("─");
        printf("%s", i == settings_height - 3 ? "┴" : "┼");
        for(j = 0; j < setting_width; j++)
          printf("─");
        printf("┨\n");
      }

    } else {

      settings current_setting = (i - 1) >> 1;

      printf("┃");

      if (current_setting == selected_setting)
        printf("\033[32m");

      print_buffer(current_setting != BACK_S ? setting_width : 2 * setting_width + 1, 
        setting_string[current_setting]);
      
      printf("\033[0m");

      if (current_setting != BACK_S) {

        printf("│");

        if (current_setting == selected_setting)
          printf("\033[32m");

        char *pointer;

        char number[5];

        switch (current_setting) {
          case SPEED_S:
            pointer = speed_char[game_speed];
            break;
          case WIDTH_S:
            sprintf(number, "%d", game_size.x);
            pointer = number;
            break;
          case HEIGHT_S:
            sprintf(number, "%d", game_size.y);
            pointer = number;
            break;
          default:
            break;
        }

        print_buffer(setting_width, pointer);
      }

      printf("\033[0m┃\n");
    }
  }

  fflush(stdout);
}

bool draw_board(
  selected game_size,
  board_state **board,
  struct winsize window_size, 
  bool redraw, uint8_t paused,
  uint16_t score
) {

  uint8_t i, j;

  uint8_t actual_height = game_size.y + 2 + 2 + 1;
  uint8_t actual_width = game_size.x * 2 + 1 + 2;


  if (actual_width > window_size.ws_col || actual_height > window_size.ws_row) {

    printf("\033[2J\033[HToo small\n");
    printf("Min width: %i & Min height: %i\n", actual_height, actual_width);

    return true;
  }

  uint8_t vertical_buffer = (window_size.ws_row - actual_height) >> 1;
  uint8_t horizontal_buffer = (window_size.ws_col - actual_width) >> 1;

  if (redraw) {
    printf("\033[2J\033[H");
    for (i = 0; i < vertical_buffer; i++)
      printf("\n");
  } else {
    for (i = 0; i < actual_height - 1; i++)
      printf("\033[%c", UP_CODE);
    printf("\r");
  }
  
  for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
    printf(" ");
  printf("╭───────╮\n");
    for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
    printf(" ");
  printf("│ \033[32m%05d\033[0m │\n", score);

  for (i = 0; i < horizontal_buffer; i++)
    printf(" ");
  printf("╭");
  for (i = 0; i < game_size.x; i++)
    printf("──");
  printf("─╮");
  printf("\r");
  for (i = 0; i < ((actual_width - 9) >> 1) + horizontal_buffer; i++)
    printf("\033[%c", RIGHT_CODE);
  printf("┴───────┴\n");



  for (i = 0; i < game_size.y; i++) {

    for (j = 0; j < horizontal_buffer; j++)
      printf(" ");

    printf("│");

    for (j = 0; j < game_size.x; j++)
      printf("%s", give_state(board[i][j]));

    printf("%s", give_state(board[i][0]));

    printf("\033[%c│\n", LEFT_CODE);
  }

  for (i = 0; i < horizontal_buffer; i++)
    printf(" ");
  printf("╰");
  for (i = 0; i < game_size.x; i++)
    printf("──");
  printf("─╯\n");

  if (paused) {
    if (paused == 2) {
      char *game_over = "Press ENTER to continue";
    
      for (i = 0; i < (window_size.ws_col - strlen(game_over)) >> 1; i++)
        printf(" ");
      printf("%s", game_over);
    }

    draw_pause(window_size, actual_width, actual_height, paused);

  }

  fflush(stdout);

  return false;
}

void draw_pause(struct winsize window_size, uint8_t field_width,
  uint8_t field_height, uint8_t game_over) {

  const uint8_t height = 5;
  const uint8_t width = 14;

  const char *pause_menu[] = {
    "┌────────────┐", 
    "│            │",
    "│   PAUSED   │",
    "│            │",
    "└────────────┘"
  };

  const char *game_over_menu = "│ GAME OVER  │";

  const uint8_t going_up = (field_height + height) >> 1;
  const uint8_t going_right = (field_width - width) >> 1;

  const uint8_t horizontal_buffer = (window_size.ws_col - field_width) >> 1;

  uint8_t i, j;

  for (i = 0; i < going_up; i++)
    printf("\033[%c", UP_CODE);
  printf("\r");

  for (i = 0; i < height; i++) {
    for (j = 0; j < horizontal_buffer; j++)
      printf("\033[%c", RIGHT_CODE);
    for (j = 0; j < going_right; j++)
      printf("\033[%c", RIGHT_CODE);

    if (i == 2 && game_over == 2) {
      printf("\033[37m%s\033[0m\033[%c\r", game_over_menu, DOWN_CODE);
    } else {
      printf("\033[37m%s\033[0m\033[%c\r", pause_menu[i], DOWN_CODE);
    }
  }

  for (i = 0; i < going_up - height; i++)
    printf("\033[%c", DOWN_CODE);
  printf("\r");

  fflush(stdout);
}

void draw_highscore (int16_t new_score, highscore leader_board[SCORE_NUMBER], struct winsize window_size) {
  
  uint8_t i, j;

  char *header[] = {
    "   _  _ ___ ___ _  _   ",
    "  | || |_ _/ __| || |  ",
    "  | __ || | (_ | __ |  ",
    " _|_||_|___\\___|_||_|_ ",
    "/ __|/ __/ _ \\| _ \\ __|",
    "\\__ \\ (_| (_) |   / _| ",
    "|___/\\___\\___/|_|_\\___|",
    "                       "
  };

  uint8_t header_height = 8;
  uint8_t header_width = strlen(header[0]);

  uint8_t full_height = header_height + SCORE_NUMBER + 5 + 1;
  uint8_t full_width = header_width + 4;

  uint8_t gap_name = 9;

  printf("\033[2J\033[H");

  if (window_size.ws_col < full_width || window_size.ws_row < full_height) {
    printf("Too small\n");
    printf("Min size: x: %d y: %d", full_width, full_height);
    fflush(stdout);
    return;
  }

  for (i = 0; i < (window_size.ws_row - full_height) >> 1; i++)
    printf("\n");

  for (i = 0; i < full_height - 1; i++) {
    
    for (j = 0; j < (window_size.ws_col - full_width) >> 1; j++)
      printf(" ");

    if (!i) {
      printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
    } else if (i < header_height + 1) {
      printf("┃ \033[31;1m%s\033[0m ┃\n", header[i - 1]);
    } else if(i < header_height + 2) {
      printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
    } else if (i < header_height + 3) {
      printf("┃ RANK     NAME    SCORE  ┃\n");
    } else if (i < header_height + 4) {
      printf("┠─────────────────────────┨\n");
    } else if (i < header_height + 4 + SCORE_NUMBER) { 
      uint8_t pos = i - header_height - 4;
      char *ordinals[] = {
        "st", "nd", "rd", "th"
      };

      printf("┃ \033[%dm%2d%-4s %-9s %06d\033[0m ┃\n", (pos == new_score) ? 32 : 0 ,pos + 1, (pos >= 3) ? ordinals[3] : ordinals[pos] ,leader_board[pos].name, leader_board[pos].points);
    } else {
      printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
    }
  }

  fflush(stdout);

  if (new_score != -1) {
 
    char *input = "Put your Name in";

    for (i = 0; i < (window_size.ws_col - strlen(input)) >> 1; i++)
      printf(" ");
    printf("%s", input);


    for (i = (SCORE_NUMBER - new_score) + 1; i > 0; i--)
      printf("\033[%c", UP_CODE);
    printf("\r");
    for (i = 0; i < (window_size.ws_col - full_width) >> 1; i++)
      printf(" ");
    for (i = 0; i < gap_name; i++)
      printf("\033[%c", RIGHT_CODE);
    printf("_");
    fflush(stdout);

    uint8_t number_characters = 0;

    while(true) {
      char string[STRING_LEN] = {};
      ssize_t length_string = read(STDIN_FILENO, string, sizeof(string) - 1);

      if (length_string > 0) {
        
        direction pressed_key = read_direction(string, false);

        if (pressed_key == ENTER) {
          leader_board[new_score].name[number_characters] = '\0';
          break;        
        }
         
        if (pressed_key == DEL && number_characters > 0) {
          printf("\033[%c \033[%c\033[%c_", LEFT_CODE, LEFT_CODE, LEFT_CODE);
          leader_board[new_score].name[--number_characters] = '\0';
        }

        if (isalnum(string[0]) && number_characters < SCORE_LEN - 2) {
          printf("\033[%c%c_", LEFT_CODE, string[0]);
          leader_board[new_score].name[number_characters++] = string[0];
        }
      }
      fflush(stdout);
      struct timespec mili100 = {0, 1000000 * 100};
      nanosleep(&mili100, NULL);
    }
  } else {

    char *back = "BACK";

    for (i = 0; i < (window_size.ws_col - strlen(back)) >> 1; i++)
      printf(" ");
    printf("\033[32m%s\033[0m", back);
    fflush(stdout);
  }
}


char *give_state(board_state square) {

  switch (square) {
    case FRUIT:
      return "\033[32m O\033[0m";
      break;
    case HEAD_1:
      return "\033[33m═\033[31mX\033[0m";
      break;
    case HEAD_2:
      return "\033[31m\33[CX\033[0m";
      break;    
    case VERTICAL:
      return "\033[33m ║\033[0m";
      break;
    case HORIZONTAL:
      return "\033[33m══\033[0m";
      break;
    case TOP_RIGHT:
      return "\033[33m ╚\033[0m";
      break;
    case TOP_LEFT:
      return "\033[33m═╝\033[0m";
      break;
    case BOTTOM_RIGHT:
      return "\033[33m ╔\033[0m";
      break;
    case BOTTOM_LEFT:
      return "\033[33m═╗\033[0m";
      break;
    default:
      return " ·";
      break;
  }
}

bool print_buffer(uint16_t width, char *string) {
  uint16_t len = strlen(string);
  uint16_t i;

  if (width < len)
    return false;

  uint16_t prebuffer = (width - len) >> 1; 
  uint16_t postbuffer = width - len - prebuffer; 

  for (i = 0; i < prebuffer; i++)
    printf(" ");
  printf("%s", string);
  for (i = 0; i < postbuffer; i++)
    printf(" ");

  return true;
}
