//    Copyright 2025 Bill Rossi
//    
//    This file is part of Hanafuda Hachi-Hachi.
//
//    Hanafuda Hachi-Hachi is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//
//    Hanafuda Hachi-Hachi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License along with Hanafuda Hachi-Hachi. If not, see <https://www.gnu.org/licenses/>. 
#include <string.h>
#include <stdlib.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_red = LoadImage("img/cards_red.png");
  g->cards_texture_red = LoadTextureFromImage(cards_image_red);
  UnloadImage(cards_image_red);

  Image cards_image_black = LoadImage("img/cards_black.png");
  g->cards_texture_black = LoadTextureFromImage(cards_image_black);
  UnloadImage(cards_image_black);

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

  g->kan_value = 12;
  g->number_of_rounds = 3;
  g->black_card_backs = true;
  g->deal_speed = 0.4;
  g->options = malloc(sizeof(Options));
  initialize_title(g);
  load_options_from_game(g);

  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, { -500, -300 }, false };
    g->cards[i].move.end_time = 0.;
    g->cards[i].move.position = &g->cards[i].position;
    g->cards[i].move.destination = (Vector2) { -500, -300 };
    g->cards[i].order = i;
    g->cards[i].selected = false;
    g->cards[i].visible = false;
    g->deck.cards[g->deck.count++] = &g->cards[i];
  }

  g->player.points = 100 * g->kan_value;
  g->right.points = 100 * g->kan_value;
  g->left.points = 100 * g->kan_value;
  g->player.last_round_points = 100 * g->kan_value;
  g->right.last_round_points = 100 * g->kan_value;
  g->left.last_round_points = 100 * g->kan_value;
  g->player.points_string[0] = '\0';
  g->right.points_string[0] = '\0';
  g->left.points_string[0] = '\0';
  g->player.teyaku_string[0] = '\0';
  g->right.teyaku_string[0] = '\0';
  g->left.teyaku_string[0] = '\0';
  g->player.name = "Player";
  g->right.name = "Right";
  g->left.name = "Left";

  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.teyaku.calculated = false;
  g->right.teyaku.calculated = false;
  g->left.teyaku.calculated = false;

  g->player.seat = PLAYER;
  g->right.seat = RIGHT;
  g->left.seat = LEFT;

  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->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;

  g->field.position = (Vector2) { 600, 300 };
  g->field.display_type = HAND_DISPLAY_FIELD;

  strcpy(teyaku_calculation, "");

  int dealer = rand() % 3;
  switch (dealer) {
  case PLAYER:
    g->dealer = &g->player;
    break;
  case RIGHT:
    g->dealer = &g->right;
    break;
  case LEFT:
    g->dealer = &g->left;
    break;
  }

  g->current_round = 0;
  g->state = GAME_STATE_TITLE_SCREEN;
}

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 (g->state == GAME_STATE_OPTIONS) {
    return options_handle_input(g);
  }

  if (g->state == GAME_STATE_TITLE_SCREEN) {
    return title_handle_input(g);
  }

  if (g->dialog) {
    return dialog_handle_input(g->dialog);
  }

  /*
  if (IsKeyPressed(KEY_R)) {
    g->state = GAME_STATE_INITIALIZING;
    return;
  }

  if (IsKeyPressed(KEY_S)) {
    ExportImage(LoadImageFromScreen(), "img.png");
  }
  */

  if (!is_player_turn(g)) return;

  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;
  case GAME_STATE_CHOOSING_TARGET_FROM_DECK:
    if (IsMouseButtonPressed(0)) {
      mouse_pos = GetMousePosition();
      for (int i = 0; i < g->field.count; i++) {
	if (point_within_card(g->field.cards[i], mouse_pos)) {
	  Card *selected_card = g->deck.cards[g->deck.count - 1];
	  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;
	}
      }
    }
    break;
  default:
    break;
  }
}

void capture_card_from_field(Game *g, Card *played, Card *target, Hand *hand, Hand *scored) {
  // capture all three cards if they play the fourth
  Card *same_month_card[3];
  int same_month_card_count = 0;
  for (int i = 0; i < g->field.count; i++) {
    Card *c = g->field.cards[i];
    if (c->month == played->month) {
      same_month_card[same_month_card_count++] = c;
    }
  }

  remove_from_hand(hand, played);
  add_to_hand(scored, played, g->deal_speed);

  if (same_month_card_count == 3) {
    for (int i = 0; i < 3; i++) {
      remove_from_hand(&g->field, same_month_card[i]);
      add_to_hand(scored, same_month_card[i], g->deal_speed);
    }
  } else {
    remove_from_hand(&g->field, target);
    add_to_hand(scored, target, g->deal_speed);
  }
}

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) {
    capture_card_from_field(g, play.played, play.target, hand, scored);
  } else {
    remove_from_hand(hand, play.played);
    add_to_hand(&g->field, play.played, g->deal_speed);
  }
}

void run_frame_initializing(Game *g) {
  g->turn_number = g->dealer->seat;

  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;

  strcpy(g->player.teyaku_string, "");
  strcpy(g->right.teyaku_string, "");
  strcpy(g->left.teyaku_string, "");

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

  g->first_sage = NULL;

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

  shuffle_hand(&g->deck);
  // order_deck(&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;
}

bool misdeal(Game *g) {
  int month_count[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };
  for (int i = 0; i < g->field.count; i++) {
    Card *c = g->field.cards[i];
    month_count[c->month]++;
  }

  for (int i = 0; i < 12; i++)
    if (month_count[i] >= 4) return true;

  return false;
}

void run_frame_dealing(Game *g) {
  if (current_player(g)->hand.count < 4) {
    deal(&g->deck, &current_player(g)->hand, 4, is_player_turn(g), g->deal_speed);
    g->turn_number++;
  } else if (g->field.count < 3) {
    deal(&g->deck, &g->field, 3, true, g->deal_speed);
  } else if (current_player(g)->hand.count < 7) {
    deal(&g->deck, &current_player(g)->hand, 3, is_player_turn(g), g->deal_speed);
    g->turn_number++;
  } else if (g->field.count < 6) {
    deal(&g->deck, &g->field, 3, true, g->deal_speed);
  } else {
    if (misdeal(g)) {
      printf("misdeal\n");
      g->state = GAME_STATE_INITIALIZING;
    } else {
      g->turn_number++;
      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);
  teyaku_to_string(&g->player.teyaku, g->player.teyaku_string);
  calculate_teyaku(g->right.hand, &g->right.teyaku);
  teyaku_to_string(&g->right.teyaku, g->right.teyaku_string);
  calculate_teyaku(g->left.hand, &g->left.teyaku);
  teyaku_to_string(&g->left.teyaku, g->left.teyaku_string);

  if (teyaku_points(&g->player.teyaku) > 0) {
    teyaku_dialog(g);
    set_teyaku_to_string(&g->player.teyaku, g->dialog->text[1]);
    chaff_teyaku_to_string(&g->player.teyaku, g->dialog->text[2]);
  } else {
    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) {
    if (
	g->player.dekiyaku_action == DEKIYAKU_ACTION_NONE &&
	g->left.dekiyaku_action == DEKIYAKU_ACTION_NONE &&
	g->right.dekiyaku_action == DEKIYAKU_ACTION_NONE) {
      g->state = GAME_STATE_CALCULATING_SCORES;
    } else {
      g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE;
    }
  } 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)) {
      cancel_dialog(g);
    } 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_SHOWING_CARD_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;
  }

  Card *selected_card = NULL;
  bool no_cards_selected = true;
  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;
    }
  }

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

  for (int i = 0; i < g->field.count; i++) {
    g->field.cards[i]->selected = g->field.cards[i]->month == selected_card->month;
  }

  if (g->current_play_from_hand) {
    if (g->current_play_target) {
      g->current_play_from_hand->selected = false;

      capture_card_from_field(
			      g,
			      g->current_play_from_hand,
			      g->current_play_target,
			      &g->player.hand,
			      &g->player.scored
			      );


      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->deal_speed);
      g->current_play_from_hand = NULL;
    }
    for (int i = 0; i < g->player.scored.count; i++) {
      g->player.scored.cards[i]->selected = false;
    }
    g->state = GAME_STATE_SHOWING_CARD_FROM_DECK;
  }
}

void run_frame_showing_card_from_deck(Game *g) {
  Card *top_card = g->deck.cards[g->deck.count - 1];
  remove_from_hand(&g->deck, top_card);
  add_to_hand(&g->deck, top_card, g->deal_speed);
  top_card->visible = true;
  top_card->move.end_time = 0.3;
  top_card->move.destination.y = top_card->move.destination.y + 150;
  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];
  Card *targets[4];
  int target_count = 0;
  valid_targets(top_card, &g->field, &targets[0], &target_count);

  if (target_count == 1) {
    capture_card_from_field(g, top_card, targets[0], &g->deck, to_hand);
    g->state = GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU;
  } else if(target_count == 0) {
    remove_from_hand(&g->deck, top_card);
    add_to_hand(&g->field, top_card, g->deal_speed);
    g->state = GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU;
  } else {
    g->state = GAME_STATE_CHOOSING_TARGET_FROM_DECK;
    return;
  }
}

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

  Card *top_card = g->deck.cards[g->deck.count - 1];
  Card *targets[4];
  int target_count = 0;
  valid_targets(top_card, &g->field, &targets[0], &target_count);

  if (is_player_turn(g)) {
    for (int i = 0; i < target_count; i++) {
      targets[i]->selected = true;
    }

    if (g->current_play_target) {
      for (int i = 0; i < target_count; i++) {
	targets[i]->selected = false;
      }
      capture_card_from_field(g, top_card, g->current_play_target, &g->deck, to_hand);
      g->state = GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU;
      g->current_play_from_hand = NULL;
      g->current_play_target = NULL;
    }
  } else {
    // TODO: better AI
    capture_card_from_field(g, top_card, targets[0], &g->deck, to_hand);
    g->state = GAME_STATE_CHECKING_FOR_NEW_DEKIYAKU;
  }
}

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)) {
      shoubu_dialog(g);
    } else {
      // TODO: better AI
      // TODO: if it can sage, set "first sage" if possible
      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 {
      shoubu_dialog(g);
    }
  } else {
    // TODO: better AI
    current_player(g)->dekiyaku_action = DEKIYAKU_ACTION_SHOUBU;
    g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE;
  }
}

void pay_teyaku_to_player(Game *g, Player *p) {
  int points = teyaku_points(&p->teyaku);
  transfer_kan(g, &p->points, &g->player.points, points);
  transfer_kan(g, &p->points, &g->right.points, points);
  transfer_kan(g, &p->points, &g->left.points, points);
}

void pay_teyaku(Game *g) {
  pay_teyaku_to_player(g, &g->player);
  pay_teyaku_to_player(g, &g->right);
  pay_teyaku_to_player(g, &g->left);
}

void run_frame_calculating_scores(Game *g) {
  no_dekiyaku_end_of_round_dialog(g);

  int hp[3];
  hp[0] = hand_points(&g->player.scored);
  hp[1] = hand_points(&g->right.scored);
  hp[2] = hand_points(&g->left.scored);

  SpecialCase special_case = calculate_special_case(g);
  switch(special_case.type) {
  case SPECIAL_CASE_ALL_EIGHTS:
    sprintf(g->dialog->text[0], "All eights!");
    sprintf(g->dialog->text[0], "Dealer gets %d kan", special_case.score);
    transfer_kan(g, &g->dealer->points, &g->player.points, special_case.score);
    transfer_kan(g, &g->dealer->points, &g->right.points, special_case.score);
    transfer_kan(g, &g->dealer->points, &g->left.points, special_case.score);
    break;
  case SPECIAL_CASE_DOUBLE_EIGHTS:
  case SPECIAL_CASE_SIXTEEN_CHAFF:
    if (special_case.type == SPECIAL_CASE_DOUBLE_EIGHTS) sprintf(g->dialog->text[0], "Double eights!");
    else sprintf(g->dialog->text[0], "Sixteen chaff!");
    sprintf(g->dialog->text[1], "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);
      g->dealer = &g->player;
      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);
      g->dealer = &g->right;
      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);
      g->dealer = &g->left;
      break;
    }
    break;
  default:
    sprintf(g->dialog->text[0], "Player score: %d", hp[0]);
    sprintf(g->dialog->text[1], "Right score: %d", hp[1]);
    sprintf(g->dialog->text[2], "Left score: %d", hp[2]);
    transfer_points(g, &g->player.points, &g->temp_points, hp[0]);
    transfer_points(g, &g->right.points, &g->temp_points, hp[1]);
    transfer_points(g, &g->left.points, &g->temp_points, hp[2]);
    if (hp[0] > hp[1]) {
      if (hp[0] > hp[2]) g->dealer = &g->player;
      else
	if (hp[2] > hp[1]) g->dealer = &g->left;
	else g->dealer = &g->right;
    } else {
      if (hp[1] > hp[2]) g->dealer = &g->right;
      else g->dealer = &g->left;
    }
    pay_teyaku(g);
    break;
  }
}

int calculate_dekiyaku_score(Game *g, Player *p) {
  Dekiyaku d;
  calculate_dekiyaku(&p->scored, &d);
  if (p->dekiyaku_action == DEKIYAKU_ACTION_SHOUBU) {
    sprintf(g->dialog->text[0], "%s wins with dekiyaku!", p->name);
    dekiyaku_to_string(&d, g->dialog->text[1]);
    sprintf(g->dialog->text[2], "%s wins %d kan", p->name, dekiyaku_score(&d));
    transfer_kan(g, &p->points, &g->player.points, dekiyaku_score(&d));
    transfer_kan(g, &p->points, &g->right.points, dekiyaku_score(&d));
    transfer_kan(g, &p->points, &g->left.points, dekiyaku_score(&d));
    return dekiyaku_score(&d) * g->kan_value;
  } else {
    sprintf(g->dialog->text[0], "%s cancels with dekiyaku!", p->name);
    dekiyaku_to_string(&d, g->dialog->text[1]);
    if (dekiyaku_score(&d) % 2) {
      sprintf(g->dialog->text[2], "%s wins %d.5 kan", p->name, dekiyaku_score(&d) / 2);
      transfer_points(g, &p->points, &g->player.points, (dekiyaku_score(&d) * g->kan_value) / 2);
      transfer_points(g, &p->points, &g->right.points, (dekiyaku_score(&d) * g->kan_value) / 2);
      transfer_points(g, &p->points, &g->left.points, (dekiyaku_score(&d) * g->kan_value) / 2);
    } else {
      sprintf(g->dialog->text[2], "%s wins %d kan", p->name, dekiyaku_score(&d) / 2);
      transfer_kan(g, &p->points, &g->player.points, dekiyaku_score(&d) / 2);
      transfer_kan(g, &p->points, &g->right.points, dekiyaku_score(&d) / 2);
      transfer_kan(g, &p->points, &g->left.points, dekiyaku_score(&d) / 2);
    }
    return (dekiyaku_score(&d) * g->kan_value) / 2;
  }
}

void calculate_exhausted_dekiyaku_score(Game *g) {
  g->player.dekiyaku_action = DEKIYAKU_ACTION_CANCEL;
  int p_score = calculate_dekiyaku_score(g, &g->player);
  g->right.dekiyaku_action = DEKIYAKU_ACTION_CANCEL;
  int r_score = calculate_dekiyaku_score(g, &g->right);
  g->left.dekiyaku_action = DEKIYAKU_ACTION_CANCEL;
  int l_score = calculate_dekiyaku_score(g, &g->left);
  printf("%d %d %d\n", p_score, r_score, l_score);
  sprintf(g->dialog->text[0], "Hands are exhausted (half dekiyaku scores)");
  sprintf(g->dialog->text[1], "Player scores %d points", p_score);
  sprintf(g->dialog->text[2], "Right scores %d points", r_score);
  sprintf(g->dialog->text[3], "Left scores %d points", l_score);
}

void run_frame_calculating_dekiyaku_score(Game *g) {
  dekiyaku_end_of_round_dialog(g);
  strcpy(g->dialog->text[3], "");
  if (g->player.dekiyaku_action == DEKIYAKU_ACTION_CANCEL || g->player.dekiyaku_action == DEKIYAKU_ACTION_SHOUBU) {
    calculate_dekiyaku_score(g, &g->player);
  } else if (g->right.dekiyaku_action == DEKIYAKU_ACTION_CANCEL || g->right.dekiyaku_action == DEKIYAKU_ACTION_SHOUBU) {
    calculate_dekiyaku_score(g, &g->right);
  } else if (g->left.dekiyaku_action == DEKIYAKU_ACTION_CANCEL || g->left.dekiyaku_action == DEKIYAKU_ACTION_SHOUBU) {
    calculate_dekiyaku_score(g, &g->left);
  } else {
    g->dealer = g->first_sage;
    calculate_exhausted_dekiyaku_score(g);
  }
  pay_teyaku(g);
}

void run_frame_end_of_round(Game *g) {
  end_of_round_dialog(g);
  int player_diff = g->player.points - g->player.last_round_points;
  if (player_diff > 0) {
    sprintf(g->dialog->text[0], "Player gains %d points, has %d", player_diff, g->player.points);
  } else if (player_diff < 0) {
    sprintf(g->dialog->text[0], "Player loses %d points, has %d", -player_diff, g->player.points);
  } else {
    sprintf(g->dialog->text[0], "Player gains no points, has %d", g->player.points);
  }
  int right_diff = g->right.points - g->right.last_round_points;
  if (right_diff > 0) {
    sprintf(g->dialog->text[1], "Right gains %d points, has %d", right_diff, g->right.points);
  } else if (right_diff < 0) {
    sprintf(g->dialog->text[1], "Right loses %d points, has %d", -right_diff, g->right.points);
  } else {
    sprintf(g->dialog->text[1], "Right gains no points, has %d", g->right.points);
  }
  int left_diff = g->left.points - g->left.last_round_points;
  if (left_diff > 0) {
    sprintf(g->dialog->text[2], "Left gains %d points, has %d", left_diff, g->left.points);
  } else if (left_diff < 0) {
    sprintf(g->dialog->text[2], "Left loses %d points, has %d", -left_diff, g->left.points);
  } else {
    sprintf(g->dialog->text[2], "Left gains no points, has %d", g->left.points);
  }

  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->current_round++;
  if (g->current_round >= g->number_of_rounds) {
    g->state = GAME_STATE_END_OF_GAME;
  } else {
    g->state = GAME_STATE_INITIALIZING;
  }
}

void *winning_player_string(Game *g, char *string) {
  int p = g->player.points;
  int r = g->right.points;
  int l = g->left.points;
  if (p == r && p == l) sprintf(string, "It's a three-way tie!");
  else if (p > r && p == l) sprintf(string, "%s and %s tie for the win!", g->player.name, g->left.name);
  else if (p > l && p == r) sprintf(string, "%s and %s tie for the win!", g->player.name, g->right.name);
  else if (p < l && l == r) sprintf(string, "%s and %s tie for the win!", g->right.name, g->left.name);
  else if (p > l && p > r) sprintf(string, "%s wins!", g->player.name);
  else if (l > r && l > p) sprintf(string, "%s wins!", g->left.name);
  else if (r > l && r > p) sprintf(string, "%s wins!", g->right.name);
  else sprintf(string, "I have no idea who wins (%d %d %d) wins!", p, r, l);
}

void run_frame_end_of_game(Game *g) {
  end_of_game_dialog(g);
  char line[200];
  kan_points_string(g, g->player.points, &line[0]);
  sprintf(g->dialog->text[0], "Player: %s", line);
  kan_points_string(g, g->right.points, &line[0]);
  sprintf(g->dialog->text[1], "Right: %s", line);
  kan_points_string(g, g->left.points, &line[0]);
  sprintf(g->dialog->text[2], "Left: %s", line);
  winning_player_string(g, g->dialog->text[3]);
}

void run_frame_new_game(Game *g) {
  g->field_multiplier = NULL;
  g->dialog = NULL;

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

  strcpy(teyaku_calculation, "");

  int dealer = rand() % 3;
  switch (dealer) {
  case PLAYER:
    g->dealer = &g->player;
    break;
  case RIGHT:
    g->dealer = &g->right;
    break;
  case LEFT:
    g->dealer = &g->left;
    break;
  }

  g->current_round = 0;
  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 (g->state == GAME_STATE_TITLE_SCREEN) run_frame_title(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_SHOWING_CARD_FROM_DECK:
    run_frame_showing_card_from_deck(g);
    break;
  case GAME_STATE_PLAYING_FROM_DECK:
    run_frame_playing_from_deck(g);
    break;
  case GAME_STATE_CHOOSING_TARGET_FROM_DECK:
    run_frame_choosing_target_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;
  case GAME_STATE_END_OF_ROUND:
    run_frame_end_of_round(g);
    break;
  case GAME_STATE_END_OF_GAME:
    run_frame_end_of_game(g);
    break;
  case GAME_STATE_NEW_GAME:
    run_frame_new_game(g);
    break;
  case GAME_STATE_TITLE_SCREEN:
    break;
  case GAME_STATE_OPTIONS:
    break;
  }
}

Texture *cards_texture_fun(Game *g) {
  return g->black_card_backs ? &g->cards_texture_black : &g->cards_texture_red;
}

void draw_player_cards(Game *g, Player *p) {
  for (int i = 0; i < p->hand.count; i++) {
    draw_card(p->hand.cards[i], cards_texture_fun(g), g->black_card_backs);
  }

  for (int i = 0; i < p->scored.count; i++) {
    draw_card(p->scored.cards[i], cards_texture_fun(g), g->black_card_backs);
  }
}

void draw_cards(Game *g) {
  draw_player_cards(g, &g->player);
  draw_player_cards(g, &g->right);
  draw_player_cards(g, &g->left);
  for (int i = 0; i < g->field.count; i++) {
    draw_card(g->field.cards[i], cards_texture_fun(g), g->black_card_backs);
    }

  for (int i = 0; i < g->deck.count; i++) {
    draw_card(g->deck.cards[i], cards_texture_fun(g), g->black_card_backs);
  }
}

void draw_frame(Game *g) {
  BeginDrawing();
  ClearBackground(RAYWHITE);

  if (g->state == GAME_STATE_TITLE_SCREEN) {
    title_draw(g);
    EndDrawing();
    return;
  }

  if (g->state == GAME_STATE_OPTIONS) {
    options_draw(g);
    EndDrawing();
    return;
  }

  draw_cards(g);

  if (g->state == GAME_STATE_DEALING) {
    DrawText("Dealing....", 60, 385, 40, BLACK);
  } else if (g->field_multiplier) {
    DrawText(g->field_multiplier->name, 60, 385, 30, BLACK);
    DrawText(g->field_multiplier->explanation, 60, 445, 20, BLACK);
  }

  DrawText(g->player.points_string, 1000, 650, 20, BLACK);
  DrawText(g->player.teyaku_string, 1000, 680, 20, BLACK);
  DrawText(g->right.points_string, 1000, 260, 20, BLACK);
  DrawText(g->right.teyaku_string, 1100, 260, 20, BLACK);
  DrawText(g->left.points_string, 200, 260, 20, BLACK);
  DrawText(g->left.teyaku_string, 300, 260, 20, BLACK);

  char round_text[40];
  if (g->current_round < g->number_of_rounds)
    sprintf(round_text, "Round %d / %d", g->current_round+1, g->number_of_rounds);
  else
    sprintf(round_text, "Game Over");

  DrawText(round_text, 20, 875, 20, BLACK);

  if (is_player_turn(g)) {
    switch (g->state) {
    case GAME_STATE_CHOOSING_FROM_HAND:
      DrawText("Choose a card to play from your hand", 60, 485, 20, BLACK);
      break;
    case GAME_STATE_CHOOSING_TARGET:
      DrawText("Choose a target on the field", 60, 485, 20, BLACK);

      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;
	}
      }
      if (!selected_card) break;

      bool draw_next_rec = true;
      for (int i = 0; i < g->field.count; i++) {
	if (g->field.cards[i]->month == selected_card->month) draw_next_rec = false;
      }
      if (draw_next_rec) 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);
}