// Copyright (c) 2023 Dominic Masters
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#include "TicTacToeGame.hpp"
#include "game/DawnGame.hpp"
#include "scene/components/example/ExampleSpin.hpp"
#include "scene/components/physics/3d/CubeCollider.hpp"
#include "state/StateProvider.hpp"

using namespace Dawn;

TicTacToeGame::TicTacToeGame(SceneItem *item) :
  SceneItemComponent(item),
  winner(TIC_TAC_TOE_EMPTY),
  nextMove(TIC_TAC_TOE_NOUGHT),
  gameOver(false),
  scoreCross(0),
  scoreNought(0)
{
}

void TicTacToeGame::onStart() {
  // Map tiles by tile number = tile
  auto ts = getScene()->findComponents<TicTacToeTile>();
  auto itTiles = ts.begin();
  while(itTiles != ts.end()) {
    this->tiles[(*itTiles)->tile] = *itTiles;
    ++itTiles;
  }

  useInterval([&]{
    std::cout << "Interval" << std::endl;
  }, 1.0f, this);

  useEffect([&]{
    if(!gameOver) return;
    
    auto board = this->getBoard();
    std::vector<uint8_t> winningCombo;
    winner = ticTacToeDetermineWinner(board, &winningCombo);

    switch(winner) {
      case TIC_TAC_TOE_CROSS:
        scoreCross++;
        break;

      case TIC_TAC_TOE_NOUGHT:
        scoreNought++;
        break;

      default:
        break;
    }
  }, gameOver);

  useEvent([&](float_t delta) {
    // Only allow player input if it's their turn.
    if(gameOver) return;

    Camera *camera = getScene()->findComponent<Camera>();
    if(camera == nullptr) return;

    TicTacToeTile *hovered = nullptr;
    bool_t isPlayerMove = nextMove == TIC_TAC_TOE_NOUGHT;

    // Get hovered tile (for player move only)
    if(isPlayerMove) {
      // Get mouse in screen space.
      auto mouse = getGame()->inputManager.getAxis2D(INPUT_BIND_MOUSE_X, INPUT_BIND_MOUSE_Y);
      mouse *= 2.0f;
      mouse -= glm::vec2(1, 1);

      struct Ray3D ray;
      ray.origin = camera->transform->getWorldPosition();
      ray.direction = camera->getRayDirectionFromScreenSpace(mouse);

      // Find the hovered tile (if any)
      auto results = getPhysics()->raycast3DAll(ray);
      auto itResult = results.begin();
      while(itResult != results.end()) {
        auto result = *itResult;
        auto tile = result.collider->item->getComponent<TicTacToeTile>();
        if(tile == nullptr) {
          ++itResult;
          continue;
        }
        
        hovered = tile;
        break;
      }
    }

    // Now update the hover state(s)
    auto itTiles = tiles.begin();
    while(itTiles != tiles.end()) {
      auto t = itTiles->second;
      if(t == hovered) {
        t->hovered = true;
      } else {
        t->hovered = false;
      }
      ++itTiles;
    }

    if(isPlayerMove) {
      if(getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK) && hovered != nullptr) {
        this->makeMove(hovered->tile, nextMove);
      }
    } else if(nextMove != TIC_TAC_TOE_NOUGHT) {
      auto board = this->getBoard();
      auto move = ticTacToeGetAiMove(board, nextMove);
      this->makeMove(move, nextMove);
    }

    // Did game just end?
    gameOver = ticTacToeIsGameOver(this->getBoard());
  }, getScene()->eventSceneUpdate);
}

std::map<uint8_t, enum TicTacToeTileState> TicTacToeGame::getBoard() {
  std::map<uint8_t, enum TicTacToeTileState> tileMap;

  auto itTiles = tiles.begin();
  while(itTiles != tiles.end()) {
    auto t = itTiles->second;
    tileMap[t->tile] = t->tileState;
    ++itTiles;
  }

  return tileMap;
}

void TicTacToeGame::makeMove(uint8_t tile, enum TicTacToeTileState player) {
  this->tiles[tile]->tileState = player;
  nextMove = player == TIC_TAC_TOE_NOUGHT ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT;
}