#ifndef BOT_HANDLER_HPP_
#define BOT_HANDLER_HPP_

#include <filesystem>
#include <iostream>
#include <string>

#include "NetworkSettings.hpp"
#include "SharedBotDataStruct.hpp"

namespace evab{

class BotHandler{
private:
  std::string name;
  sf::TcpSocket sClient;
  bool movingForward;
  bool attackForward;
  float currentRotation;
  int player_id;
  EvabBotPackage gameDataPack;

  BotHandler();
  static BotHandler& getGlobalBotHandler();
  bool updateGameInfo();

  bool flagConnected;
public:
  static void setName(const std::string &);
  static void greetServer();
  static void stablishConnectionWithServer();
  static void onBotActionRotation(const float& rotInDegrees);
  static void onBotActionMoving(const bool& enableMoving);
  static void onBotActionAttack(const bool& enableAttack);
  static bool onBotGameplay();
  static int getMyId();
  static EvabBotPackage& getGameData();
};

//==========================================================
//==========================================================
//==========================================================

BotHandler::BotHandler() {
  movingForward = attackForward = false;
  currentRotation = 0.f;
  flagConnected = false;
}

BotHandler& BotHandler::getGlobalBotHandler() {
  static BotHandler INSTANCE_BOT_HANDLER;
  return INSTANCE_BOT_HANDLER;
}

void BotHandler::setName(const std::string &name) {
  getGlobalBotHandler().name = name;
}

bool BotHandler::updateGameInfo() {
  sf::Packet receivedData;
  movingForward = false;
  attackForward = false;
  if(getGlobalBotHandler().sClient.receive(receivedData) == sf::Socket::Done){
    getGlobalBotHandler().gameDataPack.players.clear();
    getGlobalBotHandler().gameDataPack.bullets.clear();
    receivedData >> getGlobalBotHandler().gameDataPack;
    if(gameDataPack.isAlive){
#ifdef DEBUG
      std::cout << "[EVAB::BotHandler <STATUS>]: The game\'s data has been updated.\n";
#endif
      for (ElementInfo &cur : getGlobalBotHandler().gameDataPack.players)
        if (getGlobalBotHandler().player_id == cur.player_id)
          getGlobalBotHandler().currentRotation = cur.direction;
      return true;
    }
    else {
      getGlobalBotHandler().sClient.disconnect();
#ifdef DEBUG
      std::cout << "[EVAB::BotHandler <SPAM>]: Your bot has been killed :(.\n";
#endif
      return false;
    }
  }
  else
    std::cerr << "[EVAB::BotHandler <ERROR>]: The connection with the server was unexpectedly lost.\n";
  return false;
}

void BotHandler::greetServer() {
  sf::Packet idPackage;
  if(getGlobalBotHandler().sClient.receive(idPackage) == sf::Socket::Done) {
    idPackage >> getGlobalBotHandler().player_id;
    sf::Packet namePackage;
    namePackage << getGlobalBotHandler().name;
    if(getGlobalBotHandler().sClient.send(namePackage) == sf::Socket::Done){
#ifdef DEBUG
      std::cout << "[EVAB::BotHandler <STATUS>]: Sign up your bot.\n";
#endif
    }
    else {
      std::cout << "[EVAB::BotHandler <ERROR>]: The server can't process your greeting.\n";
      exit(0);
    }
  }
  else{
    std::cerr << "[EVAB::BotHandler <ERROR>]: The connection with the server was unexpectedly lost.\n";
    exit(0);
  }
  getGlobalBotHandler().updateGameInfo();
}

void BotHandler::stablishConnectionWithServer() {
  if(!getGlobalBotHandler().flagConnected){
    if(getGlobalBotHandler().sClient.connect(net::SERVER_IP, net::NET_PORT) == sf::Socket::Done){
      getGlobalBotHandler().sClient.setBlocking(true);
#ifdef DEBUG
      std::cout << "[EVAB::BotHandler <STATUS>]: REMOTE PORT -> " << getGlobalBotHandler().sClient.getRemotePort();
      std::cout << "\n[EVAB::BotHandler <STATUS>]: REMOTE IP -> " << getGlobalBotHandler().sClient.getRemoteAddress();
      std::cout << "\n[EVAB::BotHandler <SPAM>]: I\'m ready to comply.\n";
#endif
      getGlobalBotHandler().greetServer();
    }
    else{
      std::cerr << "[EVAB::BotHandler <ERROR>]: Can\'t stablish a connection with the server.\n";
      exit(0);
    }
  }
  else {
    std::cout << "[EVAB::BotHandler <WARNING>]: The connection with the server can be done only 1 time.\n";
    exit(0);
  }
}

void BotHandler::onBotActionRotation(const float& rotInDegrees) {
  getGlobalBotHandler().currentRotation = rotInDegrees;
}

void BotHandler::onBotActionMoving(const bool& enableMoving) {
  if(!getGlobalBotHandler().attackForward)
    getGlobalBotHandler().movingForward = enableMoving;
#ifdef DEBUG
  else
    std::cout << "[EVAB::BotHandler <WARNING>]: An attacking action was sent previously. The current moving action won\'t be processed.\n";
#endif
}

void BotHandler::onBotActionAttack(const bool& enableAttack) {
  if(!getGlobalBotHandler().movingForward)
    getGlobalBotHandler().attackForward = enableAttack;
#ifdef DEBUG
  else
    std::cout << "[EVAB::BotHandler <WARNING>]: An moving action was sent previously. The current attacking action won\'t be processed.\n";
#endif
}

bool BotHandler::onBotGameplay() {
  sf::Packet toSendData;
  EvabBotActions actionPackage;

  actionPackage.newAircraftRotation = getGlobalBotHandler().currentRotation;
  actionPackage.moving = getGlobalBotHandler().movingForward;
  actionPackage.attacking = getGlobalBotHandler().attackForward;

  toSendData << actionPackage;
  if(getGlobalBotHandler().sClient.send(toSendData) == sf::Socket::Done){
#ifdef DEBUG
    std::cout << "[EVAB::BotHandler <STATUS>]: The bot\'s actions has been sent successfully.\n";
#endif
  }
  else{
    std::cerr << "[EVAB::BotHandler <ERROR>]: The connection with the server was unexpectedly lost.\n";
    return false;
  }
  return getGlobalBotHandler().updateGameInfo();
}

EvabBotPackage& BotHandler::getGameData() {
  return getGlobalBotHandler().gameDataPack;
}

int BotHandler::getMyId() {
  return getGlobalBotHandler().player_id;
}

}

#endif//BOT_HANDLER_HPP_
