#include "character/states/common/MoveState.h"
#include "character/CharacterObject.h"
#include <cmath>

namespace frostbite2D {
namespace {

constexpr float kWalkMoveSpeedScale = 0.65f;
constexpr float kRunMoveSpeedScale = 1.0f;

bool HasMoveIntent(const CharacterIntent& intent) {
  return intent.moveMode != CharacterMoveMode::None
      && (std::abs(intent.moveX) > 0.01f || std::abs(intent.moveY) > 0.01f);
}

float ResolveMoveSpeedScale(const CharacterIntent& intent) {
  return intent.moveMode == CharacterMoveMode::Run
      ? kRunMoveSpeedScale
      : kWalkMoveSpeedScale;
}

const char* ResolveMoveActionId(const CharacterIntent& intent) {
  return intent.moveMode == CharacterMoveMode::Run ? "run" : "move";
}

const char* ResolveMoveAnimationTag(const CharacterIntent& intent) {
  return intent.moveMode == CharacterMoveMode::Run ? "run" : "move";
}

void SyncMovePresentation(CharacterObject& owner, const CharacterIntent& intent) {
  const char* animationTag = ResolveMoveAnimationTag(intent);
  if (owner.GetCurrentAction() != animationTag) {
    owner.PlayAnimationTag(animationTag);
  }
}

} // namespace

MoveState::MoveState()
    : CharacterStateBase(CharacterStateId::Move) {
}

void MoveState::OnEnter(CharacterStateMachine& machine,
                        CharacterStateContext& context,
                        CharacterStateId previousState) {
  (void)previousState;
  auto& owner = context.owner;
  auto& motor = owner.GetMotorMutable();
  if (!HasMoveIntent(context.intent)) {
    motor.StopGroundMovement();
    return;
  }

  motor.ApplyGroundInput(context.intent.moveX, context.intent.moveY,
                         ResolveMoveSpeedScale(context.intent));
  owner.SetFacing(motor.facing);
  owner.PlayAnimationTag(ResolveMoveAnimationTag(context.intent));
  InvokeHook(machine, owner, owner.FindAction(ResolveMoveActionId(context.intent)), "on_enter");
}

void MoveState::OnUpdate(CharacterStateMachine& machine,
                         CharacterStateContext& context) {
  auto& owner = context.owner;
  auto& motor = owner.GetMotorMutable();
  if (!motor.grounded) {
    ChangeState(machine, context, CharacterStateId::Fall);
    return;
  }

  if (TryStartRegisteredAction(machine, context)) {
    return;
  }
  if (context.commandBuffer.HasBuffered(CharacterCommandType::JumpPressed)) {
    context.commandBuffer.Consume(CharacterCommandType::JumpPressed);
    motor.Jump();
    ChangeState(machine, context, CharacterStateId::Jump);
    return;
  }

  if (!HasMoveIntent(context.intent)) {
    motor.StopGroundMovement();
    ChangeState(machine, context, CharacterStateId::Idle);
    return;
  }

  motor.ApplyGroundInput(context.intent.moveX, context.intent.moveY,
                         ResolveMoveSpeedScale(context.intent));
  owner.SetFacing(motor.facing);
  SyncMovePresentation(owner, context.intent);
}

} // namespace frostbite2D
