// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <random>

#include "open_spiel/algorithms/kriegspiel/darkboard.h"
#include "open_spiel/algorithms/kriegspiel/stats_collector.h"
#include "open_spiel/games/kriegspiel.h"
#include "open_spiel/abseil-cpp/absl/random/distributions.h"
#include "open_spiel/algorithms/evaluate_bots.h"

namespace open_spiel {
namespace algorithms{
namespace {

double Evaluate(const ProbabilityBoard& prob_board) {
  int opp_pieces = 15 - prob_board.captured_pieces_ - prob_board.captured_pawns_;
  int my_pieces = 0;
  for (int x = 0; x < 8; ++x) {
    for (int y = 0; y < 8; ++y) {
      chess::Square sq = {x, y};

      if (prob_board.board_.at(sq).color == prob_board.color_) {
        my_pieces++;
      }
    }
  }
  return my_pieces - opp_pieces;
}

void TestProbBoard() {

  int seed = time(nullptr);
  std::mt19937 rng(seed);

  std::function<double(const ProbabilityBoard&, std::optional<SimpleKriegspielUmpireMessage>, chess::Square)>
      eval_func = [&rng](const ProbabilityBoard& prob_board, std::optional<SimpleKriegspielUmpireMessage> last_msg, chess::Square sq) {

    ProbabilityBoard working_prob_board = prob_board;

    double value = 0.0;
    if (working_prob_board.board_.ToPlay() == working_prob_board.color_) {
      std::vector<chess::Move> legal_moves = working_prob_board.LegalMoves();

      chess::Move
          move = legal_moves[absl::Uniform(rng, 0u, legal_moves.size())];
      std::vector<std::pair<double, SimpleKriegspielUmpireMessage>>
          msgs = working_prob_board.MyProbabilisticMessage(move);

      for (const auto &[p, msg] : msgs) {

        if (msg.illegal) {
          value += p * Evaluate(working_prob_board);
          continue;
        }

        ProbabilityBoard loc_prob_board = working_prob_board;
        loc_prob_board.Update(move, msg);

        std::vector<std::pair<double, SimpleKriegspielUmpireMessage>>
            opp_msgs = loc_prob_board.OppProbabilisticMessage(msg, move.to);

        for (const auto &[opp_p, opp_msg] : opp_msgs) {

          ProbabilityBoard loc_prob_board2 = loc_prob_board;
          loc_prob_board2.Update(opp_msg);

          value += p * opp_p * Evaluate(loc_prob_board2);

        }

      }

    } else {
      SPIEL_CHECK_TRUE(last_msg);
      std::vector<std::pair<double, SimpleKriegspielUmpireMessage>>
          opp_msgs = working_prob_board.OppProbabilisticMessage(*last_msg, sq);

      for (const auto &[opp_p, opp_msg] : opp_msgs) {

        ProbabilityBoard loc_prob_board = working_prob_board;
        loc_prob_board.Update(opp_msg);

        std::string fen = loc_prob_board.board_.ToFEN();
        std::vector<chess::Move> legal_moves = loc_prob_board.LegalMoves();

        chess::Move
            move = legal_moves[absl::Uniform(rng, 0u, legal_moves.size())];
        std::vector<std::pair<double, SimpleKriegspielUmpireMessage>>
            msgs = loc_prob_board.MyProbabilisticMessage(move);

        for (const auto &[p, msg] : msgs) {

          if (msg.illegal) {
            value += p * Evaluate(working_prob_board);
            continue;
          }

          ProbabilityBoard loc_prob_board2 = loc_prob_board;
          loc_prob_board2.Update(move, msg);

          value += opp_p * p * Evaluate(working_prob_board);
        }
      }
    }
    if (!(value > -10000000)) {
      int a = 5;
    }
    return value;
  };

  Stats stats = ReadStatistics();

  DarkboardBot bot = DarkboardBot(time(nullptr),
                                  200,
                                  eval_func,
                                  1.0,
                                  ISMCTSFinalPolicyType::kMaxValue,
                                  chess::Color::kBlack,
                                  &stats);

  auto random_bot = MakeUniformRandomBot(1, seed);

  for (int i = 0; i < 25; ++i) {

    auto game = open_spiel::LoadGame("kriegspiel");
    std::vector<Bot *> bot_ptrs;

    bot_ptrs.push_back(&bot);
    bot_ptrs.push_back(random_bot.get());

    std::unique_ptr<open_spiel::State> state = game->NewInitialState();
    auto returns = EvaluateBots(state.get(), bot_ptrs, seed);

//    pgn::KriegspielPGN pgn = pgn::KriegspielPGN(
//        down_cast<kriegspiel::KriegspielState>(*state),
//        "Vojtech Foret Master Thesis",
//        i,
//        "Uniform Random",
//        "ISMCTS(c=" + std::to_string(c) + ")",
//        chess::Color::kEmpty);
//
//    std::cout << pgn << std::endl;
  }
















//  ProbabilityBoard prob_board = ProbabilityBoard(chess::Color::kWhite);
//  std::cout << prob_board.king_probs_ << std::endl;
//  std::cout << prob_board.piece_probs_ << std::endl;
//  std::cout << prob_board.pawn_probs_ << std::endl;
//
//  std::shared_ptr<const Game> kriegspiel_game = LoadGame("kriegspiel");
//  chess::ChessBoard board = chess::MakeDefaultBoard();
//  chess::Move move = chess::Move({0, 1}, {0, 3}, chess::Piece{chess::Color::kWhite, chess::PieceType::kPawn});
//  prob_board.Update(move, GetSimpleKriegspielUmpireMessage(kriegspiel::GetUmpireMessage(board, move)));
//
//  board.ApplyMove(move);
//
//
//  chess::Move move2 = chess::Move({0, 6}, {0, 5}, chess::Piece{chess::Color::kBlack, chess::PieceType::kPawn});
//  prob_board.Update(GetSimpleKriegspielUmpireMessage(kriegspiel::GetUmpireMessage(board, move2)));
//
//
//  std::cout << prob_board.king_probs_ << std::endl;
//  std::cout << prob_board.piece_probs_ << std::endl;
//  std::cout << prob_board.pawn_probs_ << std::endl;


  int a = 5;

}



}  // namespace
}  // namespace algorithms
}  // namespace open_spiel

int main(int argc, char** argv) {

  open_spiel::algorithms::TestProbBoard();
}
