#pragma once

#include "character/CharacterActionLibrary.h"
#include "character/CharacterDataLoader.h"
#include "character/states/CharacterStateBase.h"
#include <memory>
#include <unordered_map>

namespace frostbite2D {

class CharacterObject;

class CharacterStateMachine {
public:
  using StateRegistry = std::unordered_map<CharacterStateId, std::unique_ptr<ICharacterStateNode>>;

  CharacterStateMachine() = default;

  void Configure(const character::CharacterConfig& config);
  void Reset();
  void Update(CharacterObject& owner,
              CharacterCommandBuffer& commandBuffer,
              const CharacterIntent& intent,
              float deltaTime);
  void ForceHurt(CharacterObject& owner, const CharacterActionDefinition* hurtAction);

  CharacterStateId GetState() const { return currentState_; }
  const std::string& GetCurrentActionId() const { return currentActionId_; }
  const CharacterActionDefinition* GetCurrentActionDefinition() const {
    return currentAction_;
  }

  bool CanAcceptGroundActions() const;
  bool CanBeInterrupted() const;
  bool IsMovementLocked() const;
  bool CanTurn() const;

  void RegisterState(std::unique_ptr<ICharacterStateNode> stateNode);
  void ClearRegisteredStates();
  ICharacterStateNode* FindStateNode(CharacterStateId stateId) const;

private:
  void ChangeState(CharacterObject& owner, CharacterStateId nextState);
  void EnterAction(CharacterObject& owner, const CharacterActionDefinition* action);
  void InvokeStateHook(CharacterObject& owner,
                       const CharacterActionDefinition* action,
                       const char* phase) const;
  bool TryStartAction(CharacterObject& owner,
                      CharacterCommandBuffer& commandBuffer,
                      const std::string& requestedActionId,
                      const std::string& actionId);
  bool StartAction(CharacterObject& owner,
                   const std::string& requestedActionId,
                   const std::string& actionId);
  bool TryStartRegisteredAction(CharacterStateContext& context);

  CharacterStateId currentState_ = CharacterStateId::Idle;
  std::string currentActionId_;
  const CharacterActionDefinition* currentAction_ = nullptr;
  float stateTime_ = 0.0f;
  StateRegistry stateRegistry_;

  friend class CharacterStateBase;
};

} // namespace frostbite2D
