#pragma once

#include <array>
#include <list>
#include <memory>
#include <optional>
#include <vector>

#include "Runtime/CRandom16.hpp"
#include "Runtime/CToken.hpp"
#include "Runtime/rstl.hpp"
#include "Runtime/Graphics/CLineRenderer.hpp"
#include "Runtime/Particle/CElementGen.hpp"
#include "Runtime/Particle/CParticleGen.hpp"
#include "Runtime/Particle/CParticleSwoosh.hpp"

#include <zeus/CAABox.hpp>
#include <zeus/CColor.hpp>
#include <zeus/CTransform.hpp>
#include <zeus/CVector3f.hpp>

namespace metaforce {
class CElectricDescription;

class CParticleElectric : public CParticleGen {
  static u16 g_GlobalSeed;

public:
  static void SetGlobalSeed(u16 seed) { g_GlobalSeed = seed; }
  class CLineManager {
    friend class CParticleElectric;
    std::vector<zeus::CVector3f> x0_verts;
    std::array<float, 3> x10_widths = {1.f, 2.f, 3.f};
    std::array<zeus::CColor, 3> x1c_colors;
    zeus::CAABox x28_aabb = zeus::CAABox();
  };

  class CParticleElectricManager {
    friend class CParticleElectric;
    u32 x0_idx;
    u32 x4_slif;
    u32 x8_startFrame;
    u32 xc_endFrame;
    int x10_gpsmIdx = -1;
    int x14_epsmIdx = -1;

  public:
    CParticleElectricManager(u32 idx, u32 slif, u32 startFrame)
    : x0_idx(idx), x4_slif(slif), x8_startFrame(startFrame), xc_endFrame(startFrame + slif) {}
  };

private:
  TLockedToken<CElectricDescription> x1c_elecDesc;
  int x28_currentFrame = 0;
  int x2c_LIFE;
  double x30_curTime = 0.0;
  zeus::CVector3f x38_translation;
  zeus::CTransform x44_orientation;
  zeus::CTransform x74_invOrientation;
  zeus::CVector3f xa4_globalTranslation;
  zeus::CTransform xb0_globalOrientation;
  zeus::CVector3f xe0_globalScale = zeus::skOne3f;
  zeus::CVector3f xec_localScale = zeus::skOne3f;
  zeus::CTransform xf8_cachedXf;
  float x128 = 0.f;
  float x12c = 0.f;
  zeus::CAABox x130_buildBounds = zeus::CAABox();
  CRandom16 x14c_randState;
  int x150_SSEG = 8;
  int x154_SCNT = 1;
  int x158 = 0;
  float x15c_genRem = 0.f;
  zeus::CAABox x160_systemBounds = zeus::CAABox();
  std::optional<zeus::CVector3f> x178_overrideIPos;
  std::optional<zeus::CVector3f> x188_overrideIVel;
  std::optional<zeus::CVector3f> x198_overrideFPos;
  std::optional<zeus::CVector3f> x1a8_overrideFVel;
  zeus::CColor x1b8_moduColor;
  rstl::reserved_vector<bool, 32> x1bc_allocated;
  rstl::reserved_vector<std::unique_ptr<CParticleSwoosh>, 32> x1e0_swooshGenerators;
  rstl::reserved_vector<std::unique_ptr<CLineManager>, 32> x2e4_lineManagers;
  std::list<CParticleElectricManager> x3e8_electricManagers;
  std::vector<std::unique_ptr<CElementGen>> x400_gpsmGenerators;
  std::vector<std::unique_ptr<CElementGen>> x410_epsmGenerators;
  std::vector<zeus::CVector3f> x420_calculatedVerts;
  std::vector<float> x430_fractalMags;
  std::vector<zeus::CVector3f> x440_fractalOffsets;
  bool x450_24_emitting : 1 = true;
  bool x450_25_haveGPSM : 1 = false;
  bool x450_26_haveEPSM : 1 = false;
  bool x450_27_haveSSWH : 1 = false;
  bool x450_28_haveLWD : 1 = false;
  bool x450_29_transformDirty : 1 = true;

  size_t m_nextLineRenderer = 0;
  std::vector<std::unique_ptr<CLineRenderer>> m_lineRenderers;

  void SetupLineGXMaterial();
  void DrawLineStrip(const std::vector<zeus::CVector3f>& verts, float width, const zeus::CColor& color);
  void RenderLines();
  void RenderSwooshes();
  void UpdateCachedTransform();
  void UpdateLine(size_t idx, int frame);
  void UpdateElectricalEffects();
  void CalculateFractal(int start, int end, float ampl, float ampd);
  void CalculatePoints();
  void CreateNewParticles(int count);
  void AddElectricalEffects();
  void BuildBounds();

public:
  explicit CParticleElectric(const TToken<CElectricDescription>& desc);

  bool Update(double) override;
  void Render() override;
  void SetOrientation(const zeus::CTransform& orientation) override;
  void SetTranslation(const zeus::CVector3f& translation) override;
  void SetGlobalOrientation(const zeus::CTransform& orientation) override;
  void SetGlobalTranslation(const zeus::CVector3f& translation) override;
  void SetGlobalScale(const zeus::CVector3f& scale) override;
  void SetLocalScale(const zeus::CVector3f& scale) override;
  void SetParticleEmission(bool emitting) override;
  void SetModulationColor(const zeus::CColor&) override;
  void SetOverrideIPos(const zeus::CVector3f& vec) { x178_overrideIPos.emplace(vec); }
  void SetOverrideIVel(const zeus::CVector3f& vec) { x188_overrideIVel.emplace(vec); }
  void SetOverrideFPos(const zeus::CVector3f& vec) { x198_overrideFPos.emplace(vec); }
  void SetOverrideFVel(const zeus::CVector3f& vec) { x1a8_overrideFVel.emplace(vec); }
  const zeus::CTransform& GetOrientation() const override;
  const zeus::CVector3f& GetTranslation() const override;
  const zeus::CTransform& GetGlobalOrientation() const override;
  const zeus::CVector3f& GetGlobalTranslation() const override;
  const zeus::CVector3f& GetGlobalScale() const override;
  const zeus::CColor& GetModulationColor() const override;
  bool IsSystemDeletable() const override;
  std::optional<zeus::CAABox> GetBounds() const override;
  u32 GetParticleCount() const override;
  bool SystemHasLight() const override;
  CLight GetLight() const override;
  bool GetParticleEmission() const override;
  void DestroyParticles() override;
  void Reset() override {}
  void ForceParticleCreation(s32 count) {
    CGlobalRandom gRnd{x14c_randState};
    CreateNewParticles(count);
  }
  FourCC Get4CharId() const override { return FOURCC('ELSC'); }
};

} // namespace metaforce