#pragma once

#include <memory>
#include <optional>
#include <set>

#include "Runtime/RetroTypes.hpp"
#include "Runtime/Character/CCharAnimTime.hpp"

namespace metaforce {
class CAnimTreeNode;
class CPrimitive;
class IAnimReader;
struct CAnimSysContext;
struct CMetaAnimTreeBuildOrders;

enum class EMetaAnimType { Play, Blend, PhaseBlend, Random, Sequence };

class CPreAdvanceIndicator {
  bool x0_isTime;
  CCharAnimTime x4_time;
  const char* xc_string;
  /*
  u32 x10_;
  u32 x14_;
  u32 x18_;
  u32 x1c_;
  u32 x20_;
  u32 x24_;
  u32 x28_;
  u32 x2c_;
  u32 x30_;
  u32 x34_;
  u32 x38_;
  u16 x3c_;
  */
public:
  explicit CPreAdvanceIndicator(const CCharAnimTime& time) : x0_isTime(true), x4_time(time) {}
  explicit CPreAdvanceIndicator(const char* string) : x0_isTime(false), xc_string(string) {}
  const char* GetString() const { return xc_string; }
  bool IsString() const { return !x0_isTime; }
  const CCharAnimTime& GetTime() const { return x4_time; }
  bool IsTime() const { return x0_isTime; }
};

struct CMetaAnimTreeBuildOrders {
  std::optional<CPreAdvanceIndicator> x0_recursiveAdvance;
  std::optional<CPreAdvanceIndicator> x44_singleAdvance;
  static CMetaAnimTreeBuildOrders NoSpecialOrders() { return {}; }
  static CMetaAnimTreeBuildOrders PreAdvanceForAll(const CPreAdvanceIndicator& ind) {
    CMetaAnimTreeBuildOrders ret;
    ret.x44_singleAdvance.emplace(ind);
    return ret;
  }
};

class IMetaAnim {
public:
  virtual ~IMetaAnim() = default;
  virtual std::shared_ptr<CAnimTreeNode> GetAnimationTree(const CAnimSysContext& animSys,
                                                          const CMetaAnimTreeBuildOrders& orders) const;
  virtual void GetUniquePrimitives(std::set<CPrimitive>& primsOut) const = 0;
  virtual EMetaAnimType GetType() const = 0;
  virtual std::shared_ptr<CAnimTreeNode> VGetAnimationTree(const CAnimSysContext& animSys,
                                                           const CMetaAnimTreeBuildOrders& orders) const = 0;

  static void AdvanceAnim(IAnimReader& anim, const CCharAnimTime& dt);
  static CCharAnimTime GetTime(const CPreAdvanceIndicator& ind, const IAnimReader& anim);
};

} // namespace metaforce
