#pragma once

#include <algorithm>
#include <string>
#include <sstream>
#include <iomanip>
#include <glm/glm.hpp>
#ifdef IMGUI_VERSION
#include <imgui.h>
#define HAS_IMGUI
#endif

namespace OX
{

  struct Color
  {
    float r, g, b, a;

    // Constructors
    constexpr Color() noexcept : r(1), g(1), b(1), a(1) {}
    constexpr Color(float _r, float _g, float _b, float _a = 1.0f) noexcept
        : r(_r), g(_g), b(_b), a(_a) {}
    Color(const glm::vec3 &v, float _a = 1.0f) : r(v.x), g(v.y), b(v.z), a(_a) {}
    Color(const glm::vec4 &v) : r(v.x), g(v.y), b(v.z), a(v.w) {}

    // Conversion operators
    operator glm::vec3() const { return {r, g, b}; }
    operator glm::vec4() const { return {r, g, b, a}; }




    // Arithmetic
    Color operator+(const Color &o) const { return {r + o.r, g + o.g, b + o.b, a + o.a}; }
    Color &operator+=(const Color &o)
    {
      r += o.r;
      g += o.g;
      b += o.b;
      a += o.a;
      return *this;
    }
    Color operator-(const Color &o) const { return {r - o.r, g - o.g, b - o.b, a - o.a}; }
    Color &operator-=(const Color &o)
    {
      r -= o.r;
      g -= o.g;
      b -= o.b;
      a -= o.a;
      return *this;
    }
    Color operator*(const Color &o) const { return {r * o.r, g * o.g, b * o.b, a * o.a}; }
    Color &operator*=(const Color &o)
    {
      r *= o.r;
      g *= o.g;
      b *= o.b;
      a *= o.a;
      return *this;
    }
    Color operator*(float s) const { return {r * s, g * s, b * s, a * s}; }
    Color &operator*=(float s)
    {
      r *= s;
      g *= s;
      b *= s;
      a *= s;
      return *this;
    }
    Color operator/(float s) const
    {
      float inv = 1.0f / s;
      return {r * inv, g * inv, b * inv, a * inv};
    }
    Color &operator/=(float s)
    {
      float inv = 1.0f / s;
      r *= inv;
      g *= inv;
      b *= inv;
      a *= inv;
      return *this;
    }

    bool operator==(const Color &o) const { return r == o.r && g == o.g && b == o.b && a == o.a; }
    bool operator!=(const Color &o) const { return !(*this == o); }

    // Utilities
    static Color Lerp(const Color &a, const Color &b, float t)
    {
      return a + (b - a) * t;
    }

    void Clamp()
    {
      r = std::clamp(r, 0.0f, 1.0f);
      g = std::clamp(g, 0.0f, 1.0f);
      b = std::clamp(b, 0.0f, 1.0f);
      a = std::clamp(a, 0.0f, 1.0f);
    }

    Color WithAlpha(float alpha) const { return {r, g, b, alpha}; }
    Color Inverted() const { return {1.0f - r, 1.0f - g, 1.0f - b, a}; }
    Color Grayscale() const
    {
      float gray = (r + g + b) / 3.0f;
      return {gray, gray, gray, a};
    }

    std::string ToHexString(bool includeAlpha = false) const
    {
      auto toU8 = [&](float x)
      { return static_cast<int>(std::clamp(x, 0.0f, 1.0f) * 255.0f); };
      std::ostringstream oss;
      oss << std::hex << std::setw(2) << std::setfill('0') << toU8(r)
          << std::setw(2) << std::setfill('0') << toU8(g)
          << std::setw(2) << std::setfill('0') << toU8(b);
      if (includeAlpha)
      {
        oss << std::setw(2) << std::setfill('0') << toU8(a);
      }
      return oss.str();
    }

    static Color FromHexString(const std::string &hex)
    {
      unsigned int v = 0;
      std::stringstream ss;
      ss << std::hex << hex;
      ss >> v;
      if (hex.size() <= 6)
      {
        return {((v >> 16) & 0xFF) / 255.0f,
                ((v >> 8) & 0xFF) / 255.0f,
                (v & 0xFF) / 255.0f,
                1.0f};
      }
      return {((v >> 24) & 0xFF) / 255.0f,
              ((v >> 16) & 0xFF) / 255.0f,
              ((v >> 8) & 0xFF) / 255.0f,
              (v & 0xFF) / 255.0f};
    }

    // Common color factories
    static constexpr Color White(float alpha = 1.0f) noexcept { return {1.0f, 1.0f, 1.0f, alpha}; }
    static constexpr Color Black(float alpha = 1.0f) noexcept { return {0.0f, 0.0f, 0.0f, alpha}; }
    static constexpr Color Red(float alpha = 1.0f) noexcept { return {1.0f, 0.0f, 0.0f, alpha}; }
    static constexpr Color Green(float alpha = 1.0f) noexcept { return {0.0f, 1.0f, 0.0f, alpha}; }
    static constexpr Color Blue(float alpha = 1.0f) noexcept { return {0.0f, 0.0f, 1.0f, alpha}; }
    static constexpr Color Yellow(float alpha = 1.0f) noexcept { return {1.0f, 1.0f, 0.0f, alpha}; }
    static constexpr Color Magenta(float alpha = 1.0f) noexcept { return {1.0f, 0.0f, 1.0f, alpha}; }
    static constexpr Color Cyan(float alpha = 1.0f) noexcept { return {0.0f, 1.0f, 1.0f, alpha}; }
    static constexpr Color Gray(float alpha = 1.0f) noexcept { return {0.5f, 0.5f, 0.5f, alpha}; }
    static constexpr Color LightGray(float alpha = 1.0f) noexcept { return {0.75f, 0.75f, 0.75f, alpha}; }
    static constexpr Color DarkGray(float alpha = 1.0f) noexcept { return {0.25f, 0.25f, 0.25f, alpha}; }
    static constexpr Color Orange(float alpha = 1.0f) noexcept { return {1.0f, 0.5f, 0.0f, alpha}; }
    static constexpr Color Pink(float alpha = 1.0f) noexcept { return {1.0f, 0.4f, 0.7f, alpha}; }
    static constexpr Color Purple(float alpha = 1.0f) noexcept { return {0.6f, 0.0f, 0.6f, alpha}; }
    static constexpr Color Brown(float alpha = 1.0f) noexcept { return {0.6f, 0.3f, 0.0f, alpha}; }
    static constexpr Color Transparent() noexcept { return {0.0f, 0.0f, 0.0f, 0.0f}; }
  };

  // Free function for scalar multiplication
  inline Color operator*(float s, const Color &c)
  {
    return c * s;
  }

} // namespace CE::types
