#pragma once
#include "gpu_primitives.hpp"

namespace JabyEngine {
    namespace Make {
        template<typename T, typename...ARGS>
        static constexpr T creator_template(const ARGS&...args) {
            return T::create(args...);
        }

        // ###################################################################

        static constexpr GPU::SizeI8 SizeI8() {
            return creator_template<GPU::SizeI8>(0_i8, 0_i8);
        }
        static constexpr GPU::SizeI8 SizeI8(int8_t x, int8_t y) {
            return creator_template<GPU::SizeI8>(x, y);
        }

        // ###################################################################

        static constexpr GPU::SizeU8 SizeU8() {
            return creator_template<GPU::SizeU8>(0_u8, 0_u8);
        }
        static constexpr GPU::SizeU8 SizeU8(uint8_t x, uint8_t y) {
            return creator_template<GPU::SizeU8>(x, y);
        }

        // ###################################################################

        static constexpr GPU::SizeI16 SizeI16() {
            return creator_template<GPU::SizeI16>(0_i16, 0_i16);
        }
        static constexpr GPU::SizeI16 SizeI16(int16_t x, int16_t y) {
            return creator_template<GPU::SizeI16>(x, y);
        }

        // ###################################################################

        static constexpr GPU::SizeU16 SizeU16() {
            return creator_template<GPU::SizeU16>(0_u16, 0_u16);
        }
        static constexpr GPU::SizeU16 SizeU16(uint16_t x, uint16_t y) {
            return creator_template<GPU::SizeU16>(x, y);
        }
        
        // ###################################################################

        static constexpr GPU::PositionI8 PositionI8() {
            return creator_template<GPU::PositionI8>(0_i8, 0_i8);
        }
        static constexpr GPU::PositionI8 PositionI8(int8_t x, int8_t y) {
            return creator_template<GPU::PositionI8>(x, y);
        }

        // ###################################################################

        static constexpr GPU::PositionU8 PositionU8() {
            return creator_template<GPU::PositionU8>(0_u8, 0_u8);
        }
        static constexpr GPU::PositionU8 PositionU8(int8_t x, int8_t y) {
            return creator_template<GPU::PositionU8>(x, y);
        }

        // ###################################################################

        static constexpr GPU::PositionI16 PositionI16() {
            return creator_template<GPU::PositionI16>(0_i16, 0_i16);
        }
        static constexpr GPU::PositionI16 PositionI16(int16_t x, int16_t y) {
            return creator_template<GPU::PositionI16>(x, y);
        }

        // ###################################################################

        static constexpr GPU::PositionU16 PositionU16() {
            return creator_template<GPU::PositionU16>(0_u16, 0_u16);
        }
        static constexpr GPU::PositionU16 PositionU16(uint16_t x, uint16_t y) {
            return creator_template<GPU::PositionU16>(x, y);
        }

        // ###################################################################

        static constexpr GPU::Vertex Vertex() {
            return creator_template<GPU::Vertex>(0_i16, 0_i16);
        }
        static constexpr GPU::Vertex Vertex(int16_t x, int16_t y) {
            return creator_template<GPU::Vertex>(x, y);
        }

        // ###################################################################

        static constexpr GPU::AreaI16 AreaI16() {
            return creator_template<GPU::AreaI16>(0, 0, 0, 0);
        }
        static constexpr GPU::AreaI16 AreaI16(int16_t x, int16_t y, int16_t w, int16_t h) {
            return creator_template<GPU::AreaI16>(x, y, w, h);
        }
        static constexpr GPU::AreaI16 AreaI16(GPU::PositionI16 pos, GPU::SizeI16 size) {
            return creator_template<GPU::AreaI16>(pos, size);
        }
        
        // ###################################################################

        static constexpr GPU::AreaU16 AreaU16() {
            return creator_template<GPU::AreaU16>(0, 0, 0, 0);
        }
        static constexpr GPU::AreaU16 AreaU16(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
            return creator_template<GPU::AreaU16>(x, y, w, h);
        }
        static constexpr GPU::AreaU16 AreaU16(GPU::PositionU16 pos, GPU::SizeU16 size) {
            return creator_template<GPU::AreaU16>(pos, size);
        } 

        // ###################################################################

        static constexpr GPU::PageOffset PageOffset() {
            return creator_template<GPU::PageOffset>(0_u8, 0_u8);
        }
        static constexpr GPU::PageOffset PageOffset(uint8_t u, uint8_t v) {
            return creator_template<GPU::PageOffset>(u, v);
        }

        // ###################################################################

        static constexpr GPU::PageClut PageClut() {
            return creator_template<GPU::PageClut>(0_u16, 0_u16);
        }
        static constexpr GPU::PageClut PageClut(uint16_t x, uint16_t y) {
            return creator_template<GPU::PageClut>(x, y);
        }
        static constexpr GPU::PageClut PageClut(const GPU::PositionU16& clut_pos) {
            return creator_template<GPU::PageClut>(clut_pos);
        }

        // ###################################################################

        static constexpr GPU::TPage TPage() {
            return creator_template<GPU::TPage>(0_u16, 0_u16, GPU::SemiTransparency::B_add_F, GPU::TextureColorMode::clut4);
        }
        static constexpr GPU::TPage TPage(uint16_t x, uint16_t y, GPU::SemiTransparency transparency, GPU::TextureColorMode clut_color) {
            return creator_template<GPU::TPage>(x, y, transparency, clut_color);
        }
        static constexpr GPU::TPage TPage(const GPU::PositionU16& tex_pos, GPU::SemiTransparency transparency, GPU::TextureColorMode clut_color) {
            return creator_template<GPU::TPage>(tex_pos, transparency, clut_color);
        }

        // ###################################################################        

        static constexpr GPU::TexPage TexPage() {
            return creator_template<GPU::TexPage>(PositionU16(), GPU::TextureColorMode::clut4, GPU::SemiTransparency::B_Half_add_F_Half, false);
        }
        static constexpr GPU::TexPage TexPage(const GPU::PositionU16& tex_pos, GPU::TextureColorMode tex_color, GPU::SemiTransparency transparency = GPU::SemiTransparency::B_Half_add_F_Half, bool dither = false) {
            return creator_template<GPU::TexPage>(tex_pos, tex_color, transparency, dither);
        }

        // ###################################################################

        static constexpr GPU::OffsetPageWithClut OffsetPageWithClut() {
            return creator_template<GPU::OffsetPageWithClut>(PageOffset(), PageClut());
        }
        static constexpr GPU::OffsetPageWithClut OffsetPageWithClut(GPU::PageOffset tex_offset, GPU::PageClut clut) {
            return creator_template<GPU::OffsetPageWithClut>(tex_offset, clut);
        }

        // ###################################################################

        static constexpr GPU::VertexColor VertexColor() {
            return creator_template<GPU::VertexColor>(Vertex(), GPU::Color24::Black());
        }
        static constexpr GPU::VertexColor VertexColor(GPU::Vertex pos, GPU::Color24 color) {
            return creator_template<GPU::VertexColor>(pos, color);
        }

        // ###################################################################

        static constexpr GPU::ColorVertex ColorVertex() {
            return creator_template<GPU::ColorVertex>(GPU::Color24::Black(), Vertex());
        }
        static constexpr GPU::ColorVertex ColorVertex(GPU::Color24 color, GPU::Vertex pos) {
            return creator_template<GPU::ColorVertex>(color, pos);
        }

        // ###################################################################

        static constexpr GPU::LINE_F_SINGLE LINE_F(const GPU::Color24& color, const GPU::Vertex& start_point, const GPU::Vertex& end_point) {
            return GPU::LINE_F::create(color, start_point, end_point);
        }

        template<typename...ARGS>
        static constexpr GPU::LINE_F_MULTI<sizeof...(ARGS) + 1> LINE_F(const GPU::Color24& color, const GPU::Vertex& start_point, const ARGS&...rest) {
            return GPU::LINE_F::create(color, start_point, rest...);
        }

        static constexpr GPU::LINE_G_SINGLE LINE_G(const GPU::ColorVertex& start_point, const GPU::ColorVertex& end_point) {
            return GPU::LINE_G::create(start_point, end_point);
        }

        template<typename...ARGS>
        static constexpr GPU::LINE_G_MULTI<sizeof...(ARGS) + 1> LINE_G(const GPU::ColorVertex& start_point, const ARGS&...rest) {
            return GPU::LINE_G::create(start_point, rest...);
        }

        // ###################################################################
        // ###################################################################
        // ###################################################################

        static constexpr GPU::POLY_F3 POLY_F3(const GPU::Vertex (&verticies)[3], GPU::Color24 color) {
            return creator_template<GPU::POLY_F3>(verticies, color);
        }

        static constexpr GPU::POLY_FT3 POLY_FT3(const GPU::Vertex (&verticies)[3], const GPU::PageOffset (&tex_offset)[3], GPU::TPage tpage, GPU::PageClut clut, GPU::Color24 color = GPU::Color24::Grey()) {
            return creator_template<GPU::POLY_FT3>(verticies, tex_offset, tpage, clut, color);
        }

        static constexpr GPU::POLY_FT3 POLY_FT3(const GPU::POLY_FT3::VertexEx (&vertices_ex)[3], GPU::TPage tpage, GPU::PageClut clut, GPU::Color24 color) {
            return creator_template<GPU::POLY_FT3>(vertices_ex, tpage, clut, color);
        }
        
        static constexpr GPU::POLY_G3 POLY_G3(const GPU::Vertex (&verticies)[3], const GPU::Color24 (&color)[3]) {
            return creator_template<GPU::POLY_G3>(verticies, color);
        }

        static constexpr GPU::POLY_G3 POLY_G3(const GPU::VertexColor (&verticies_ex)[3]) {
            return creator_template<GPU::POLY_G3>(verticies_ex);
        }

        static constexpr GPU::POLY_GT3 POLY_GT3(const GPU::Vertex (&verticies)[3], const GPU::PageOffset (&tex_offset)[3], const GPU::Color24 (&color)[3], GPU::TPage tpage, GPU::PageClut clut) {
            return creator_template<GPU::POLY_GT3>(verticies, tex_offset, color, tpage, clut); 
        }
        
        static constexpr GPU::POLY_GT3 POLY_GT3(const GPU::POLY_GT3::VertexEx (&verticies_ex)[3], GPU::TPage tpage, GPU::PageClut clut) {
            return creator_template<GPU::POLY_GT3>(verticies_ex, tpage, clut);
        }

        static constexpr GPU::POLY_F4 POLY_F4(const GPU::Vertex (&verticies)[4], GPU::Color24 color) {
            return creator_template<GPU::POLY_F4>(verticies, color);
        }
                
        static constexpr GPU::POLY_F4 POLY_F4(const GPU::AreaI16& area, GPU::Color24 color) {
            return creator_template<GPU::POLY_F4>(area, color);
        }

        static constexpr GPU::POLY_FT4 POLY_FT4(const GPU::Vertex (&verticies)[4], const GPU::PageOffset (&tex_offset)[4], GPU::TPage tpage, GPU::PageClut clut, GPU::Color24 color) {
            return creator_template<GPU::POLY_FT4>(verticies, tex_offset, tpage, clut, color);
        }
        
        static constexpr GPU::POLY_FT4 POLY_FT4(const GPU::POLY_FT4::VertexEx (&vertices_ex)[4], GPU::TPage tpage, GPU::PageClut clut, GPU::Color24 color = GPU::Color24::Grey()) {
            return creator_template<GPU::POLY_FT4>(vertices_ex, tpage, clut, color);
        }
        
        static constexpr GPU::POLY_FT4 POLY_FT4(const GPU::AreaI16& area, const GPU::PageOffset& tex_offset, GPU::TPage tpage, GPU::PageClut clut, GPU::Color24 color = GPU::Color24::Grey()) {
            return creator_template<GPU::POLY_FT4>(area, tex_offset, tpage, clut, color);
        }

        static constexpr GPU::POLY_G4 POLY_G4(const GPU::Vertex (&verticies)[4], const GPU::Color24 (&color)[4]) {
            return creator_template<GPU::POLY_G4>(verticies, color);
        }

        static constexpr GPU::POLY_G4 POLY_G4(const GPU::VertexColor (&verticies_ex)[4]) {
            return creator_template<GPU::POLY_G4>(verticies_ex);
        }

        static constexpr GPU::POLY_G4 POLY_G4(const GPU::AreaI16& area, const GPU::Color24 (&color)[4]) {
            return creator_template<GPU::POLY_G4>(area, color); 
        }

        static constexpr GPU::POLY_GT4 POLY_GT4(const GPU::Vertex (&verticies)[4], const GPU::PageOffset (&tex_offset)[4], const GPU::Color24 (&color)[4], GPU::TPage tpage, GPU::PageClut clut) {
            return creator_template<GPU::POLY_GT4>(verticies, tex_offset, color, tpage, clut);
        }

        static constexpr GPU::POLY_GT4 POLY_GT4(const GPU::POLY_GT4::VertexEx (&verticies_ex)[4], GPU::TPage tpage, GPU::PageClut clut) {
            return creator_template<GPU::POLY_GT4>(verticies_ex, tpage, clut); 
        }

        static constexpr GPU::POLY_GT4 POLY_GT4(const GPU::AreaI16& area, const GPU::PageOffset& tex_offset, GPU::TPage tpage, GPU::PageClut clut, const GPU::Color24 (&color)[4]) {
            return creator_template<GPU::POLY_GT4>(area, tex_offset, tpage, clut, color);
        }

        // ###################################################################

        static constexpr GPU::TILE_1 TILE_1() {
            return creator_template<GPU::TILE_1>(Vertex(), GPU::Color24::Black());
        }

        static constexpr GPU::TILE_1 TILE_1(const GPU::Vertex& position, const GPU::Color24& color) {
            return creator_template<GPU::TILE_1>(position, color);
        }

        static constexpr GPU::TILE_8 TILE_8() {
            return creator_template<GPU::TILE_8>(Vertex(), GPU::Color24::Black());
        }

        static constexpr GPU::TILE_8 TILE_8(const GPU::Vertex& position, const GPU::Color24& color) {
            return creator_template<GPU::TILE_8>(position, color);
        }

        static constexpr GPU::TILE_16 TILE_16() {
            return creator_template<GPU::TILE_16>(Vertex(), GPU::Color24::Black());
        }

        static constexpr GPU::TILE_16 TILE_16(const GPU::Vertex& position, const GPU::Color24& color) {
            return creator_template<GPU::TILE_16>(position, color);
        }

        static constexpr GPU::TILE TILE() {
            return creator_template<GPU::TILE>(AreaI16(), GPU::Color24::Black());
        }

        static constexpr GPU::TILE TILE(const GPU::AreaI16& area, const GPU::Color24& color) {
            return creator_template<GPU::TILE>(area, color);
        }

        // ###################################################################

        static constexpr GPU::SPRT_1 SPRT_1() {
            return creator_template<GPU::SPRT_1>(Vertex(), OffsetPageWithClut(), GPU::Color24::Black());
        }

        static constexpr GPU::SPRT_1 SPRT_1(const GPU::Vertex& position, const GPU::OffsetPageWithClut& tex_offset_w_clut, const GPU::Color24& color = GPU::Color24::Grey()) {
            return creator_template<GPU::SPRT_1>(position, tex_offset_w_clut, color);
        }

        static constexpr GPU::SPRT_8 SPRT_8() {
            return creator_template<GPU::SPRT_8>(Vertex(), OffsetPageWithClut(), GPU::Color24::Black());
        }

        static constexpr GPU::SPRT_8 SPRT_8(const GPU::Vertex& position, const GPU::OffsetPageWithClut& tex_offset_w_clut, const GPU::Color24& color = GPU::Color24::Grey()) {
            return creator_template<GPU::SPRT_8>(position, tex_offset_w_clut, color);
        }

        static constexpr GPU::SPRT_16 SPRT_16() {
            return creator_template<GPU::SPRT_16>(Vertex(), OffsetPageWithClut(), GPU::Color24::Black());
        }

        static constexpr GPU::SPRT_16 SPRT_16(const GPU::Vertex& position, const GPU::OffsetPageWithClut& tex_offset_w_clut, const GPU::Color24& color = GPU::Color24::Grey()) {
            return creator_template<GPU::SPRT_16>(position, tex_offset_w_clut, color);
        }

        static constexpr GPU::SPRT SPRT() {
            return creator_template<GPU::SPRT>(AreaI16(), OffsetPageWithClut(), GPU::Color24::Black());
        }

        static constexpr GPU::SPRT SPRT(const GPU::AreaI16& area, const GPU::OffsetPageWithClut& tex_offset_w_clut, const GPU::Color24& color = GPU::Color24::Grey()) {
            return creator_template<GPU::SPRT>(area, tex_offset_w_clut, color);
        }
    }
}