#pragma once

#include "utils/Perlin.h"
#include "render/Renderer.h"
#include <vector>
#include <array>
#include <string>
#include <map>
#include <set>

namespace ZL {


    using VertexID = std::string;
    using V2TMap = std::map<VertexID, std::vector<int>>;

    struct Vector3fComparator {
        bool operator()(const Eigen::Vector3f& a, const Eigen::Vector3f& b) const {
            // Лексикографическое сравнение (x, затем y, затем z)
            if (a.x() != b.x()) return a.x() < b.x();
            if (a.y() != b.y()) return a.y() < b.y();
            return a.z() < b.z();
        }
    };

    VertexID generateEdgeID(const VertexID& id1, const VertexID& id2);

    constexpr static int MAX_LOD_LEVELS = 6;

    struct Triangle
    {
        std::array<Vector3f, 3> data;
        std::array<VertexID, 3> ids;

        Triangle()
        {
        }

        Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
            : data{ p1, p2, p3 }
        {
        }

        Triangle(std::array<Vector3f, 3> idata, std::array<VertexID, 3> iids)
            : data{ idata }
            , ids{ iids }
        {
        }
    };

    struct LodLevel
    {
        std::vector<Triangle> triangles;
        VertexDataStruct vertexData;
        std::vector<VertexID> VertexIDs;
        V2TMap v2tMap;

        void Scale(float s)
        {
			vertexData.Scale(s);
			for (auto& t : triangles) {
                for (int i = 0; i < 3; i++) {
                    t.data[i] = t.data[i] * s;
                }
            }
        }
        void Move(Vector3f pos)
        {
            vertexData.Move(pos);
            for (auto& t : triangles) {
                for (int i = 0; i < 3; i++) {
                    t.data[i] = t.data[i] + pos;
                }
            }
		}
    };

    struct PlanetCampObject
    {
        Vector3f position;
        Matrix3f rotation;

        std::array<Vector3f, 5> platformPos = {
            Vector3f{ 0.f, 0.f,-38.f },
            Vector3f{ 20.f, 0.f,-18.f },
            Vector3f{ 20.f, 0.f,-58.f },
            Vector3f{ -20.f, 0.f,-58.f },
            Vector3f{ -20.f, 0.f,-18.f }
        };
    };

    class PlanetData {
    public:
        static const float PLANET_RADIUS;
        static const Vector3f PLANET_CENTER_OFFSET;

    private:
        PerlinNoise perlin;
        PerlinNoise colorPerlin;

        std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLods;
        LodLevel planetAtmosphereLod;

        //int currentLod; // Логический текущий уровень детализации

        //std::map<Vector3f, VertexID, Vector3fComparator> initialVertexMap;

        // Внутренние методы генерации
        std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles, float noiseCoeff);
        LodLevel createLodLevel(const std::vector<Triangle>& triangles);
        void recalculateMeshAttributes(LodLevel& lod);
        LodLevel generateSphere(int subdivisions, float noiseCoeff);
    public:
        PlanetData();

        void init();
        const LodLevel& getLodLevel() const;
        const LodLevel& getAtmosphereLod() const;

        // Логика
        std::pair<float, float> calculateZRange(float distanceToSurface);
        float distanceToPlanetSurfaceFast(const Vector3f& viewerPosition);

        // Возвращает индексы треугольников, видимых камерой
        std::vector<int> getBestTriangleUnderCamera(const Vector3f& viewerPosition);
        std::vector<int> getTrianglesUnderCameraNew2(const Vector3f& viewerPosition);

        void applySphericalRelaxation(LodLevel& lod, int iterations);

        std::vector<PlanetCampObject> campObjects;

    };

} // namespace ZL