/******************************************************************************
*	File		-	simple_render_scene.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/10/27 (y/m/d)
*	Mod Date	-	2021/10/27 (y/m/d)
*	Description	-	Displays a simple scene that tests basic render features.
******************************************************************************/

#include "simple_render_scene.h"
#include <core/core.h>
#include <gui/gui.h>
#include <utils/helpers.h>
#include <utils/logger.h>
#include <renderer/texture.h>
#include <renderer/frame_buffer.h>
#include <renderer/orthographic_camera.h>
#include <input/input_manager.h>
#include <utils/stb/std_image_write.h>
#include <utils/stb/stb_image.h>

#include <thread>        
#include <chrono> 

#include <cstdlib>
#include <ctime>

namespace lunarium
{
    SimpleRenderScene::SimpleRenderScene(uint32_t logCat)
        : BaseScene(logCat), mStressTest(true), mFrameTime(0.0), mNumFrames(0)
    {
        srand((u32)time(0));
    }

    SimpleRenderScene::~SimpleRenderScene()
    {
        delete mpTestImageLoad;
    }

    void SimpleRenderScene::OnLoad()
    {
        Logger::Info(mLogCat, "Running Simple Render Test Scene");
        mTextBoxWidth = 500;


        // Currently the full default window size
        mImageSize.Width = 1280;
        mImageSize.Height = 720;

        // Create the render textures
        mFrameBufferOne = FrameBuffer::Create(mImageSize.Width, mImageSize.Height); //Core::Graphics().CreateRenderTexture(mImageSize.Width, mImageSize.Height, 4);
        mFrameBufferTwo = FrameBuffer::Create(1024, 1024); //Core::Graphics().CreateRenderTexture(1024, 1024, 4);

        // Load test image
        int w, h, n;
        stbi_set_flip_vertically_on_load(1);
        unsigned char* buffer = stbi_load("LinkToThePast1_sized.png", &w, &h, &n, 0);

        TextureFormat format = TextureFormat::RGBA;
        if (n == 3)
        {
            format = TextureFormat::RGB;
        }
        mpTestImageLoad = Texture::Create(buffer, w, h, format);
        mSrcWidth = w;
        mSrcHeight = h;

        //delete[] buffer;
        stbi_set_flip_vertically_on_load(1);
        buffer = stbi_load("lunarium_test_image.png", &w, &h, &n, 0);

        format = TextureFormat::RGBA;
        if (n == 3)
        {
            format = TextureFormat::RGB;
        }
        mpTestImageLoad2 = Texture::Create(buffer, w, h, format);
        //delete[] buffer;

        mQuads = new Rectangle[NUM_QUADS];
        for (int i = 0; i < NUM_QUADS; i++)
        {
            float x = (rand() % 1000);
            float y = (rand() % 600);
            float w = (rand() % 200) + 10;
            float h = (rand() % 200) + 10;

            mQuads[i] = Rectangle(x, y, w, h);
        }

        mQuadColors = new Color[NUM_QUADS];
        mQuadTexures = new Texture*[NUM_QUADS];
        
        for (int i = 0; i < NUM_QUADS; i++)
        {
            mQuadTexures[i] =  (i % 2 == 0) ? mpTestImageLoad : mpTestImageLoad2;
            mQuadColors[i].R = ((float)(rand() % 10000)) / 10000.0f;
            mQuadColors[i].G = ((float)(rand() % 10000)) / 10000.0f;
            mQuadColors[i].B = ((float)(rand() % 10000)) / 10000.0f;
            mQuadColors[i].A = 1.0f;
        }

        angle = 0.0f;
        box_angle = 0.0f;
    }

    void SimpleRenderScene::OnTick(double delta)
    {
        if (Core::Input().IsKeyDown(KeyCode::ESCAPE))
        {
            Core::GetInstance().SignalShutdown();
        }

        // Textbox size adjustment
        if (Core::Input().IsKeyDown(KeyCode::LEFT))
        {
            mTextBoxWidth -= 10;
        }

        if (Core::Input().IsKeyDown(KeyCode::RIGHT))
        {
            mTextBoxWidth += 10;
        }

        if (Core::Input().IsKeyDown(KeyCode::R))
        {
            mSrcWidth -= 10.0f;
            mSrcHeight -= 10.0f;
        }

        if (Core::Input().IsKeyDown(KeyCode::F))
        {
            mSrcWidth += 10.0f;
            mSrcHeight += 10.0f;
        }

        mTextBoxWidth = Math::ClampI(mTextBoxWidth, 20, 500);

        // Image Size adjustment
        if (Core::Input().IsKeyDown(KeyCode::DOWN))
        {
            mImageSize.Width -= 10;
            mImageSize.Height -= 10;
        }

        if (Core::Input().IsKeyDown(KeyCode::UP))
        {
            mImageSize.Width += 10;
            mImageSize.Height += 10;
        }

        if (Core::Input().IsKeyDown(KeyCode::PAGE_UP))
        {
            angle += 0.1f;
        }

        if (Core::Input().IsKeyDown(KeyCode::PAGE_DOWN))
        {
            angle -= 0.1f;
        }

        if (Core::Input().IsKeyPressed(KeyCode::TAB))
        {
            mStressTest = !mStressTest;
        }

        if (Core::Input().IsKeyPressed(KeyCode::Q))
        {
            // Test writing out a rendered image with transparency
            Renderer2D& g = Core::Graphics();
            Color prev = g.GetClearColor();
            g.SetClearColor(Color(0.0f, 0.0f, 0.0f, 0.0f));
            mFrameBufferTwo->Bind();
            OrthographicCamera cam(Vec2f{0.0f, 0.0f}, Sizef{(float)mFrameBufferTwo->GetTexture()->GetWidth(), (float)mFrameBufferTwo->GetTexture()->GetHeight()});
            g.BeginDraw(&cam);
            // if (Failed(result))
            // {
            //     Logger::Warn(mLogCat, "Unable to render to texture: %s", result.Description.c_str());
            //     return;
            // }

            Logger::Info(mLogCat, "Running transparent image test");

            g.DrawQuad(Rectangle(500, 400, 300, 300), Color(0.5f, 0.0f, 0.75f, 1.0f));
            g.DrawQuad(Rectangle(200, 300, 100, 100), Color(0.2f, 0.5f, 0.4f, 1.0f), nullptr, 45.0f);
            g.DrawString("This is a test of rendering an image with transparency", 
                            Rectangle::MakeFromTopLeft(50, 50, 600, 200), Color(0.0f, 1.0f, 0.2f, 1.0f), 0.5f);
            
            g.EndDraw();
            mpRenderedImage = mFrameBufferTwo->GetTexture();
            mFrameBufferTwo->Unbind();
            //mpRenderedImage = Core::GetInstance().EndRenderToTexture();
            g.SetClearColor(prev);

            stbi_flip_vertically_on_write(1);
            stbi_write_png("lunarium_test_image.png", mpRenderedImage->GetWidth(), mpRenderedImage->GetHeight(), 4, 
                            mpRenderedImage->GetPixels(), mpRenderedImage->GetWidth() * 4);
            //Logger::Info(mLogCat, "")
        }

        mImageSize.Width = Math::ClampI(mImageSize.Width, 320, 1280);
        mImageSize.Height = Math::ClampI(mImageSize.Height, 180, 720);

        // Render to texture testing
        // mFrameBufferOne->Bind();
        // OrthographicCamera cam(Vec2f{0.0f, 0.0f}, Sizef{(float)mFrameBufferOne->GetTexture()->GetWidth(), (float)mFrameBufferOne->GetTexture()->GetHeight()});
        // Renderer2D& g = Core::Graphics();
        // g.BeginDraw(&cam);
        // // OpRes result = Core::GetInstance().BeginRenderToTexture(mFrameBufferOne);
        // // if (Failed(result))
        // // {
        // //     Logger::Error(mLogCat, "Unable to render to texture: %s", result.Description.c_str());
        // //     return;
        // // }


        // g.DrawQuad(Rectangle(300, 300, 150, 150), Color::Red());
        // g.DrawQuad(Rectangle(200, 300, 100, 100), Color(0.2f, 0.5f, 0.4f, 1.0f), nullptr, 45.0f);
        // //g.DrawFilledEllipse(glm::vec2(600, 300), glm::vec2(100, 150), Color(1.0f, 0.0f, 1.0f, 1.0f), 100);
        // // g.DrawString("This is a test of the text renderer!", Rectangle::MakeFromTopLeft(100, 200, mTextBoxWidth, 300), 
        // //                         Color(0.0f, 1.0f, 1.0f, 1.0f), 0.5f, g.DefaultFont());

        // //mpRenderedImage = Core::GetInstance().EndRenderToTexture();
        // g.EndDraw();
        // mpRenderedImage = mFrameBufferOne->GetTexture();

        box_angle += 0.01f;
        int w, h;
        Core::MainWindow().GetFramebufferSize(&w, &h);
        OrthographicCamera main_cam(Vec2f { 0.0f, 0.0f }, Sizef { (float)w, (float)h });
        Core::GUI().NewFrame();
        mTimer.Reset();
        Core::Graphics().BeginDraw(&main_cam);

        if (mStressTest)
        {
            RenderBatchStressTest(Core::Graphics());
        }
        else
        {
            RenderScene(Core::Graphics());
        }


        Core::Graphics().EndDraw(); // Graphics end draw must happen before GUI end frame
        DrawStatsGUI();
        Core::GUI().EndFrame();
        Core::MainWindow().SwapBuffers();
        mFrameTime += mTimer.GetElapsedSeconds();
        mNumFrames++;
        mRenderStats = Core::Graphics().GetFrameStats();
        Core::Graphics().ResetFrameStats();
        //std::this_thread::sleep_for (std::chrono::seconds(2));
    }

    void SimpleRenderScene::RenderScene(Renderer2D& g)
    {
        // g.DrawImage(*mpRenderedImage, Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mpRenderedImage->GetWidth(), (float)mpRenderedImage->GetHeight()), 
        //                Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mImageSize.Width, (float)mImageSize.Height), Color(1.0f, 1.0f, 1.0f, 1.0f), angle);

        //g.DrawQuad(Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mImageSize.Width, (float)mImageSize.Height), Color(1.0f, 1.0f, 0.0f, 1.0f), nullptr, angle);

        // g.DrawQuad(Rectangle(200, 200, 128.0f, 128.0f), Color(0.0f, 1.0f, 0.0f, 1.0f));//, nullptr, box_angle);
        // g.DrawQuad(Rectangle(400, 300, 64.0f, 64.0f), Color(0.0f, 1.0f, 1.0f, 1.0f), nullptr, box_angle);
        // g.DrawQuad(Rectangle(600, 300, 100.0f, 100.0f), Color::Blue(), nullptr, 45.0f);
        // g.DrawQuad(Rectangle(500, 600, 100.0f, 100.0f), Color(0.25f, 0.75f, 0.8f), nullptr, 65.0f);
        // g.DrawQuad(Rectangle(800, 500, 100.0f, 100.0f), Color(0.75f, 0.5f, 0.425f), nullptr, 90.0f);
        // g.DrawQuad(Rectangle(300, 400, 64.0f, 64.0f), Color(0.25f, 0.5f, 1.0f, 1.0f), nullptr, box_angle * -1);


        //g.DrawQuads(mQuads, NUM_QUADS, mQuadColors);

        //g->DrawImage(*mpTestImageLoad, glm::vec2(0.0f, 0.0f), Color::White());
        //Rectangle src = Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mpTestImageLoad->GetWidth(), (float)mpTestImageLoad->GetHeight());
        //Rectangle src = Rectangle::MakeFromTopLeft(0.0f, 0.0f, mSrcWidth, mSrcHeight);
        // Rectangle src = Rectangle::MakeFromTopLeft(0.0f, 0.0f, 16, 16);
        // Rectangle dest = Rectangle::MakeFromTopLeft(100.0f, 100.0f, 512.0f, 512.0f);
        //g.DrawImage(*mpTestImageLoad, src, dest, Color(1.0f, 1.0f, 1.0f, 0.9f));
        
        // g->DrawImage(*mpTestImageLoad, src,
        //             dest, Color(1.0f, 1.0f, 1.0f, 0.8f));
    }
    
    void SimpleRenderScene::RenderBatchStressTest(Renderer2D& g)
    {
        for (int i = 0; i < mNumQuadsToRender; i++)
        {   
            Texture* t = mUseTextures ? mQuadTexures[i] : nullptr;
            g.DrawQuad(mQuads[i], mQuadColors[i], t, box_angle + (i / 10.0f));
        }

    }

    
    void SimpleRenderScene::DrawStatsGUI()
    {
        ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_FirstUseEver);
        ImGui::SetNextWindowSize(ImVec2(400, 600));
        ImGui::Begin("RENDER INFO");
        ImGui::BeginChild("Per Frame", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetFrameHeightWithSpacing() * 3.5f), true);
        ImGui::Text("Per Frame");
        ImGui::Separator();
        ImGui::Text("Number of triangles: %d", mRenderStats.NumTris);
        ImGui::Text("Draw calls: %d", mRenderStats.DrawCalls);
        ImGui::EndChild();
        ImGui::BeginChild("General", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetFrameHeightWithSpacing() * 4.5f), true);
        ImGui::Text("General Info");
        ImGui::Separator();
        ImGui::Text("Average Render time: %f", Core::GetInstance().GetFrameData().AverageFrameTime);
        ImGui::Text("FPS: %d", Core::GetInstance().GetFrameData().CurrentFPS);
        ImGui::Text("Frame Number: %d", mNumFrames);
        ImGui::EndChild();
        ImGui::BeginChild("Settings", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetFrameHeightWithSpacing() * 3.5f), true);
        ImGui::Text("Settings");
        ImGui::Separator();
        ImGui::InputInt("Number of Quads to draw", &mNumQuadsToRender);
        ImGui::Checkbox("Draw Textures", &mUseTextures);
        ImGui::EndChild();
        ImGui::End();

        mNumQuadsToRender = Math::ClampI(mNumQuadsToRender, 0, NUM_QUADS);
    }
}