#include "polypartiCL.hpp"
#include "raylib-cpp.hpp"
#include <vector>
#include "rlgl.h"
#include <exception>
#include <iostream>

using namespace std;

string roomName = "rooms/load.room";
string partName = "particles/load.prtc";


int main(int argc, char* argv[]) {
    const int fps = 60;
    const int screenWidth = 1920;
    const int screenHeight = 1080;
    int rotFrame = 0;
    float rotSpeed = -0.11f;
    float rotAngle = 0.0f;
    float d_interval_pct = 0.00001f;
    float stopAngle = 0.0f;
    bool rotate = false;
    bool drawLines = true;
    bool drawFPS = false;
    bool useShader = true;
    bool paused = false;
    //bool gentleTurn = false;
    //bool turning = false;

    // bool counterclockwise = false;
    //
    for (int i = 0; i < argc; i++) {
        if (argv[i] == "--rotate"s || argv[i] == "-r"s) {
            rotate = true;
        }
        if (argv[i] == "--nolines"s || argv[i] == "-l"s) {
            drawLines = false;
        }
        if (argv[i] == "--noblur"s || argv[i] == "-b"s) {
            useShader = false;
        }
    }

    Polyparti::Room sim(screenWidth, screenHeight, fps);

    vector<raylib::Vector2> lines;

    sim.loadRoom(roomName);
    sim.loadParticles(partName);
    sim.addFields();
    for (const auto& item : sim.getCorners()) {
        lines.push_back({item[0], item[1]});
    }

    raylib::Window window(screenWidth, screenHeight, "polypartiCL");
    //raylib::Button::key R(KEY_R);

    window.SetTargetFPS(fps);
    
    raylib::RenderTexture2D renderTarget(screenWidth, screenHeight);
    raylib::RenderTexture2D blur(screenWidth, screenHeight);
    raylib::RenderTexture2D rot(screenWidth, screenHeight);
    raylib::Shader blurShader("", "shaders/blur2.fs");
    raylib::Shader noBlur("", "shaders/noblur.fs");
    raylib::Shader rotShader("", "shaders/rotate.fs");
    raylib::Vector2 mouse_i, mouse_f, mouse_d;
    raylib::Vector2 midpoint(screenWidth * 0.5, screenHeight * 0.5);

    raylib::Image screenshot(screenWidth, screenHeight, WHITE);


    float dirH[2] = {1.0f, 0.0f};
    float dirV[2] = {0.0f, 1.0f};

    int resXY = blurShader.GetLocation("resolution");
    int dirXY = blurShader.GetLocation("direction");
    int angleGL = rotShader.GetLocation("angle");
    int resRotGL = rotShader.GetLocation("resolution");
    float resolution[2] = { float(screenWidth), float(screenHeight)};
    blurShader.SetValue(resXY, resolution, RL_SHADER_UNIFORM_VEC2);
    noBlur.SetValue(resXY, resolution, RL_SHADER_UNIFORM_VEC2);

    for(const auto& item : sim.getCorners()) { lines.push_back({item[0], item[1]});}

    while(!window.ShouldClose()) {
        if (IsKeyPressed(KEY_R)) rotate = !rotate;
        if (IsKeyPressed(KEY_L)) drawLines = !drawLines;
        if (IsKeyPressed(KEY_F)) drawFPS = !drawFPS;
        if (IsKeyPressed(KEY_D)) rotSpeed *= -1;
        if (IsKeyPressed(KEY_B)) useShader = !useShader;
        if (IsKeyPressed(KEY_SPACE)) paused = !paused;
        if (IsKeyPressed(KEY_T)) sim.reverseInterval();
        if (IsKeyPressed(KEY_S)) {
            screenshot.LoadFromScreen();
            screenshot.ExportAsCode("screenshot" + std::to_string(window.GetTime()));
        }

        if (!paused) {
            sim.updateFields(window.GetTime());
            if (rotate) {
                rotAngle += rotSpeed * window.GetFrameTime();
                rotAngle = std::fmod(rotAngle, 2.0f * PI);
            }
        }
        //for(const auto& item : sim.getCorners()) lines.push_back({item[0], item[1]});

        renderTarget.BeginMode();
        ClearBackground(BLACK);

        if(drawLines) {
            for(int i = 0; i < (lines.size() - 1); i++) { lines[i].DrawLine(lines[i+1], WHITE); }
        }

        for (int i = 0; i < sim.getFieldCount(); i++) {
            auto& field = sim.getField(i);
            int *color = field.getColor();
            rlBegin(RL_LINES);
            rlColor4ub(color[0], color[1], color[2], color[3]);
            for (const auto& p : field.getPositions()) {
                rlVertex2f(p[0]-1.0f,p[1]-1.0f);
                rlVertex2f(p[0]+1.0f,p[1]+1.0f);
            }
            rlEnd();
        }
        renderTarget.EndMode();

        if (useShader) {
            blurShader.SetValue(dirXY, dirH, SHADER_UNIFORM_VEC2);

            blur.BeginMode();
                ClearBackground(BLACK);
                blurShader.BeginMode();
                DrawTextureRec(renderTarget.texture, {0, 0, resolution[0], -resolution[1]},
                    {0, 0}, WHITE);
                blurShader.EndMode();
            blur.EndMode();

            blurShader.SetValue(dirXY, dirV, SHADER_UNIFORM_VEC2);
        }

        rotShader.SetValue(angleGL, &rotAngle, SHADER_UNIFORM_FLOAT);
        rotShader.SetValue(resRotGL, resolution, SHADER_UNIFORM_VEC2);

        while(window.Drawing()) {
            window.ClearBackground(BLACK);

            rot.BeginMode();
            ClearBackground(BLACK);
            if (useShader) {
                blurShader.BeginMode();
                DrawTextureRec(blur.texture, {0, 0, resolution[0], -resolution[1]}, {0,0}, WHITE);
                blurShader.EndMode();
            } else {
                //noBlur.BeginMode();
                DrawTextureRec(renderTarget.texture, {0, 0, resolution[0], -resolution[1]}, {0,0}, WHITE);
                //noBlur.EndMode();
            }
            rot.EndMode();
            rotShader.BeginMode();
            DrawTextureRec(rot.texture, {0, 0, resolution[0], -resolution[1]}, {0,0}, WHITE);
            rotShader.EndMode();

            if(drawFPS) window.DrawFPS(10,10);
         }

    }
}
