#ifndef _CSTATEMACHINE
#define _CSTATEMACHINE

#include "rstl/string.hpp"
#include "rstl/vector.hpp"
#include "string.h"

#include "MetroidPrime/Enemies/CAiFuncMap.hpp"

class CAi;
class CStateManager;
class CInputStream;

class CAiState;
class CAiTrigger {
public:
  CAiTrigger()
  : x0_func(nullptr), xc_arg(0.f), x10_andTrigger(nullptr), x14_state(nullptr), x18_lNot(false) {}
  CAiTrigger* GetAnd() const { return x10_andTrigger; }
  CAiState* GetState() const { return x14_state; }

  bool CallFunc(CStateManager& mgr, CAi& ai) {
    bool ret = true;
    if (x0_func) {
      ret = (ai.*x0_func)(mgr, xc_arg);
      if (x18_lNot) {
        ret = !ret;
      }
    }

    return ret;
  }

  void Setup(CAiTriggerFunc func, bool lnot, float arg, CAiTrigger* andTrig) {
    x0_func = func;
    xc_arg = arg;
    x10_andTrigger = andTrig;
    x18_lNot = lnot;
  }

  void Setup(CAiTriggerFunc func, bool lnot, float arg, CAiState* state) {
    x0_func = func;
    xc_arg = arg;
    x14_state = state;
    x18_lNot = lnot;
  }

private:
  CAiTriggerFunc x0_func;
  float xc_arg;
  CAiTrigger* x10_andTrigger;
  CAiState* x14_state;
  bool x18_lNot;
};

class CAiState {
public:
  CAiState(CAiStateFunc func, const char* name)
  : x0_func(func)
  , x2c_numTriggers(0)
  , x30_firstTrigger(nullptr) {
    strncpy(xc_name, name, 31);
  }

  CAiTrigger* GetTrig(int idx) { return &x30_firstTrigger[idx]; }
  const char* GetName() const { return xc_name; }
  void SetTriggers(CAiTrigger* triggers) { x30_firstTrigger = triggers; }
  void SetNumTriggers(int numTriggers) { x2c_numTriggers = numTriggers; }
  int GetNumTriggers() const { return x2c_numTriggers; }

  void CallFunc(CStateManager& mgr, CAi& ai, EStateMsg msg, float arg) const {
    if (x0_func) {
      (ai.*x0_func)(mgr, msg, arg);
    }
  }

private:
  CAiStateFunc x0_func;
  char xc_name[32];
  uint x2c_numTriggers;
  CAiTrigger* x30_firstTrigger;
};

class CStateMachine {
public:
  explicit CStateMachine(CInputStream& in);

  int GetStateIndex(const rstl::string& state) const;
  const rstl::vector< CAiState >& GetStateVector() const { return x0_states; }

private:
  rstl::vector< CAiState > x0_states;
  rstl::vector< CAiTrigger > x10_triggers;
};

class CStateMachineState {
public:
  CStateMachineState();

  void Update(CStateManager& mgr, CAi& ai, float delta);
  void SetState(CStateManager& mgr, CAi& ai, int state);
  void SetState(CStateManager& mgr, CAi&, const CStateMachine* machine, const rstl::string& state);
  const rstl::vector< CAiState >& GetStateVector() const { return x0_machine->GetStateVector(); }
  void Setup(const CStateMachine* machine);
  const char* GetName() const;
  void SetDelay(float delay) { x10_delay = delay; }
  float GetTime() const { return x8_time; }
  float GetRandom() const { return xc_random; }
  float GetDelay() const { return x10_delay; }
  void SetCodeTrigger() { x18_24_codeTrigger = true; }

private:
  const CStateMachine* x0_machine;
  CAiState* x4_state;
  float x8_time;
  float xc_random;
  float x10_delay;
  float x14_;
  bool x18_24_codeTrigger : 1;
};

#endif // _CSTATEMACHINE
