#include "lab_m1/tema1/tema1.h"
#include "lab_m1/tema1/transform2D.h"
#include "lab_m1/lab3/object2D.h"

using namespace std;
using namespace m1;

Tema1::Tema1() {
}

Tema1::~Tema1() {
}

std::vector<glm::vec2> terrain_points;
float freq = 0.02f;
float squareSide = 1;
int activeTank;
float TerrainFunction(float t) {
    t *= freq;
    return 5 * sin(t) + 10 * sin(0.5f * t) + 0.5f * sin(0.5f * t);

}

struct Projectile {
    glm::vec2 position;
    glm::vec2 velocity;
    float angle;
};
std::vector<Projectile> activeProjectiles;

void Tema1::Init() {
    glm::ivec2 resolution = window->GetResolution();
    auto camera = GetSceneCamera();
    camera->SetOrthographic(0, (float)resolution.x, 0, (float)resolution.y, 0.01f, 400);
    camera->SetPosition(glm::vec3(0, 0, 50));
    camera->SetRotation(glm::vec3(0, 0, 0));
    camera->Update();
    GetCameraInput()->SetActive(false);

    glm::vec3 corner = glm::vec3(0, 0, 0);
    Mesh* square = object2D::CreateSquare("square", corner, squareSide, glm::vec3(0.2f, 0.7f, 0.3f), true);
    AddMeshToList(square);

    Mesh* baseTrapezoid1 = object2D::CreateTrapezoid("baseTrapezoid1", 110, 20, 100, glm::vec3(0.6f, 0.4f, 0.2f), true);
    AddMeshToList(baseTrapezoid1);
    Mesh* baseTrapezoid2 = object2D::CreateTrapezoid("baseTrapezoid2", 80, 17, 100, glm::vec3(0.2f, 0.2f, 0.6f), true);
    AddMeshToList(baseTrapezoid2);

    Mesh* circle = object2D::CreateCircle("circle", corner, 21, glm::vec3(0.6f, 0.4f, 0.2f), true);
    AddMeshToList(circle);

    Mesh* rectangle = object2D::CreateRectangle("rectangle", corner, 6.0f, 60.0f, glm::vec3(0.4f, 0.4f, 0.4f), true);
    AddMeshToList(rectangle);

	Mesh* projectile = object2D::CreateCircle("projectile", corner, 7, glm::vec3(0.4f, 0.4f, 0.4f), true);
	AddMeshToList(projectile);

	Mesh* trajectory = object2D::CreateSquare("trajectory", corner, 2, glm::vec3(0.8f, 0.8f, 0.8f), true);
	AddMeshToList(trajectory);

    tank1X = 100;
    tank1Y = 100;
	turret1Angle = 0;

    tank2X = 1100;
    tank2Y = 100;
	turret2Angle = 0;

	projectileActive = false;
	gravity = 300.0f;

    int num_points = resolution.x;
    terrain_points.reserve(num_points);

    for (int i = 0; i < num_points; i++) {
        float x = i;
        float y = 8 * TerrainFunction(i) + 300;
        terrain_points.emplace_back(x, y);
    }
}

void Tema1::RenderTank(float x, float y, float rotationAngle, float turretAngle) {
    glm::mat3 modelMatrix;

    // Mută centrul bazei tancului în punctul de pe teren
    modelMatrix = glm::mat3(1);
    modelMatrix *= transform2D::Translate(x, y);
    modelMatrix *= transform2D::Rotate(rotationAngle);

    // Trapezul de baza
    glm::mat3 baseMatrix = modelMatrix;
    baseMatrix *= transform2D::Translate(0, 20);
    RenderMesh2D(meshes["baseTrapezoid1"], shaders["VertexColor"], baseMatrix);

    baseMatrix = modelMatrix;
    baseMatrix *= transform2D::Translate(0, 6);
    baseMatrix *= transform2D::Scale(-1, -1);
    RenderMesh2D(meshes["baseTrapezoid2"], shaders["VertexColor"], baseMatrix);

    // Turretul circular superior
    glm::mat3 turretMatrix = modelMatrix;
    turretMatrix *= transform2D::Translate(0, 28.0f);
    RenderMesh2D(meshes["circle"], shaders["VertexColor"], turretMatrix);

    //  țeava tancului
    glm::mat3 barrelMatrix = modelMatrix;
    barrelMatrix *= transform2D::Translate(0, 33.0f);
    float barrelInclineAngle = glm::radians(-45.0f);
    barrelMatrix *= transform2D::Rotate(barrelInclineAngle);
	barrelMatrix *= transform2D::Rotate(turretAngle);
    RenderMesh2D(meshes["rectangle"], shaders["VertexColor"], barrelMatrix);


    glm::vec3 tipPosition = barrelMatrix * glm::vec3(1, 1, 1);
    turretTipX = tipPosition.x;
    turretTipY = tipPosition.y;
}

float CalculateSlopeAngle(glm::vec2 A, glm::vec2 B) {
    glm::vec2 V = B - A;
    return atan2(V.y, V.x);
}

float CalculateTankY(float tankX, glm::vec2 A, glm::vec2 B) {
    float t = (tankX - A.x) / (B.x - A.x);
    return A.y + t * (B.y - A.y);
}

bool CheckProjectileCollision(const glm::vec2& projectilePos, const std::vector<glm::vec2>& terrain, float collisionThreshold) {
    for (size_t i = 1; i < terrain.size(); ++i) {
        glm::vec2 P1 = terrain[i - 1];
        glm::vec2 P2 = terrain[i];

        if (projectilePos.x >= P1.x && projectilePos.x <= P2.x) {
            float t = (projectilePos.x - P1.x) / (P2.x - P1.x);
            float terrainY = P1.y * (1 - t) + P2.y * t;

            if (abs(projectilePos.y - terrainY) < collisionThreshold) {
                return true;
            }
        }
    }
    return false;
}
std::vector<glm::vec2> CalculateProjectilePath(glm::vec2 startPosition, float angle, float initialSpeed, float gravity, int numSegments, const std::vector<glm::vec2>& terrain) {
    std::vector<glm::vec2> trajectory;
    float timeStep = 0.1f; // Intervalul de timp dintre fiecare punct

    for (int i = 0; i < numSegments; i++) {
        float t = i * timeStep;
        float x = startPosition.x + initialSpeed * cos(angle) * t;
        float y = startPosition.y + initialSpeed * sin(angle) * t - 0.5f * gravity * t * t;
        trajectory.push_back(glm::vec2(x, y));
		if (y < 0) {
			break;
		}
      
    }

    return trajectory;
}

void Tema1::RenderTrajectory(glm::vec2 startPosition, float angle, float initialSpeed, float gravity, const std::vector<glm::vec2>& terrain) {
    int numSegments = 30;
    std::vector<glm::vec2> trajectory = CalculateProjectilePath(startPosition, angle, initialSpeed, gravity, numSegments, terrain);
   
    for (size_t i = 1; i < trajectory.size(); ++i) {
        glm::vec2 p1 = trajectory[i - 1];
        glm::vec2 p2 = trajectory[i];

		modelMatrix = glm::mat3(1);
        modelMatrix *= transform2D::Translate(p1.x, p1.y);

        float segmentLength = glm::length(p2 - p1);
        float segmentAngle = atan2(p2.y - p1.y, p2.x - p1.x);

        modelMatrix *= transform2D::Rotate(segmentAngle);
        modelMatrix *= transform2D::Scale(segmentLength, 1.0f);

        RenderMesh2D(meshes["trajectory"], shaders["VertexColor"], modelMatrix);
    }
}


void DeformTerrain(std::vector<glm::vec2>& terrain, const glm::vec2& collisionPoint, float radius, float depth) {
    for (auto& point : terrain) {
        float distance = glm::distance(point, collisionPoint);
        if (distance < radius) {
            point.y -= depth * (1.0f - distance / radius);  // Coborâre proporțională cu distanța față de centrul coliziunii
        }
    }
}

void ApplyTerrainSlide(std::vector<glm::vec2>& terrain, float heightThreshold, float slideAmount, float deltaTime) {
    for (size_t i = 1; i < terrain.size(); ++i) {
        float d = abs(terrain[i].y - terrain[i - 1].y);
        if (d > heightThreshold) {
            if (terrain[i].y > terrain[i - 1].y) {
                terrain[i].y -= slideAmount * deltaTime;
                terrain[i - 1].y += slideAmount * deltaTime;
            }
            else {
                terrain[i].y += slideAmount * deltaTime;
                terrain[i - 1].y -= slideAmount * deltaTime;
            }
        }
    }
}

void Tema1::Update(float deltaTimeSeconds) {
    glm::ivec2 resolution = window->GetResolution();

    // First render the terrain to ensure it's drawn behind everything else
    for (int i = 1; i < terrain_points.size(); i++) {
        glm::vec2 prev = terrain_points[i - 1];
        glm::vec2 curr = terrain_points[i];

        modelMatrix = glm::mat3(1);
        float width = curr.x - prev.x;
        float height = std::max(curr.y, prev.y);

        modelMatrix *= transform2D::Translate(prev.x, prev.y);
        float sheerFactor = (curr.y - prev.y) / (curr.x - prev.x);
        modelMatrix *= transform2D::Sheer(sheerFactor);
        modelMatrix *= transform2D::Scale(width, -height);

        RenderMesh2D(meshes["square"], shaders["VertexColor"], modelMatrix);
    }

    // Now render Tank 1
    for (int i = 1; i < terrain_points.size(); i++) {
        glm::vec2 prev = terrain_points[i - 1];
        glm::vec2 curr = terrain_points[i];

        if (tank1X >= prev.x && tank1X <= curr.x) {
            glm::vec2 A = prev;
            glm::vec2 B = curr;
            tank1Y = CalculateTankY(tank1X, A, B);
            rotationAngle1 = CalculateSlopeAngle(A, B);
            RenderTank(tank1X, tank1Y, rotationAngle1, turret1Angle);
            turretTip1X = turretTipX;
            turretTip1Y = turretTipY;

            float angle = turret1Angle + rotationAngle1 - glm::radians(-45.0f);
            RenderTrajectory(glm::vec2(turretTip1X, turretTip1Y), angle, 400.0f, gravity, terrain_points);
        }

        if (tank2X >= prev.x && tank2X <= curr.x) {
            glm::vec2 A = prev;
            glm::vec2 B = curr;
            tank2Y = CalculateTankY(tank2X, A, B);
            rotationAngle2 = CalculateSlopeAngle(A, B);
            RenderTank(tank2X, tank2Y, rotationAngle2, turret2Angle);
            turretTip2X = turretTipX;
            turretTip2Y = turretTipY;

            float angle = turret2Angle + rotationAngle2 - glm::radians(-45.0f);
            RenderTrajectory(glm::vec2(turretTip2X, turretTip2Y), angle, 400.0f, gravity, terrain_points);
        }
    }

    // Render active projectiles
    for (auto projectile = activeProjectiles.begin(); projectile != activeProjectiles.end();) {
        projectile->position += projectile->velocity * deltaTimeSeconds;
        projectile->velocity.y -= gravity * deltaTimeSeconds;  // Apply gravity

        glm::mat3 modelMatrix = glm::mat3(1);
        modelMatrix *= transform2D::Translate(projectile->position.x, projectile->position.y);
        modelMatrix *= transform2D::Rotate(projectile->angle);
        RenderMesh2D(meshes["projectile"], shaders["VertexColor"], modelMatrix);

        // Remove the projectile if it goes off-screen
        if (CheckProjectileCollision(projectile->position, terrain_points, 10.0f)) {
            DeformTerrain(terrain_points, projectile->position, 100.0f, 30.0f);
            projectile = activeProjectiles.erase(projectile);
        }
        else {
            projectile++;
        }
    }

    // Apply terrain slide if needed
    // ApplyTerrainSlide(terrain_points, 10.0f, 5.0f, deltaTimeSeconds);
}


void Tema1::FrameStart()
{
    glClearColor(0.52f, 0.81f, 0.92f, 1);  // Light blue background
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glm::ivec2 resolution = window->GetResolution();
    glViewport(0, 0, resolution.x, resolution.y);
}

void Tema1::FrameEnd()
{
}

void Tema1::OnInputUpdate(float deltaTime, int mods)
{
    if (window->KeyHold(GLFW_KEY_A)) {
        tank1X -= 200 * deltaTime;
    }

    if (window->KeyHold(GLFW_KEY_D)) {
        tank1X += 200 * deltaTime;
    }

	if (window->KeyHold(GLFW_KEY_LEFT)) {
		tank2X -= 200 * deltaTime;
	}

	if (window->KeyHold(GLFW_KEY_RIGHT)) {
		tank2X += 200 * deltaTime;
	}

    if (window->KeyHold(GLFW_KEY_W) && turret1Angle <= glm::pi<float>() / 1.5) {
        turret1Angle += 1.5f * deltaTime;
    }

    if (window->KeyHold(GLFW_KEY_S) && turret1Angle > -0.5) {
        turret1Angle -= 1.5f * deltaTime;
    }

    if (window->KeyHold(GLFW_KEY_UP) && turret2Angle < glm::pi<float>() / 1.5) {
        turret2Angle += 1.5f * deltaTime;
    }

    if (window->KeyHold(GLFW_KEY_DOWN) && turret2Angle > -0.5) {
        turret2Angle -= 1.5f * deltaTime;
    }
}


void Tema1::OnKeyPress(int key, int mods)
{
    // Add key press event
    if (window->KeyHold(GLFW_KEY_SPACE)) {
        Projectile newProjectile;
        float angle = turret1Angle + rotationAngle1 - glm::radians(-45.0f);
        newProjectile.position = glm::vec2(turretTip1X, turretTip1Y);
        newProjectile.velocity = glm::vec2(cos(angle), sin(angle)) * 400.0f;
        newProjectile.angle = angle;
        activeProjectiles.push_back(newProjectile);
    }

    if (window->KeyHold(GLFW_KEY_ENTER)) {
		Projectile newProjectile;
        float angle = turret2Angle + rotationAngle2 - glm::radians(-45.0f);
        newProjectile.position = glm::vec2(turretTip2X, turretTip2Y);
		newProjectile.velocity = glm::vec2(cos(angle), sin(angle)) * 400.0f;
		newProjectile.angle = angle;
		activeProjectiles.push_back(newProjectile);
    }

}


void Tema1::OnKeyRelease(int key, int mods)
{
    // Add key release event
}


void Tema1::OnMouseMove(int mouseX, int mouseY, int deltaX, int deltaY)
{
    // Add mouse move event
}


void Tema1::OnMouseBtnPress(int mouseX, int mouseY, int button, int mods)
{
    // Add mouse button press event
}


void Tema1::OnMouseBtnRelease(int mouseX, int mouseY, int button, int mods)
{
    // Add mouse button release event
}


void Tema1::OnMouseScroll(int mouseX, int mouseY, int offsetX, int offsetY)
{
}


void Tema1::OnWindowResize(int width, int height)
{
}De ce cand tank1 este in pozitia initiala mi se genereaza 2 traiectorii pentru el?