// 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 <fstream>

#include "open_spiel/bots/stockfish/stockfish_bot.h"
#include "open_spiel/games/kriegspiel.h"
#include "open_spiel/algorithms/kriegspiel/ismcts.h"
#include "open_spiel/algorithms/kriegspiel/mo_ismcts.h"

#include "open_spiel/abseil-cpp/absl/random/distributions.h"
#include "open_spiel/spiel.h"
#include "open_spiel/spiel_bots.h"
#include "open_spiel/spiel_utils.h"
#include "open_spiel/algorithms/evaluate_bots.h"
#include "open_spiel/algorithms/kriegspiel/random_bot.h"
#include "open_spiel/games/chess/pgn.h"

namespace open_spiel {
namespace {

void RunWhiteGames(double c, int board_eval_time, int number_of_games) {
  int seed = time(nullptr);
  std::mt19937 rng(seed);

  std::unique_ptr<stockfish::StockfishBot> sf_bot = stockfish::MakeStockfishBot(10);

  auto sf_evaluator = [&sf_bot, &board_eval_time] (const chess::ChessBoard& board, chess::Color color) {
    uci::ResultMove result_move = sf_bot->Step(board, board_eval_time);
    absl::optional<chess::Move> move = board.ParseLANMove(result_move.move);
    if (!move || !board.IsMoveLegal(*move)) {
      uci::ResultMove r = sf_bot->ReadBestMove();
      result_move = sf_bot->Step(board, board_eval_time);
      move = board.ParseLANMove(result_move.move);
    }
    double win_prob;
    if (result_move.score_type == uci::ScoreType::kMate) {
      win_prob = result_move.score > 0 ? 1.0 : 0.0;
    }
    else {
      int multiplier = board.ToPlay() == color ? -1 : 1;
      win_prob = 0.9837 / (1.0 + std::pow(1.03457, ((double) multiplier * result_move.score) * 0.0078));
    }
    return win_prob;
  };

  algorithms::HSInfoset is = algorithms::HSInfoset(seed, chess::Color::kWhite, 1000);

  auto ismcts_bot = algorithms::MyISMCTSBot(
      time(nullptr),
      2000,
      &is,
      sf_evaluator,
      c,
      algorithms::ISMCTSFinalPolicyType::kMaxValue);
  auto random_bot = MakeUniformRandomBot(0, seed);

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

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

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

    std::unique_ptr<open_spiel::State> state = game->NewInitialState();
    auto returns = EvaluateBots(state.get(), bot_ptrs, seed);
//    std::ofstream ofstream("/hdd/neco.pgn", std::ios::app);

    pgn::KriegspielPGN pgn = pgn::KriegspielPGN(
        down_cast<kriegspiel::KriegspielState>(*state),
        "Vojtech Foret Master Thesis",
        i,
        "ISMCTS with stockfish (c=" + std::to_string(c) + ",time" + std::to_string(board_eval_time) + ")",
        "Uniform Random",
        chess::Color::kEmpty);

    std::cout << pgn << std::endl;
  }

}

void RunBlackGames(double c, int board_eval_time, int number_of_games) {
  int seed = time(nullptr);


  std::unique_ptr<stockfish::StockfishBot> sf_bot = stockfish::MakeStockfishBot(10);

  auto sf_evaluator = [&sf_bot, &board_eval_time] (const chess::ChessBoard& board, chess::Color color) {
    uci::ResultMove result_move = sf_bot->Step(board, board_eval_time);
    absl::optional<chess::Move> move = board.ParseLANMove(result_move.move);
    if (!move || !board.IsMoveLegal(*move)) {
      uci::ResultMove r = sf_bot->ReadBestMove();
      result_move = sf_bot->Step(board, board_eval_time);
      move = board.ParseLANMove(result_move.move);
    }
    double win_prob;
    if (result_move.score_type == uci::ScoreType::kMate) {
      win_prob = result_move.score > 0 ? 1.0 : 0.0;
    }
    else {
      int multiplier = board.ToPlay() == color ? -1 : 1;
      win_prob = 0.9837 / (1.0 + std::pow(1.03457, ((double) multiplier * result_move.score) * 0.0078));
    }
    return win_prob;
  };

  algorithms::HSInfoset is = algorithms::HSInfoset(seed, chess::Color::kBlack, 1000);

  auto ismcts_bot = algorithms::MyISMCTSBot(
      time(nullptr),
      2000,
      &is,
      sf_evaluator,
      c,
      algorithms::ISMCTSFinalPolicyType::kMaxValue);
  auto random_bot = MakeUniformRandomBot(1, seed);

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

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

    bot_ptrs.push_back(&ismcts_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);

//    std::ofstream ofstream("/hdd/neco.pgn", std::ios::app);

    pgn::KriegspielPGN pgn = pgn::KriegspielPGN(
        down_cast<kriegspiel::KriegspielState>(*state),
        "Vojtech Foret Master Thesis",
        i,
        "Uniform Random",
        "ISMCTS with stockfish (c=" + std::to_string(c) + ",time" + std::to_string(board_eval_time) + ")",
        chess::Color::kEmpty);

    std::cout << pgn << std::endl;
  }

}

void PlayGames(double c, int board_eval_time) {
  for (int i = 0; i < 25; ++i) {
    RunWhiteGames(c, board_eval_time, 1);
    RunBlackGames(c, board_eval_time, 1);
  }
}

}  // namespace
}  // namespace open_spiel

int main(int argc, char** argv) {
  double c = std::stod(argv[1]);
  int board_eval_time = std::stod(argv[2]);
  open_spiel::PlayGames(c, board_eval_time);
}
