#include "grunt.hpp"

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

//#include "defines.hpp"
#include "playstate.hpp"
#include "stage.hpp"
#include "kungflowutils.hpp" // for quantization
#include "spaces/space.hpp" // for tile

#include "../character.hpp"
#include "../character_modelview.hpp"

GruntController::GruntController(const KungFlowGame *game, const PlayState *playState, Character *character, e_GruntType gruntType) : CharacterAIController(game, playState, character), gruntType(gruntType) {

  character->GetModelView()->AddMoveOwnership( e_MoveType_Jab  , new CharacterJabMove  (playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().jabStats) ); // todo: probably should put those default stats elsewhere
  character->GetModelView()->AddMoveOwnership( e_MoveType_Dodge, new CharacterDodgeMove(playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().dodgeStats) );
  character->GetModelView()->AddMoveOwnership( e_MoveType_Jump , new CharacterJumpMove (playState, character->GetModelView()->GetCharacterSprite(), character->GetModelView()->GetStats().jumpStats) );

  basePosition = character->GetPosition();

}

GruntController::~GruntController() {
}

void GruntController::OnStepPhysicsTime(CharacterModelView *characterModelView) {

  CharacterAIController::OnStepPhysicsTime(characterModelView);


  // some base vars

  bool dummyMode = false;
  //float skillFactor = 1.0f * _AI_SKILLFACTOR;
  float activationDistance = 2.0f; // from this distance and smaller, get busy
  float futureFactor = 0.25f; // aim how far in the future?
  float rotateRadius = 1.7f;
  float maxDeviation = 1.7f; // 2.2f
  float rotateSpeed = 3.0f;
  float phaseVariance = 1.2f;

  Vector2 position = character->GetPosition();

  //int flowAxis = playState->GetStage()->GetStadium()->GetFlowAxis();
  //if (position.coords[flowAxis] < playState->GetStage()->GetStadium()->GetFlowPosition().coords[flowAxis]) character->Expire();

  if (!assignedOpponent) {
    characterModelView->SetDesiredVelocity(0);
    characterModelView->SetDesiredBodyAngle((-playState->GetStage()->GetStadium()->GetFlowDirection()).GetAngle());
    return;
  }

  float q = position.coords[0] * phaseVariance; // sin/cos phase offset
  float r = position.coords[1] * phaseVariance;
  Vector2 adaptedBasePosition = basePosition + Vector2( sin(q + game->GetPhysicsTime_ms() * 0.001f * rotateSpeed) , cos(r + game->GetPhysicsTime_ms() * 0.001f * rotateSpeed) ) * rotateRadius;
  Vector2 assignedOpponentPosition = assignedOpponent->GetPosition();
  Vector2 toAssignedOpponent = assignedOpponentPosition - position;
  Vector2 assignedOpponentFuturePosition = assignedOpponent->GetPosition() + playState->GetStage()->GetStadium()->GetFlowMovement() * toAssignedOpponent.GetLength() * futureFactor;
  Vector2 toAssignedOpponentFuture = assignedOpponentFuturePosition - position;
  toAssignedOpponentFuture = toAssignedOpponentFuture.EnforceMaximumDeviation(adaptedBasePosition - position, maxDeviation);

  if ((toAssignedOpponentFuture * playState->GetStage()->GetStadium()->GetFlowDirection()).GetLength() > activationDistance) {
    Vector2 toBasePosition = adaptedBasePosition - position;
    characterModelView->SetDesiredDirection(toBasePosition.GetNormalized(_DEFAULTVECTOR));
    characterModelView->SetDesiredVelocity(std::min(toBasePosition.GetLength(), playState->GetMediumVelocity()));
    characterModelView->SetDesiredBodyAngle(toBasePosition.GetNormalized(_DEFAULTVECTOR).GetAngle());
    return;
  }

  Vector2 targetMovement;

  switch (gruntType) {

    case e_GruntType_Rusher:

      targetMovement = toAssignedOpponentFuture * 2.0f * (0.2f + 0.8f * _AI_SKILLFACTOR);//.GetNormalizedMax(playState->GetMediumVelocity());
      break;

    case e_GruntType_Defender:

      break;

    default:
      break;

  }

  Vector2 flattenVec = Vector2(1, 1);
  //flattenVec.coords[playState->GetStage()->GetStadium()->GetFlowAxis()] *= 0.7f; // less expressive forwards/backwards movement
  //targetMovement *= flattenVec;
  targetMovement = GetQuantizedDirection(targetMovement * flattenVec, playState->GetQuantizationFactor());
  characterModelView->SetDesiredDirection(targetMovement.GetNormalized(_DEFAULTVECTOR));//.GetNormalizedMax(1.0f));
  characterModelView->SetDesiredVelocity(std::min(targetMovement.GetLength(), playState->GetMediumVelocity()));
  characterModelView->SetDesiredBodyAngle(toAssignedOpponent.GetNormalized(_DEFAULTVECTOR).GetAngle());

/*
  float walkPow = 0.8f; // lower: more active / lively. higher: more apathic / lazy
  walkPow *= 0.5f + 0.5f * NormalizedClamp(targetMovement.GetLength(), 2.5f, 6.0f); // farther away characters get progressively more relaxed
  //printf("%f\n", forceFieldMovement.GetLength());
  characterModelView->SetDesiredVelocity(pow(std::min(targetMovement.GetLength(), 1.0f), walkPow) * playState->GetMediumVelocity());
*/

  if (dummyMode) return;

  // attack

  float jabThresholdDistance = 2.0f;
  Vector2 desiredJabTranslation = toAssignedOpponent + playState->GetStage()->GetStadium()->GetFlowMovement() * 0.2f;
  Vector2 desiredJabDirection = desiredJabTranslation.GetNormalized(0);

  // only if enemy close enough
  if (desiredJabTranslation.GetLength() < jabThresholdDistance) {// && Random(0, 100) < 1.0f) {
    characterModelView->RequestJab(desiredJabDirection, Clamp(desiredJabTranslation.GetLength() / jabThresholdDistance, 0.0f, 1.0f));
  }

}
