#include "game.h"

void framebuffer_size_callback(GLFWwindow *window, int width, int height);

void calcCollision(Player *&player, Object *object)
{
    c2Manifold manifold;
    c2AABBtoAABBManifold(player->playerBody, object->objectBody, &manifold);
    if (manifold.count > 0)
    {
        if (manifold.n.y == 1)
        {
            player->onGround++;
            player->vSpeed = 0.0f;
            player->Position.y = object->position.y - player->Size.y;
        }
        if (manifold.n.y == -1)
        {
            player->Position.y = object->position.y + object->size.y - 50.0f;
            player->vSpeed = -player->vSpeed * .3f;
        }
        if (manifold.n.x == 1)
        {
            player->Position.x = object->position.x - player->Size.x + 50.0f;
            player->hSpeed = -player->hSpeed * 0.01f;
        }
        if (manifold.n.x == -1)
        {
            player->Position.x = object->position.x + object->size.x - 50.0f;
            player->hSpeed = -player->hSpeed * 0.01f;
        }
    }
};
void calcCollisionE(Enemy *&enemy, Object *object)
{
    c2Manifold manifold;
    c2AABBtoAABBManifold(enemy->EnemyBody, object->objectBody, &manifold);
    if (manifold.count > 0)
    {
        if (manifold.n.y == 1)
        {
            enemy->onGround++;
            enemy->vSpeed = 0.0f;
            enemy->Position.y = object->position.y - enemy->Size.y;
        }
        if (manifold.n.y == -1)
        {
            enemy->Position.y = object->position.y + object->size.y - 50.0f;
            enemy->vSpeed = -enemy->vSpeed * .3f;
        }
        if (manifold.n.x == 1)
        {
            enemy->Position.x = object->position.x - enemy->Size.x + 50.0f;
            enemy->hSpeed = -enemy->hSpeed * 0.01f;
        }
        if (manifold.n.x == -1)
        {
            enemy->Position.x = object->position.x + object->size.x - 50.0f;
            enemy->hSpeed = -enemy->hSpeed * 0.01f;
        }
    }
};
//constructor & destructor
Game::Game(const unsigned int SCR_WIDTH, const unsigned int SCR_HEIGHT, const char *name)
    : scrWidth(SCR_WIDTH), scrHeight(SCR_HEIGHT)
{
    //initialise window
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //create window
    window = glfwCreateWindow(scrWidth, scrHeight, name, glfwGetPrimaryMonitor(), NULL);
    glfwMakeContextCurrent(window);

    //set callbacks
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
    }
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO();
    (void)io;
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsClassic();

    // Setup Platform/Renderer bindings
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init((char *)glGetString(GL_NUM_SHADING_LANGUAGE_VERSIONS));
}
Game::~Game()
{
}
//Game state
void Game::init()
{
    //----------testing-----------

    //----------testing-----------
    //create shader object
    Shader shader = ResourceManager::LoadShader("../shaders/shader.vert", "../shaders/shader.frag", nullptr, "shader");
    // Shader shader = ResourceManager::LoadShader("../shaders/shaderBackground.vert", "../shaders/shaderBackground.frag", nullptr, "shaderBackground");
    //perspective projection
    //glm::mat4 projection;
    //projection = glm::perspective(glm::radians(45.0f), 1920.0f/ 1080.0f , 0.0f, 1.0f);
    glm::mat4 projection = glm::ortho(0.0f, scrWidth, scrHeight, 0.0f, -1.0f, 1.0f);
    ResourceManager::GetShader("shader").Use();
    glUniform1i(glGetUniformLocation(ResourceManager::GetShader("shader").ID, "image"), 0);
    glUniformMatrix4fv(glGetUniformLocation(ResourceManager::GetShader("shader").ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

    //ResourceManager::LoadTexture("art/dirt.png", "dirtTexture");

    renderer = new SpriteRenderer(shader);
    player = new Player(glm::vec2(900.0f, 1500.0f), glm::vec2(192.0f, 192.0f));
    enemy = new Enemy(glm::vec2(100.0f, 2100.0f), glm::vec2(192.0f / 2, 192.0f / 2));
    world = new World("../terrainFile/terrain.txt", glm::vec2(32.0f, 32.0f));
    background = new Background();
    Texture Sprite = ResourceManager::LoadTexture("../art/background/Clouds10x10.png", "cloudSheet");
    this->objects.push_back(new Object(Sprite, glm::vec2(0.0f, 2000.0f), glm::vec2(128.0f, 128.0f), 0));
    this->objects.push_back(new Object(Sprite, glm::vec2(128.0f, 2000.0f), glm::vec2(128.0f, 128.0f), 1));
    //dirtBlock = new Object(glm::vec2(71-0.0-710, 700.0), glm::vec2(1600.0, 512.0));
    //dirtBlock1 = new Object(glm::vec2(1210.0-100.0, 200.0+250.0-300), glm::vec2(32.0 , 32.0));
}
//--------imgui--------
void Game::renderImgui()
{
    ImGui_ImplOpenGL3_NewFrame();

    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    {
        ImGui::Begin("babyboigame");
        ImGui::Text("Very nice game info window hmhmhmh tetyeysyessss");
        ImGui::SliderFloat("Height", &player->Position.y, 0.0f, 4000.0f); // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("Height", &player->Position.x, 0.0f, 4000.0f); // Edit 1 float using a slider from 0.0f to 1.0f

        ImGui::SliderFloat("MaxGrav", &player->maxGravity, 0.0f, 4000.0f);                     // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("Grav", &player->gravity, 0.0f, 2000.0f);                           // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("Leg boi", &player->jumpStrength, 0.0f, -3000.0f);                  // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("maxWalki", &player->maxWalkingSpeed, 0.0f, 10000.0f);              // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("maxriunnkmmm", &player->maxRunningSpeed, 0.0f, 10000.0f);          // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("walkaccilatraion oui", &player->hAcceleration, 0.0f, 10000.0f);    // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::SliderFloat("walkdeaccilatraion oui", &player->hDeacceleration, 0.0f, 5000.0f); // Edit 1 float using a slider from 0.0f to 1.0f

        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
        ImGui::End();
    }

    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
//Gameloop
void Game::handleEvents(float dt)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
    {
        if (enemy->vSpeed > enemy->maxSpeed)
        {
            enemy->vSpeed = enemy->maxSpeed;
        }
        else
        {
            enemy->vSpeed += 1.0f * enemy->hAcceleration * dt;
        }
    }
    else if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    {
        if (enemy->vSpeed < -enemy->maxSpeed)
        {
            enemy->vSpeed = -enemy->maxSpeed;
        }
        else
        {
            enemy->vSpeed -= 1.0f * enemy->hAcceleration * dt;
        }
    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
    {
        if (player->hSpeed > player->maxSpeed)
        {
            player->hSpeed = player->maxSpeed;
        }
        else
        {
            player->hSpeed += 1.0f * player->hAcceleration * dt;
        }
        player->direction = 1;

        if (enemy->hSpeed > enemy->maxSpeed)
        {
            enemy->hSpeed = enemy->maxSpeed;
        }
        else
        {
            enemy->hSpeed += 1.0f * enemy->hAcceleration * dt;
        }
        enemy->direction = 1;
    }
    else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
    {
        if (player->hSpeed < -player->maxSpeed)
        {
            player->hSpeed = -player->maxSpeed;
        }
        else
        {
            player->hSpeed -= 1.0f * player->hAcceleration * dt;
        }
        player->direction = -1;

        if (enemy->hSpeed < -enemy->maxSpeed)
        {
            enemy->hSpeed = -enemy->maxSpeed;
        }
        else
        {
            enemy->hSpeed -= 1.0f * enemy->hAcceleration * dt;
        }
        enemy->direction = -1;
    }
    else
    {
        if (player->hSpeed > -5.0f && player->hSpeed < 5.0f)
        {
            player->hSpeed = 0.0f;
            player->state = player->IDLE;
        }
        else if (player->hSpeed > 0.0f)
        {
            player->hSpeed -= 1.0f * player->hDeacceleration * dt;
        }
        else if (player->hSpeed < 0.0f)
        {
            player->hSpeed += 1.0f * player->hDeacceleration * dt;
        }
    }
    if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
    {
        player->maxSpeed = player->maxRunningSpeed;
    }
    else
    {
        player->maxSpeed = player->maxWalkingSpeed;
    }
    if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && player->onGround)
    {
        player->onGround = 0;
        player->vSpeed = player->jumpStrength;
    }
    if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && player->onGround)
    {
        player->onGround = 0;
        player->vSpeed = player->jumpStrength;
    }
    this->x += dt;
    /*
    if (this->player->Position.x > this->enemy->Position.x) {
        if (enemy->hSpeed > enemy->maxSpeed) { enemy->hSpeed = enemy->maxSpeed; }
        else { enemy->hSpeed += 1.0f * enemy->hAcceleration * dt; }
        enemy->direction = 1;
    }
    else {
        if (enemy->hSpeed < -enemy->maxSpeed) { enemy->hSpeed = -enemy->maxSpeed; }
        else { enemy->hSpeed -= 1.0f * enemy->hAcceleration * dt; }
        enemy->direction = -1;
    }*/

    player->calcPos(dt);
    enemy->calcPos(dt);

    for (int i = 0; i < this->world->objects.size(); i++)
    {
        calcCollision(this->player, this->world->objects[i]);
        calcCollisionE(this->enemy, this->world->objects[i]);
    } /*
    if (enemy->hSpeed == 0.0f) {
        enemy->onGround = 0;
        enemy->vSpeed = enemy->jumpStrength;
    }*/

    player->animSprite(dt);
    enemy->animSprite(dt);
}

void Game::render(float dt)
{
    //view matrix
    glm::mat4 view;
    const float backgroundHorizontalDelay = 5.0f;
    const float backgroundVerticalDelay = 20.0f;
    view = glm::lookAt(glm::vec3((player->Position.x + player->Size.x / 2 - scrWidth / 2) / backgroundHorizontalDelay, (player->Position.y + player->Size.y / 2 - scrHeight / 2)/backgroundVerticalDelay, 1.0f),
                       glm::vec3((player->Position.x + player->Size.x / 2 - scrWidth / 2) / backgroundHorizontalDelay, (player->Position.y + player->Size.y / 2 - scrHeight / 2)/backgroundVerticalDelay, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

    glUniformMatrix4fv(glGetUniformLocation(ResourceManager::GetShader("shader").ID, "view"), 1, GL_FALSE, glm::value_ptr(view));

    //draw objects
    background->drawBackground(*renderer, dt);

    view = glm::lookAt(glm::vec3(player->Position.x + player->Size.x / 2 - scrWidth / 2, player->Position.y + player->Size.y / 2 - scrHeight / 2 - 250.0f, 1.0f), glm::vec3(player->Position.x + player->Size.x / 2 - scrWidth / 2, player->Position.y + player->Size.y / 2 - scrHeight / 2 - 250.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

    glUniformMatrix4fv(glGetUniformLocation(ResourceManager::GetShader("shader").ID, "view"), 1, GL_FALSE, glm::value_ptr(view));

    this->player->drawPlayer(*renderer);
    //this->enemy->drawEnemy(*renderer);
    // this->objects[0]->drawObject(*renderer);
    // this->objects[1]->drawObject(*renderer);

    ResourceManager::GetShader("shader").SetMatrix4("model", glm::mat4(1.0f));
    this->world->drawWorld(*renderer);
}

//------CALLBACKS---------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    glViewport(0, 0, width, height);
}

//---------getset---------
GLFWwindow *Game::getWindow()
{
    return window;
}