#include "MCRenderer.h"
#include "Framebuffer3D.h"
#include "Input.h"
#include "VertexBuffer.h"
#include <glm/ext/matrix_transform.hpp>

MCRenderer::MCRenderer(int size_x, int size_y, int size_z)
    : size_x(size_x), size_y(size_y), size_z(size_z),
      render_shader(Shader("Shader/mc.vs", "Shader/mc.fs", "Shader/mc.gs")),
      noise_shader(Shader("Shader/noise.vs", "Shader/noise.fs")),
      noise(Framebuffer3D(size_x, size_y, size_z)), camera(Camera(90, 1920, 1080, 0.1, 1000.0)),
      albedo("Resources/Textures/PavingStones/PavingStones070_2K_Color.jpg", Texture::Settings()),
      bump("Resources/Textures/PavingStones/PavingStones070_2K_Displacement.jpg",
           Texture::Settings()) {
    // Each noise layer needs vertices, so we define two triangles which span a rectangle
    float data[6][2] = {{-1.0f, -1.0f}, {-1.0, 1.0}, {1.0, -1.0},
                        {1.0f, 1.0f},   {-1.0, 1.0}, {1.0, -1.0}};

    vertex_rectangle.set_data(sizeof(data), 2, data, GL_STATIC_DRAW);

    // Move the camera to the center
    camera.translate(glm::vec3(size_x / 2.0, size_z / 2.0, size_y / 2.0));
}

void MCRenderer::render(float delta) {
    // Create the noise
    glViewport(0, 0, size_x, size_y);

    noise_shader.use();
    noise.bind_and_clear();

    if (Input::is_key_down(GLFW_KEY_E)) {
        height += delta;
    }
    if (Input::is_key_down(GLFW_KEY_Q)) {
        height -= delta;
    }
    if (Input::is_key_down(GLFW_KEY_W)) {
        camera.translate(glm::vec3(0.0, 0.0, -delta * 20.0));
    }
    if (Input::is_key_down(GLFW_KEY_S)) {
        camera.translate(glm::vec3(0.0, 0.0, delta * 20.0));
    }
    if (Input::is_key_down(GLFW_KEY_D)) {
        camera.rotate(-delta * 90.0, glm::vec3(0.0, 1.0, 0.0));
    }
    if (Input::is_key_down(GLFW_KEY_A)) {
        camera.rotate(delta * 90.0, glm::vec3(0.0, 1.0, 0.0));
    }

    noise_shader.setFloat("height", height);

    for (int i = 0; i < size_z; i++) {
        // Create one layer
        noise_shader.setFloat("layer", i * (1.0f / (static_cast<float>(size_z - 1))));
        noise.bind_layer(i);

        // Each layer is made up of a rectangle of vertices
        vertex_rectangle.draw();
    }

    // Actual rendering
    glViewport(0, 0, 1920, 1080);
    render_shader.use();

    // Bind the screen framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    // glDrawBuffer(GL_BACK);

    glClearColor(0.6f, 0.9f, 0.9f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Bind the camera to the rendering shader
    render_shader.setMat4("proj", camera.get_projection());
    render_shader.setMat4("view", camera.get_view());

    // Set other variables
    render_shader.setFloat("step", 1.0 / static_cast<float>(size_z - 1));
    render_shader.setFloat("threshold", 0.4);

    render_shader.setInt("size_x", size_x);
    render_shader.setInt("size_y", size_y);
    render_shader.setInt("size_z", size_z);

    // Bind the noise texture to the rendering shader
    noise.bind_to(0);
    albedo.bind_to(1);
    bump.bind_to(2);

    // Draw all layers as polygons
    glDrawArrays(GL_POINTS, 0, size_x * size_y * size_z);
    glPolygonMode(GL_FRONT_AND_BACK, Input::is_key_down(GLFW_KEY_X) ? GL_LINE : GL_FILL);
}