#include "../../../include/asset_mgr.hpp"
#include "../../../include/shared.hpp"
#include "include/GTE_Sprite.hpp"
#include "include/gte_test_assets.hpp"
#include <PSX/Periphery/periphery.hpp>

namespace GTETest {
    using namespace JabyEngine;
    using namespace GTETest;

    namespace Jaby {
        static constexpr auto AnimationTime = 250_ms;
        static const struct {
            gte_float scale_left;
            gte_float scale_right;
        } animation[] = {
            {.scale_left = 1.0_gf, .scale_right = 1.0_gf},
            {.scale_left = 1.0_gf, .scale_right = 1.0_gf},

            {.scale_left = 1.2_gf, .scale_right = 1.5_gf},
            {.scale_left = 1.5_gf, .scale_right = 1.2_gf},
            {.scale_left = 1.2_gf, .scale_right = 1.5_gf},
            {.scale_left = 1.5_gf, .scale_right = 1.2_gf},

            {.scale_left = 1.8_gf, .scale_right = 2.3_gf},
            {.scale_left = 2.3_gf, .scale_right = 1.8_gf},
            {.scale_left = 1.8_gf, .scale_right = 2.3_gf},
            {.scale_left = 2.3_gf, .scale_right = 1.8_gf},

            {.scale_left = 3.2_gf, .scale_right = 4.5_gf},
            {.scale_left = 4.5_gf, .scale_right = 3.2_gf},
            {.scale_left = 3.2_gf, .scale_right = 4.5_gf},
            {.scale_left = 4.5_gf, .scale_right = 3.2_gf},

            {.scale_left = 1.8_gf, .scale_right = 2.3_gf},
            {.scale_left = 2.3_gf, .scale_right = 1.8_gf},
            {.scale_left = 1.8_gf, .scale_right = 2.3_gf},
            {.scale_left = 2.3_gf, .scale_right = 1.8_gf},

            {.scale_left = 1.2_gf, .scale_right = 1.5_gf},
            {.scale_left = 1.5_gf, .scale_right = 1.2_gf},
            {.scale_left = 1.2_gf, .scale_right = 1.5_gf},
            {.scale_left = 1.5_gf, .scale_right = 1.2_gf},
        };
        static IntervalTimer<uint8_t> animation_timer;
        static auto                   animation_id = 0;
        static constexpr auto         Position     = Make::PositionI16(GPU::Display::Width - 64, GPU::Display::Height - 64);

        static constexpr GTE_Sprite make_star_eye(GPU::PositionI16 pos) {
            return GTE_Sprite::create(Make::POLY_FT4(
                Make::AreaI16(pos, GPU::SizeI16(8, 8)),
                JabySTARTim.get_page_offset_clut4().add(0, 64),
                Make::TPage(JabySTARTim.get_texture_position(), GPU::SemiTransparency::B_add_F, GPU::TextureColorMode::clut4),
                Make::PageClut(JabySTARTim.get_clut_position()),
                GPU::Color24::Grey()
            ).linked());
        }

        static auto star_base = Make::SPRT(
            Make::AreaI16(Position, Make::SizeI16(64, 64)), 
            Make::OffsetPageWithClut(JabySTARTim.get_page_offset_clut4(), Make::PageClut(JabySTARTim.get_clut_position()))
        ).linked();
    
        static GTE_Sprite star_eyes[2] = {
            make_star_eye(Position.add(11, 30)), 
            make_star_eye(Position.add(33, 31))
        };
    }

    namespace Background {
        static constexpr auto ColorBase = 0xC0;

        static constexpr GPU::AreaI16 Area[2]      = {
            Make::AreaI16(-30, -30, 350,                 350), 
            Make::AreaI16(0, 0, GPU::Display::Width, GPU::Display::Width), 
        };
        static constexpr GPU::PositionI16 AreaPivot[2] = {
            Make::PositionI16(Area[0].size.width/2, Area[0].size.height/2),
            Make::PositionI16(Area[1].size.width/2, Area[1].size.height/2),
        };

        static GPU::POLY_G4 poly[2] = {
            Make::POLY_G4(Area[0], {GPU::Color24::Blue(ColorBase), GPU::Color24::Red(ColorBase), GPU::Color24::Green(ColorBase), GPU::Color24::Purple(ColorBase)}),
            Make::POLY_G4(Area[1], {GPU::Color24::Blue(ColorBase), GPU::Color24::Red(ColorBase), GPU::Color24::Green(ColorBase), GPU::Color24::Purple(ColorBase)}),
        };
    }

    static auto doener_fish = GTE_Sprite::create(Make::POLY_FT4(
        Make::AreaI16(Make::PositionI16(0, 0), Assets::Main::DoenerFishInfo.size),
        Assets::Main::DoenerFishInfo.tim.get_page_offset_clut4(),
        Make::TPage(Assets::Main::DoenerFishInfo.tim.get_texture_position(), GPU::SemiTransparency::B_add_F, GPU::TextureColorMode::clut4),
        Make::PageClut(Assets::Main::DoenerFishInfo.tim.get_clut_position()),
        GPU::Color24::Grey()
    ).linked());

    static auto gbl_rotation = 0.0_deg;

    static void setup() {
        Jaby::star_base.concat(Jaby::star_eyes[0].display.concat(Jaby::star_eyes[1].display));
        Jaby::star_eyes[0].scale = Jaby::animation[0].scale_right;
        Jaby::star_eyes[1].scale = Jaby::animation[0].scale_left;
        Jaby::animation_id       = 1;
        Jaby::animation_timer    = IntervalTimer<uint8_t>::create(Jaby::AnimationTime);

        doener_fish.area.position = GPU::PositionI16::create(100, 100);
        Shared::back_menu.reset();

        GTE::set_geom_offset(0, 0);
        GTE::set_geom_screen(256);
    }

    static bool update_or_exit() {
        Periphery::query_controller();
        if(Shared::back_menu.update(Make::PositionI16(0, GPU::Display::Height - 32))) {
            return true;
        }

        if(Jaby::animation_timer.is_expired()) {
            Jaby::star_eyes[0].scale = Jaby::animation[Jaby::animation_id].scale_right;
            Jaby::star_eyes[1].scale = Jaby::animation[Jaby::animation_id].scale_left;

            Jaby::animation_id = (Jaby::animation_id + 1)%(sizeof(Jaby::animation)/sizeof(Jaby::animation[0]));
            Jaby::animation_timer.reset();
        }

        for(size_t n = 0; n < sizeof(Background::poly)/sizeof(Background::poly[0]); n++) {
            auto matrix = [](size_t n) -> GTE::MATRIX {
                auto matrix = GTE::MATRIX::translated(-Background::AreaPivot[n].x, -Background::AreaPivot[n].y);
                matrix.rotate(0.0_deg, 0.0_deg, (n == 0) ? gbl_rotation : -gbl_rotation);
                return matrix.translate(Background::Area[n].position.x + Background::AreaPivot[n].x, Background::Area[n].position.y + Background::AreaPivot[n].y);
            }(n);
            matrix.apply_to_area(Background::poly[n], Make::AreaI16(Make::PositionI16(), Background::Area[n].size));
        }
        
        const auto matrix = GTE::MATRIX::rotated(-gbl_rotation, gbl_rotation, -gbl_rotation);
        doener_fish.apply(matrix);
        Jaby::star_eyes[0].apply();
        Jaby::star_eyes[1].apply();

        doener_fish.angle += 25.0_deg;
        gbl_rotation      += 2.5_deg;
        return false;
    }
    
    static void render() {
        for(const auto& poly : Background::poly) {
            GPU::render(poly);
        }
        doener_fish.render();
        GPU::render(Jaby::star_base);
        Shared::back_menu.render();
        GPU::wait_for_render(); //< Because we are single buffer
    }

    void main() {
        setup();

        while(true) {
            if(update_or_exit()) {
                break;
            }
            GPU::swap_buffers_vsync(1, false);
            render();
        }
    }
}