#include "pch.h"
#include "VertexList.h"
#include <sstream>
#include <algorithm>
#include <numeric>

namespace Vivid2D::util {

    // --- 构造函数实现 ---

    VertexList::VertexList()
        : name("KongFuZi"), indices() {
        // std::vector 的默认构造函数创建一个空向量
    }

    VertexList::VertexList(const std::string& name)
        : name(name), indices() {
        if (name.empty()) {
            throw std::invalid_argument("Name cannot be empty");
        }
    }

    VertexList::VertexList(const std::string& name, const std::vector<Vertex>& initialVertices, const std::vector<int>& initialIndices)
        : VertexList(name)
    {
        if (!initialVertices.empty()) {
            for (const auto& vertex : initialVertices) {
                this->add(vertex);
            }
        }
        setIndices(initialIndices);
    }

    
    VertexList::VertexList(const VertexList& other)
        : vertices(other.vertices),
        name(other.name),
        indices(other.indices)
    {
    }

    VertexList& VertexList::operator=(const VertexList& other) {
        if (this != &other) {
            vertices = other.vertices;
            name = other.name;
            indices = other.indices;
        }
        return *this;
    }


    // --- 列表管理实现 ---

    void VertexList::add(const Vertex& vertex) {
        Vertex v = vertex;
        v.setIndex(static_cast<int>(this->vertices.size()));
        this->vertices.push_back(v);
    }

    bool VertexList::remove(const Vertex& vertex) {
        auto it = std::find_if(this->vertices.begin(), this->vertices.end(),
            [&vertex](const Vertex& v) { return v == vertex; });

        if (it != this->vertices.end()) {
            int index = std::distance(this->vertices.begin(), it);
            remove(index);
            return true;
        }
        return false;
    }

    Vertex VertexList::remove(int index) {
        if (index < 0 || index >= static_cast<int>(this->vertices.size())) {
            throw std::out_of_range("Index out of bounds: " + std::to_string(index));
        }

        // 1. 重构索引数组
        std::vector<int> newIndices;
        newIndices.reserve(this->indices.size());

        for (size_t i = 0; i < this->indices.size(); i += 3) {
            int i1 = this->indices[i];
            int i2 = this->indices[i + 1];
            int i3 = this->indices[i + 2];

            if (i1 == index || i2 == index || i3 == index) {
                continue;
            }

            if (i1 > index) i1--;
            if (i2 > index) i2--;
            if (i3 > index) i3--;

            newIndices.push_back(i1);
            newIndices.push_back(i2);
            newIndices.push_back(i3);
        }

        this->indices = std::move(newIndices);

        Vertex removedVertex = this->vertices[index];
        this->vertices.erase(this->vertices.begin() + index);

        for (size_t i = index; i < this->vertices.size(); ++i) {
            this->vertices[i].setIndex(static_cast<int>(i));
        }

        return removedVertex;
    }

    const Vertex& VertexList::get(int index) const {
        if (index < 0 || index >= static_cast<int>(this->vertices.size())) {
            throw std::out_of_range("Index out of bounds: " + std::to_string(index));
        }
        return this->vertices.at(index);
    }

    void VertexList::clear() {
        this->vertices.clear();
        this->indices.clear();
    }

    std::vector<Vertex> VertexList::getVertices() const {
        return this->vertices;
    }

    std::vector<Vertex> VertexList::getVertices(VertexTag tag) const {
        std::vector<Vertex> filtered;
        for (const auto& vertex : vertices) {
            if (vertex.getTag() == tag) {
                filtered.push_back(vertex);
            }
        }
        return filtered;
    }

    // --- Getter 和 Setter 实现 ---

    void VertexList::setName(const std::string& name) {
        if (name.empty()) {
            throw std::invalid_argument("Name cannot be empty");
        }
        this->name = name;
    }

    std::vector<int> VertexList::getIndices() const {
        return this->indices;
    }

    std::vector<int> VertexList::getIndices(VertexTag tag) const {
        if (indices.empty()) {
            return {};
        }

        std::unordered_set<int> taggedOriginalIndices;
        for (size_t i = 0; i < this->vertices.size(); ++i) {
            if (this->vertices[i].getTag() == tag) {
                taggedOriginalIndices.insert(static_cast<int>(i));
            }
        }

        if (taggedOriginalIndices.empty()) {
            return {};
        }

        std::vector<int> newIndices;
        newIndices.reserve(this->indices.size());

        for (size_t i = 0; i < this->indices.size(); i += 3) {
            int i1 = this->indices[i];
            int i2 = this->indices[i + 1];
            int i3 = this->indices[i + 2];

            if (taggedOriginalIndices.count(i1) ||
                taggedOriginalIndices.count(i2) ||
                taggedOriginalIndices.count(i3)) {

                newIndices.push_back(i1);
                newIndices.push_back(i2);
                newIndices.push_back(i3);
            }
        }

        return newIndices;
    }

    void VertexList::setIndices(const std::vector<int>& indices) {
        this->indices = indices;
    }

    void VertexList::set(int index, const Vertex& vertex) {
        if (index < 0 || index >= static_cast<int>(this->vertices.size())) {
            throw std::out_of_range("Index out of bounds: " + std::to_string(index));
        }
        Vertex v = vertex;
        v.setIndex(index);
        this->vertices.at(index) = v;
    }


    bool VertexList::operator==(const VertexList& other) const {
        return vertices == other.vertices &&
            name == other.name &&
            indices == other.indices;
    }

    std::ostream& operator<<(std::ostream& os, const VertexList& vertexList) {
        os << "VertexList{"
            << "name='" << vertexList.name << '\''
            << ", vertexCount=" << vertexList.vertices.size()
            << ", indexCount=" << vertexList.indices.size()
            << '}';
        return os;
    }

} // namespace util