#pragma once
#include <algorithm>
#include <glm/glm.hpp>
#include <imgui.h>

namespace core
{
  namespace types
  {

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

      Color() = default;
      Color(float _r, float _g, float _b, float _a = 1.0f)
          : r(_r), g(_g), b(_b), a(_a) {}

      // Conversions
      Color(const glm::vec3 &v, float _a = 1.0f)
          : r(v.x), g(v.y), b(v.z), a(_a) {}

      Color(const ImVec4 &v)
          : r(v.x), g(v.y), b(v.z), a(v.w) {}

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

      // Add
      Color operator+(const Color &other) const
      {
        return {r + other.r, g + other.g, b + other.b, a + other.a};
      }

      Color &operator+=(const Color &other)
      {
        r += other.r;
        g += other.g;
        b += other.b;
        a += other.a;
        return *this;
      }

      // Subtract
      Color operator-(const Color &other) const
      {
        return {r - other.r, g - other.g, b - other.b, a - other.a};
      }

      Color &operator-=(const Color &other)
      {
        r -= other.r;
        g -= other.g;
        b -= other.b;
        a -= other.a;
        return *this;
      }

      // Multiply
      Color operator*(const Color &other) const
      {
        return {r * other.r, g * other.g, b * other.b, a * other.a};
      }

      Color operator*(float scalar) const
      {
        return {r * scalar, g * scalar, b * scalar, a * scalar};
      }

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

      Color &operator*=(float scalar)
      {
        r *= scalar;
        g *= scalar;
        b *= scalar;
        a *= scalar;
        return *this;
      }

      // Divide
      Color operator/(float scalar) const
      {
        float inv = 1.0f / scalar;
        return {r * inv, g * inv, b * inv, a * inv};
      }

      Color &operator/=(float scalar)
      {
        float inv = 1.0f / scalar;
        r *= inv;
        g *= inv;
        b *= inv;
        a *= inv;
        return *this;
      }

      // Equality
      bool operator==(const Color &other) const
      {
        return r == other.r && g == other.g && b == other.b && a == other.a;
      }

      bool operator!=(const Color &other) const
      {
        return !(*this == other);
      }

      // Linear interpolation
      static Color Lerp(const Color &a, const Color &b, float t)
      {
        return {
            a.r + (b.r - a.r) * t,
            a.g + (b.g - a.g) * t,
            a.b + (b.b - a.b) * t,
            a.a + (b.a - a.a) * t};
      }

      // Clamp all components between 0 and 1
      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);
      }
    };

    inline Color operator*(float scalar, const Color &c)
    {
      return c * scalar;
    }

  } // namespace types
} // namespace core
