#pragma once

#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/types/type_color.h>
#include <cstdint>

namespace frostbite2D {

enum class BlendMode {
  None,
  Normal,
  Additive,
  Screen,
  Premultiplied,
  Subtractive,
  Multiply
};

enum class PrimitiveType {
  Triangles,
  TriangleStrip,
  TriangleFan,
  Quads
};

struct Vertex {
  Vec2 position;
  Vec2 texCoord;
  float r, g, b, a;
  
  Vertex() = default;
  Vertex(const Vec2& pos, const Vec2& uv, float cr, float cg, float cb, float ca)
      : position(pos), texCoord(uv), r(cr), g(cg), b(cb), a(ca) {}
};

struct Quad {
  Vertex vertices[4];
  
  Quad() = default;
  
  static Quad create(const Rect& rect, float cr = 1.0f, float cg = 1.0f, 
                     float cb = 1.0f, float ca = 1.0f) {
    Quad q;
    Vec2 bl(rect.left(), rect.bottom());
    Vec2 br(rect.right(), rect.bottom());
    Vec2 tl(rect.left(), rect.top());
    Vec2 tr(rect.right(), rect.top());
    
    q.vertices[0] = Vertex(bl, Vec2(0, 0), cr, cg, cb, ca);
    q.vertices[1] = Vertex(br, Vec2(1, 0), cr, cg, cb, ca);
    q.vertices[2] = Vertex(tl, Vec2(0, 1), cr, cg, cb, ca);
    q.vertices[3] = Vertex(tr, Vec2(1, 1), cr, cg, cb, ca);
    
    return q;
  }
  
  static Quad createTextured(const Rect& destRect, const Rect& srcRect,
                         const Vec2& texSize, float cr = 1.0f, 
                         float cg = 1.0f, float cb = 1.0f, float ca = 1.0f,
                         bool shrinkSubTextureUVs = false) {
    Quad q;
    
    Vec2 bl(destRect.left(), destRect.bottom());
    Vec2 br(destRect.right(), destRect.bottom());
    Vec2 tl(destRect.left(), destRect.top());
    Vec2 tr(destRect.right(), destRect.top());
    
    auto resolveUvX = [&srcRect, &texSize, shrinkSubTextureUVs](bool useRightEdge) {
      float pixelInset = 0.0f;
      if (!shrinkSubTextureUVs) {
        return (useRightEdge ? srcRect.right() : srcRect.left()) / texSize.x;
      }
      if (useRightEdge && srcRect.right() < texSize.x) {
        pixelInset = -0.5f;
      } else if (!useRightEdge && srcRect.left() > 0.0f) {
        pixelInset = 0.5f;
      }
      return (useRightEdge ? srcRect.right() : srcRect.left()) / texSize.x +
             pixelInset / texSize.x;
    };

    auto resolveUvY = [&srcRect, &texSize, shrinkSubTextureUVs](bool useBottomEdge) {
      float pixelInset = 0.0f;
      if (!shrinkSubTextureUVs) {
        return (useBottomEdge ? srcRect.bottom() : srcRect.top()) / texSize.y;
      }
      if (useBottomEdge && srcRect.bottom() < texSize.y) {
        pixelInset = -0.5f;
      } else if (!useBottomEdge && srcRect.top() > 0.0f) {
        pixelInset = 0.5f;
      }
      return (useBottomEdge ? srcRect.bottom() : srcRect.top()) / texSize.y +
             pixelInset / texSize.y;
    };

    Vec2 uvBL(resolveUvX(false), resolveUvY(true));
    Vec2 uvBR(resolveUvX(true), resolveUvY(true));
    Vec2 uvTL(resolveUvX(false), resolveUvY(false));
    Vec2 uvTR(resolveUvX(true), resolveUvY(false));
    
    q.vertices[0] = Vertex(bl, uvBL, cr, cg, cb, ca);
    q.vertices[1] = Vertex(br, uvBR, cr, cg, cb, ca);
    q.vertices[2] = Vertex(tl, uvTL, cr, cg, cb, ca);
    q.vertices[3] = Vertex(tr, uvTR, cr, cg, cb, ca);
    
    return q;
  }
};

}
