#pragma once

#include "character/CharacterActionTypes.h"
#include <string>

namespace frostbite2D {

class CharacterObject;
class CharacterStateMachine;
struct CharacterActionDefinition;

struct CharacterStateContext {
  CharacterObject& owner;
  CharacterCommandBuffer& commandBuffer;
  const CharacterIntent& intent;
  float deltaTime = 0.0f;
};

class ICharacterStateNode {
public:
  virtual ~ICharacterStateNode() = default;

  virtual CharacterStateId GetId() const = 0;
  virtual void OnEnter(CharacterStateMachine& machine,
                       CharacterStateContext& context,
                       CharacterStateId previousState) {
    (void)machine;
    (void)context;
    (void)previousState;
  }
  virtual void OnUpdate(CharacterStateMachine& machine,
                        CharacterStateContext& context) = 0;
  virtual void OnExit(CharacterStateMachine& machine,
                      CharacterStateContext& context,
                      CharacterStateId nextState) {
    (void)machine;
    (void)context;
    (void)nextState;
  }
};

class ICharacterActionStateNode {
public:
  virtual ~ICharacterActionStateNode() = default;
  virtual bool TryEnter(CharacterStateMachine& machine,
                        CharacterStateContext& context) = 0;
};

class CharacterStateBase : public ICharacterStateNode {
public:
  explicit CharacterStateBase(CharacterStateId stateId);
  ~CharacterStateBase() override = default;

  CharacterStateId GetId() const override;

protected:
  void ChangeState(CharacterStateMachine& machine,
                   CharacterStateContext& context,
                   CharacterStateId nextState) const;
  void InvokeHook(CharacterStateMachine& machine,
                  CharacterObject& owner,
                  const CharacterActionDefinition* action,
                  const char* phase) const;
  bool TryStartAction(CharacterStateMachine& machine,
                      CharacterStateContext& context,
                      const std::string& actionId) const;
  bool TryStartAction(CharacterStateMachine& machine,
                      CharacterStateContext& context,
                      const std::string& requestedActionId,
                      const std::string& actionId) const;
  bool StartAction(CharacterStateMachine& machine,
                   CharacterStateContext& context,
                   const std::string& actionId) const;
  bool StartAction(CharacterStateMachine& machine,
                   CharacterStateContext& context,
                   const std::string& requestedActionId,
                   const std::string& actionId) const;
  bool TryStartRegisteredAction(CharacterStateMachine& machine,
                                CharacterStateContext& context) const;

  const CharacterActionDefinition* GetCurrentAction(const CharacterStateMachine& machine) const;
  const std::string& GetCurrentActionId(const CharacterStateMachine& machine) const;
  void SetCurrentAction(CharacterStateMachine& machine,
                        const CharacterActionDefinition* action,
                        const std::string& actionId) const;
  void ClearCurrentAction(CharacterStateMachine& machine) const;

  float& StateTime(CharacterStateMachine& machine) const;

private:
  CharacterStateId stateId_ = CharacterStateId::Idle;
};

} // namespace frostbite2D
