#include "character_aicontroller.hpp"

#include "gaia/gaia.hpp"
using namespace gaia;

//#include "main.hpp"
#include "playstate.hpp"
#include "stage.hpp"
#include "../character.hpp"
//#include "../character_modelview.hpp"
//#include "kungflowutils.hpp"

CharacterAIController::CharacterAIController(const KungFlowGame *game, const PlayState *playState, const Character *character) : CharacterController(game, playState, e_ControllerType_AI), character(character) {

  randomSeed = character->GetUniqueID() * 123.0f;

  closestOpponent = nullptr;
  assignedOpponent = nullptr;
  //isClosestToOpponent = false;
  //isTopXClosestToOpponent = false;
  nthClosestToAssignedOpponent = 1000;

  //forceUpdate = true;
  personnelChangeConnection = playState->GetStage()->sig_PersonnelChange.connect(boost::bind(&CharacterAIController::UpdateCaches, this));
  UpdateCaches();
  updateInterval_ms = 1000;//1000;
  timeSinceLastUpdate_ms = updateInterval_ms * Random(0.0f, 1.0f); // let all characters start update at different times to spread CPU and bring randomness
}

CharacterAIController::~CharacterAIController() {
  personnelChangeConnection.disconnect();
}

void CharacterAIController::UpdateCaches() {

  //printf("y\n");

  //printf("%i\n", character->GetUniqueID());
  //const std::vector<Character*> opponents = stage->GetCharacters(opponentTeamID);
  Vector2 position = character->GetPosition();
  e_Team opponentTeam = (character->GetTeam() == e_Team_BadGuys) ? e_Team_GoodGuys : e_Team_BadGuys;
  this->closestOpponents = playState->GetStage()->GetClosestCharacters(opponentTeam, position, 3);
  this->closestMates = playState->GetStage()->GetClosestCharacters(character->GetTeam(), position, 3, character);

  // a better way might be a rating that would only switch to different closest guys when reaching a certain treshold
  closestOpponent = playState->GetStage()->GetClosestCharacter(opponentTeam, position + character->GetMovement().GetNormalizedMax(2.0f) * 0.1f);
  assignedOpponent = closestOpponent; // default

  //this->isClosestToOpponent = false;
  //this->isTopXClosestToOpponent = false;

  assignedOpponents.clear();
  playState->GetStage()->GetAssignedOpponentCharacters(character, assignedOpponents);

  if (!assignedOpponents.empty()) {
    //printf("%i\n", assignedOpponents.size());

    // sort assigned opponents, closest first
    std::vector<CharacterProximity> proximities;
    for (unsigned int i = 0; i < assignedOpponents.size(); i++) {
      //if (assignedOpponents.at(i)->GetTeamID() == teamID && assignedOpponents.at(i) != ignore) {
      CharacterProximity prox;
      prox.character = assignedOpponents.at(i);
      prox.distance = position.GetDistance(assignedOpponents.at(i)->GetPosition());
      proximities.push_back(prox);
    }
    std::sort(proximities.begin(), proximities.end());
    assignedOpponent = proximities.at(0).character;
  }


  // find out if we are part of the elite warriors that may attack the opponent

  if (assignedOpponent != nullptr) {
    Vector2 opponentPosition = assignedOpponent->GetPosition();
    Character *closestTeamPlayer = playState->GetStage()->GetClosestCharacter(character->GetTeam(), opponentPosition);
    if (closestTeamPlayer != nullptr) {

      /*
      bool oppIsHuman = false;
      if (playState->GetStage()->GetHumanCharactersRef().size() > 0) {
        oppIsHuman = (playState->GetStage()->GetHumanCharactersRef().at(0) == assignedOpponent);
      }
      */

      std::vector<Character*> enemysBlacklist;
      playState->GetStage()->GetAssignedOpponentCharacters(assignedOpponent, enemysBlacklist);
      // sort enemy's list by proximity
      std::vector<CharacterProximity> ourProximities;
      for (unsigned int i = 0; i < enemysBlacklist.size(); i++) {
        CharacterProximity prox;
        prox.character = enemysBlacklist.at(i);
        prox.distance = enemysBlacklist.at(i)->GetPosition().GetDistance(assignedOpponent->GetPosition());
        //if (enemysBlacklist.at(i).GetController() == ai
        //  prox.distance += enemysBlacklist.at(i).GetController()->GetNthClosestToAssignedOpponent() * 10.0f; // this keeps the current order intact somewhat, by giving a penalty for people higher on the list (the rich get richer! hehe)
        ourProximities.push_back(prox);
      }
      std::sort(ourProximities.begin(), ourProximities.end());
      for (unsigned int i = 0; i < ourProximities.size(); i++) {
        if (ourProximities.at(i).character == character) {
          //if (abs(nthClosestToAssignedOpponent - i) > 5) // only switch on large changes (buggy, because this doesn't communicate with other nth's, so we get double/missing n positions)
          //nthClosestToAssignedOpponent = i * 0.4f + nthClosestToAssignedOpponent * 0.6f; // only switch if there's 2 positions of difference
          nthClosestToAssignedOpponent = i;
          break;
        }
      }

      /*
      if (closestTeamPlayer->GetController() == this) {// && oppIsHuman) { // i am the best for this job! woohoo!

        this->isClosestToOpponent = true;
        //if (character->GetTeamID() == 1) printf("closest! :D\n");

      } else { // if (!isClosestToOpponent) {

        // not the best :( maybe we are part of the closest X though, so we can get in line
        std::vector<Character*> closestFightMates = playState->GetStage()->GetClosestCharacters(character->GetTeamID(), opponentPosition, 3);
        for (unsigned int i = 0; i < closestFightMates.size(); i++) {
          if (closestFightMates.at(i)->GetController() == this) {
            //int positionsTaken = 0;
            this->isTopXClosestToOpponent = true;
            //positionsTaken++;
            //if (character->GetTeamID() == 1) printf("topx :)\n");
            break;
          }
        }

      }
      */

    } else this->nthClosestToAssignedOpponent = 0; // no mates == we are always the closest
  }

  // check if pass requester is still alive
  if (passRequest.active) {
    const std::vector<Character*> &charsRef = playState->GetStage()->GetAllCharactersRef();
    const std::vector<Character*>::const_iterator charIter = std::find(charsRef.begin(), charsRef.end(), passRequest.requester);
    if (charIter == charsRef.end()) ResetPassRequest();
    /*
    while (charIter != charsRef.end()) {
      if ((*charIter) == passRequest.requester) {
        stillAlive = true;
        break;
      } else charIter++;
    }
    if (stillAlive == false) {
      ResetPassRequest();
    }
    */
  }

  //if (character->GetTeamID() == 1) printf("\n");

}

void CharacterAIController::OnStepPhysicsTime(CharacterModelView *characterModelView) {

  timeSinceLastUpdate_ms += playState->GetPhysicsTimeStep_ms();
  if (timeSinceLastUpdate_ms > updateInterval_ms) {
    UpdateCaches();
    //printf("UPDATE\n");
    timeSinceLastUpdate_ms = 0;
  }

  if (passRequest.active) {
    passRequest.elapsedTime_ms += playState->GetPhysicsTimeStep_ms();
    if (passRequest.elapsedTime_ms > 2000) {
      ResetPassRequest();
    }
  }

}
