#include <string.h>
#include <stdio.h>

#include "game.h"
#include "card.h"
#include "teyaku.h"
#include "dekiyaku.h"
#include "field_multiplier.h"
#include "special_cases.h"
#include "play.h"
#include "dialog.h"

Vector2 mouse_pos;
char teyaku_calculation[400];

void initialize_game(Game *g) {
  Image cards_image = LoadImage("img/cards.png");
  g->cards_texture = LoadTextureFromImage(cards_image);
  UnloadImage(cards_image);

  g->deck.count = 0;
  g->deck.position = (Vector2) { 800, 400 };
  g->deck.display_type = HAND_DISPLAY_DECK;
  g->should_close = false;
  g->state = GAME_STATE_INITIALIZING;
  g->field_multiplier = NULL;
  g->kan_value = 12;
  g->dialog = NULL;

  init_dialogs(g);

  for (int i = 0; i < 48; i++) {
    CardType t = CHAFF;
    RibbonType rt = RIBBON_NONE;
    Month month = i / 4;
    switch (i) {
    case 0:
    case 8:
    case 28:
    case 40:
    case 44:
      t = BRIGHT; break;
    case 1:
    case 5:
    case 9:
      t = RIBBON; rt = RIBBON_POETRY; break;
    case 21:
    case 33:
    case 37:
      t = RIBBON; rt = RIBBON_BLUE; break;
    case 13:
    case 17:
    case 25:
    case 42:
      t = RIBBON; rt = RIBBON_PLAIN; break;
    case 4:
    case 12:
    case 16:
    case 20:
    case 24:
    case 29:
    case 32:
    case 36:
    case 41:
      t = ANIMAL; break;
    }
    g->cards[i] = (Card) { i, t, rt, month, { 800, 100 }, false };
    g->cards[i].move.end_time = 0.;
    g->cards[i].move.position = &g->cards[i].position;
    g->cards[i].move.destination = (Vector2) { 800, 400 };
    g->cards[i].order = i;
    g->cards[i].selected = false;
  }

  shuffle_hand(&g->deck);

  g->player.points = 10 * g->kan_value;
  g->right.points = 10 * g->kan_value;
  g->left.points = 10 * g->kan_value;
  g->player.points_string[0] = '\0';
  g->right.points_string[0] = '\0';
  g->left.points_string[0] = '\0';

  g->player.teyaku.calculated = false;
  g->right.teyaku.calculated = false;
  g->left.teyaku.calculated = false;

  g->player.hand.position = (Vector2) { 300, 600 };
  g->player.hand.display_type = HAND_DISPLAY_ROW;
  g->right.hand.position = (Vector2) { 750, 125 };
  g->right.hand.display_type = HAND_DISPLAY_ROW;
  g->left.hand.position = (Vector2) { 50, 125 };
  g->left.hand.display_type = HAND_DISPLAY_ROW;
  g->field.position = (Vector2) { 400, 300 };
  g->field.display_type = HAND_DISPLAY_FIELD;
  g->player.scored.position = (Vector2) { 300, 750 };
  g->player.scored.display_type = HAND_DISPLAY_SCORED;
  g->right.scored.position = (Vector2) { 750, 25 };
  g->right.scored.display_type = HAND_DISPLAY_SCORED;
  g->left.scored.position = (Vector2) { 50, 25 };
  g->left.scored.display_type = HAND_DISPLAY_SCORED;

  strcpy(teyaku_calculation, "");

  g->state = GAME_STATE_INITIALIZING;
}

Player *current_player(Game *g) {
  switch (g->turn_number % 3) {
  case 0:
    return &g->player;
  case 1:
    return &g->right;
  case 2:
    return &g->left;
  }

  return NULL;
}

bool is_player_turn(Game *g) {
  return current_player(g) == &g->player;
}

void handle_input(Game *g) {
  if (!is_player_turn(g)) return;
  if (g->dialog) return dialog_handle_input(g->dialog);

  switch (g->state) {
  case GAME_STATE_CHOOSING_FROM_HAND:
    if (IsMouseButtonPressed(0)) {
      mouse_pos = GetMousePosition();
      for (int i = 0; i < g->player.hand.count; i++) {
	if (point_within_card(g->player.hand.cards[i], mouse_pos)) {
	  for (int j = 0; j < 48; j++) {
	    g->cards[j].selected = false;
	  }
	  g->player.hand.cards[i]->selected = true;
	  break;
	}
      }
    }
    break;
  case GAME_STATE_CHOOSING_TARGET:
    if (IsMouseButtonPressed(0)) {
      mouse_pos = GetMousePosition();
      for (int i = 0; i < g->player.hand.count; i++) {
	if (point_within_card(g->player.hand.cards[i], mouse_pos)) {
	  if (g->player.hand.cards[i]->selected) {
	    g->player.hand.cards[i]->selected = false;
	  } else {
	    for (int j = 0; j < g->player.hand.count; j++) {
	      g->player.hand.cards[j]->selected = false;
	    }
	    g->player.hand.cards[i]->selected = true;
	  }
	}
      }

      Card *selected_card = NULL;
      for (int i = 0; i < g->player.hand.count; i++) {
	if (g->player.hand.cards[i]->selected) {
	  selected_card = g->player.hand.cards[i];
	  break;
	}
      }

      for (int i = 0; i < g->field.count; i++) {
	if (point_within_card(g->field.cards[i], mouse_pos)) {
	  if (selected_card == NULL) printf("No card selected, whoops");
	  if (valid_play(&g->field, selected_card, g->field.cards[i])) {
	    g->current_play_from_hand = selected_card;
	    g->current_play_target = g->field.cards[i];
	  } else {
	    printf("Invalid\n");
	  }
	  break;
	}
      }

      if (CheckCollisionPointRec(mouse_pos, next_card_position(&g->field))) {
	if (valid_play(&g->field, selected_card, NULL)) {
	  g->current_play_from_hand = selected_card;
	  g->current_play_target = NULL;
	} else {
	  printf("Invalid\n");
	}
      }
    }
    break;
  default:
    break;
  }
}

void run_frame_ai_playing(Game *g) {
  Hand *hand = &current_player(g)->hand;
  Hand *scored = &current_player(g)->scored;

  Play play = ai_play(hand, &g->field);
  play.played->visible = true;

  if (play.target) {
    remove_from_hand(hand, play.played);
    add_to_hand(scored, play.played);
    remove_from_hand(&g->field, play.target);
    add_to_hand(scored, play.target);
  } else {
    remove_from_hand(hand, play.played);
    add_to_hand(&g->field, play.played);
  }
}

void run_frame_initializing(Game *g) {
  // TODO: choose the dealer in a more effective manner
  g->turn_number = -1;

  g->player.hand.count = 0;
  g->right.hand.count = 0;
  g->left.hand.count = 0;
  g->field.count = 0;
  g->player.scored.count = 0;
  g->right.scored.count = 0;
  g->left.scored.count = 0;
  g->player.dekiyaku_score = 0;
  g->left.dekiyaku_score = 0;
  g->right.dekiyaku_score = 0;

  g->player.dekiyaku_action = DEKIYAKU_ACTION_NONE;
  g->right.dekiyaku_action = DEKIYAKU_ACTION_NONE;
  g->left.dekiyaku_action = DEKIYAKU_ACTION_NONE;

  g->current_play_from_hand = NULL;
  g->current_play_target = NULL;

  for (int i = 0; i < 48; i++) {
    Card *c = &g->cards[i];
    c->visible = false;
    add_to_hand(&g->deck, c);
  }

  shuffle_hand(&g->deck);

  kan_points_string(g, g->player.points, g->player.points_string);
  kan_points_string(g, g->right.points, g->right.points_string);
  kan_points_string(g, g->left.points, g->left.points_string);
  g->state = GAME_STATE_DEALING;
}

void run_frame_dealing(Game *g) {
  if (g->player.hand.count < 4) {
    deal(&g->deck, &g->player.hand, 4, true);
  } else if (g->left.hand.count < 4) {
    deal(&g->deck, &g->left.hand, 4, false);
  } else if (g->right.hand.count < 4) {
    deal(&g->deck, &g->right.hand, 4, false);
  } else if (g->field.count < 3) {
    deal(&g->deck, &g->field, 3, true);
  } else if (g->player.hand.count < 7) {
    deal(&g->deck, &g->player.hand, 3, true);
  } else if (g->left.hand.count < 7) {
    deal(&g->deck, &g->left.hand, 3, false);
  } else if (g->right.hand.count < 7) {
    deal(&g->deck, &g->right.hand, 3, false);
  } else if (g->field.count < 6) {
    deal(&g->deck, &g->field, 3, true);
  } else {
    g->state = GAME_STATE_CALCULATING_FIELD_MULTIPLIER;
  }
}

void run_frame_calculating_field_multiplier(Game *g) {
  g->field_multiplier = calculate_field_multiplier(&g->field);
  g->state = GAME_STATE_CALCULATING_TEYAKU;
}

void run_frame_calculating_teyaku(Game *g) {
  calculate_teyaku(g->player.hand, &g->player.teyaku);
  calculate_teyaku(g->right.hand, &g->right.teyaku);
  calculate_teyaku(g->left.hand, &g->left.teyaku);
  g->state = GAME_STATE_START_OF_TURN;
}

void run_frame_start_of_turn(Game *g) {
  g->turn_number++;
  if(g->player.hand.count == 0 && g->right.hand.count == 0 && g->left.hand.count == 0) {
    g->state = GAME_STATE_CALCULATING_SCORES;
  } else {
    g->state = GAME_STATE_CHECKING_FOR_CANCEL;
  }
}

void run_frame_checking_for_cancel(Game *g) {
  if (current_player(g)->dekiyaku_action == DEKIYAKU_ACTION_SAGE) {
    if (is_player_turn(g)) {
      g->dialog = cancel_dialog();
    } else {
      // TODO: the AI might want to cancel at some point
      g->state = GAME_STATE_CHOOSING_FROM_HAND;
    }
  } else {
    g->state = GAME_STATE_CHOOSING_FROM_HAND;
  }
}

void run_frame_player_choosing_from_hand(Game *g) {
  for (int i = 0; i < g->player.hand.count; i++) {
    if (g->player.hand.cards[i]->selected) {
      g->state = GAME_STATE_CHOOSING_TARGET;
      return;
    }
  }
}

void run_frame_choosing_from_hand(Game *g) {
  if (is_player_turn(g)) {
    run_frame_player_choosing_from_hand(g);
  } else {
    run_frame_ai_playing(g);
    g->state = GAME_STATE_PLAYING_FROM_DECK;
  }
}

void run_frame_choosing_target(Game *g) {
  if (!is_player_turn(g)) {
    printf("An AI player is choosing a target. That ain't right\n");
    return;
  }

  bool no_cards_selected = true;
  for (int i = 0; i < g->player.hand.count; i++) {
    if (g->player.hand.cards[i]->selected) {
      no_cards_selected = false;
      break;
    }
  }

  if (no_cards_selected) {
    g->state = GAME_STATE_CHOOSING_FROM_HAND;
    return;
  }

  if (g->current_play_from_hand) {
    if (g->current_play_target) {
      g->current_play_from_hand->selected = false;
      remove_from_hand(&g->player.hand, g->current_play_from_hand);
      add_to_hand(&g->player.scored, g->current_play_from_hand);
      remove_from_hand(&g->field, g->current_play_target);
      add_to_hand(&g->player.scored, g->current_play_target);
      g->current_play_from_hand = NULL;
      g->current_play_target = NULL;
    } else {
      g->current_play_from_hand->selected = false;
      remove_from_hand(&g->player.hand, g->current_play_from_hand);
      add_to_hand(&g->field, g->current_play_from_hand);
      g->current_play_from_hand = NULL;
    }
    g->state = GAME_STATE_PLAYING_FROM_DECK;
  }
}

void run_frame_playing_from_deck(Game *g) {
  Hand *to_hand = &current_player(g)->scored;

  Card *top_card = g->deck.cards[g->deck.count - 1];
  if (top_card->visible) {
    Card *target = valid_target(top_card, &g->field);
    if (target) {
      remove_from_hand(&g->field, target);
      add_to_hand(to_hand, target);
      remove_from_hand(&g->deck, top_card);
      add_to_hand(to_hand, top_card);
    } else {
      remove_from_hand(&g->deck, top_card);
      add_to_hand(&g->field, top_card);
    }
    g->state = GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU;
  } else {
    remove_from_hand(&g->deck, top_card);
    add_to_hand(&g->deck, top_card);
    top_card->visible = true;
    top_card->move.end_time = 0.3;
    top_card->move.destination.x = top_card->move.destination.x + 100;
  }
}

void run_frame_checking_for_new_dekiyaku(Game *g) {
  Player *cp = current_player(g);
  calculate_dekiyaku(&cp->scored, &cp->dekiyaku);
  int new_score = dekiyaku_score(&cp->dekiyaku);
  if (new_score != cp->dekiyaku_score) {
    cp->dekiyaku_score = new_score;
    if (is_player_turn(g)) {
      g->dialog = shoubu_dialog();
    } else {
      // TODO: better AI
      cp->dekiyaku_action = DEKIYAKU_ACTION_SHOUBU;
      g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE;
    }
  } else {
    g->state = GAME_STATE_START_OF_TURN;
  }
}

void run_frame_selecting_dekiyaku_action(Game *g) {
  if (is_player_turn(g)) {
    if (g->player.dekiyaku_action != DEKIYAKU_ACTION_NONE) {
      g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE;
    } else {
      g->dialog = shoubu_dialog();
    }
  } else {
    // TODO: better AI
    current_player(g)->dekiyaku_action = DEKIYAKU_ACTION_SHOUBU;
    g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE;
  }
}

void run_frame_calculating_scores(Game *g) {
  printf("Hand scores: %d %d %d\n", hand_points(&g->player.scored), hand_points(&g->right.scored), hand_points(&g->left.scored));

  SpecialCase special_case = calculate_special_case(g);
  switch(special_case.type) {
  case SPECIAL_CASE_ALL_EIGHTS:
    printf("All eights! Dealer gets %d kan\n", special_case.score);
    break;
  case SPECIAL_CASE_DOUBLE_EIGHTS:
  case SPECIAL_CASE_SIXTEEN_CHAFF:
    printf("Double eights or 16 chaff! Player %d gets %d kan\n", special_case.target, special_case.score);
    switch (special_case.target) {
    case SPECIAL_CASE_TARGET_PLAYER:
      transfer_kan(g, &g->player.points, &g->right.points, special_case.score);
      transfer_kan(g, &g->player.points, &g->left.points, special_case.score);
      break;
    case SPECIAL_CASE_TARGET_RIGHT:
      transfer_kan(g, &g->right.points, &g->player.points, special_case.score);
      transfer_kan(g, &g->right.points, &g->left.points, special_case.score);
      break;
    case SPECIAL_CASE_TARGET_LEFT:
      transfer_kan(g, &g->left.points, &g->right.points, special_case.score);
      transfer_kan(g, &g->left.points, &g->player.points, special_case.score);
      break;
    }
    break;
  default:
    transfer_points(g, &g->player.points, &g->temp_points, hand_points(&g->player.scored));
    transfer_points(g, &g->right.points, &g->temp_points, hand_points(&g->right.scored));
    transfer_points(g, &g->left.points, &g->temp_points, hand_points(&g->left.scored));
    break;
  }
  g->state = GAME_STATE_INITIALIZING;
}

void run_frame_calculating_dekiyaku_score(Game *g) {
  printf("Somebody won with dekiyaku. Cool.\n"); fflush(stdout);
  g->state = GAME_STATE_INITIALIZING;
}

void move_cards(Game *g) {
  float delta = GetFrameTime();
  for (int i = 0; i < 48; i++) {
    move_position(&g->cards[i].move, delta);
  }
}

bool done_moving(Game *g) {
  for (int i = 0; i < 48; i++) {
    if (!card_done_moving(&g->cards[i])) return false;
  }

  return true;
}

void run_frame(Game *g) {
  handle_input(g);
  if (g->dialog) return;

  move_cards(g);
  if (!done_moving(g)) return;

  switch (g->state) {
  case GAME_STATE_INITIALIZING:
    run_frame_initializing(g);
    return;
  case GAME_STATE_DEALING:
    run_frame_dealing(g);
    break;
  case GAME_STATE_CALCULATING_FIELD_MULTIPLIER:
    run_frame_calculating_field_multiplier(g);
    break;
  case GAME_STATE_CALCULATING_TEYAKU:
    run_frame_calculating_teyaku(g);
    break;
  case GAME_STATE_START_OF_TURN:
    run_frame_start_of_turn(g);
    break;
  case GAME_STATE_CHECKING_FOR_CANCEL:
    run_frame_checking_for_cancel(g);
    break;
  case GAME_STATE_CHOOSING_FROM_HAND:
    run_frame_choosing_from_hand(g);
    break;
  case GAME_STATE_CHOOSING_TARGET:
    run_frame_choosing_target(g);
    break;
  case GAME_STATE_PLAYING_FROM_DECK:
    run_frame_playing_from_deck(g);
    break;
  case GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU:
    run_frame_checking_for_new_dekiyaku(g);
    break;
  case GAME_STATE_SELECTING_DEKIYAKU_ACTION:
    run_frame_selecting_dekiyaku_action(g);
    break;
  case GAME_STATE_CALCULATING_SCORES:
    run_frame_calculating_scores(g);
    break;
  case GAME_STATE_CALCULATING_DEKIYAKU_SCORE:
    run_frame_calculating_dekiyaku_score(g);
    break;
  }
}

void draw_frame(Game *g) {
  BeginDrawing();
  ClearBackground(RAYWHITE);
  for (int i = 0; i < 48; i++) {
    draw_card(&g->cards[i], &g->cards_texture);
  }

  if (g->field_multiplier) DrawText(g->field_multiplier->name, 60, 385, 40, BLACK);

  if (g->player.teyaku.calculated) {
    char s[200];
    teyaku_to_string(&g->player.teyaku, s);
    DrawText(s, 5, 25, 30, BLACK);
  }

  DrawText(g->player.points_string, 40, 700, 20, BLACK);
  DrawText(g->right.points_string, 40, 750, 20, BLACK);
  DrawText(g->left.points_string, 40, 800, 20, BLACK);

  if (is_player_turn(g)) {
    switch (g->state) {
    case GAME_STATE_CHOOSING_FROM_HAND:
      DrawText("Choose a card to play", 60, 485, 20, BLACK);
      break;
    case GAME_STATE_CHOOSING_TARGET:
      DrawText("Choose a target on the field", 60, 485, 20, BLACK);
      DrawRectangleRec(next_card_position(&g->field), BLUE);
      break;
    default:
      break;
    }
  }

  if (g->dialog) {
    dialog_draw(g->dialog);
  }

  EndDrawing();
}

void run_until_closing(Game *g) {
  while (!WindowShouldClose() && !g->should_close) {
    run_frame(g);
    draw_frame(g);
  }
}

void transfer_points(Game *g, int *to, int *from, int amount) {
  amount *= g->field_multiplier->value;
  *to += amount;
  *from -= amount;
}

void transfer_kan(Game *g, int *to, int *from, int amount) {
  amount *= g->kan_value;
  transfer_points(g, to, from, amount);
}
