#include <QDebugStateSaver>

#include "triangle.h"

Triangle::Edge::Name rotate_edge_name(Triangle::Edge::Name edge);

Triangle::Triangle() {}

Triangle::Triangle(const Triangle &other) {
    this->vertex_buffer_ = other.vertex_buffer_;
    this->u_ = other.u_;
    this->v_ = other.v_;
    this->w_ = other.w_;
}

Triangle::Triangle(const QVector<Vertex> &vertex_buffer, unsigned int u, unsigned int v, unsigned int w) {
    this->vertex_buffer_ = vertex_buffer;
    this->u_ = u;
    this->v_ = v;
    this->w_ = w;
}

Vertex Triangle::u() const {
    return vertex_buffer_[u_];
}

Vertex Triangle::v() const {
    return vertex_buffer_[v_];
}

Vertex Triangle::w() const {
    return vertex_buffer_[w_];
}

unsigned int Triangle::u_idx() const {
    return u_;
}

unsigned int Triangle::v_idx() const {
    return v_;
}

unsigned int Triangle::w_idx() const {
    return w_;
}

bool Triangle::get_shared_edge(Triangle other, Edge &edge_a, Edge &edge_b) const {
    if (get_shared_edge_(other, edge_a, edge_b)) {
        return true;
    }

    other.rotate_indices();
    if (get_shared_edge_(other, edge_a, edge_b)) {
        edge_b.name = rotate_edge_name(edge_b.name);
        return true;
    }

    other.rotate_indices();
    if (get_shared_edge_(other, edge_a, edge_b)) {
        edge_b.name = rotate_edge_name(rotate_edge_name(edge_b.name));
        return true;
    }

    return false;
}

bool Triangle::get_shared_edge_(const Triangle &other, Edge &edge_a, Edge &edge_b) const {
    if (u().samePos(other.u())) {
        if (v().samePos(other.w())) {
            edge_a.name = Triangle::Edge::Name::uv;
            edge_a.a = u_idx();
            edge_a.b = v_idx();
            edge_a.c = w_idx();

            edge_b.name = Triangle::Edge::Name::wu;
            edge_b.a = other.w_idx();
            edge_b.b = other.u_idx();
            edge_b.c = other.v_idx();

            return true;
        } else if (w().samePos(other.v())) {
            edge_a.name = Triangle::Edge::Name::wu;
            edge_a.a = w_idx();
            edge_a.b = u_idx();
            edge_a.c = v_idx();

            edge_b.name = Triangle::Edge::Name::uv;
            edge_b.a = other.u_idx();
            edge_b.b = other.v_idx();
            edge_b.c = other.w_idx();

            return true;
        }
    } else if (v().samePos(other.u()) && w().samePos(other.w())) {
        edge_a.name = Triangle::Edge::Name::vw;
        edge_a.a = v_idx();
        edge_a.b = w_idx();
        edge_a.c = u_idx();

        edge_b.name = Triangle::Edge::Name::wu;
        edge_b.a = other.w_idx();
        edge_b.b = other.u_idx();
        edge_b.c = other.v_idx();

        return true;
    }

    return false;
}

Triangle &Triangle::operator=(const Triangle &other) {
    this->vertex_buffer_ = other.vertex_buffer_;
    this->u_ = other.u_;
    this->v_ = other.v_;
    this->w_ = other.w_;
    return *this;
}

bool Triangle::operator==(const Triangle &other) const {
    return u_ == other.u_ && v_ == other.v_ && w_ == other.w_;
}

bool Triangle::operator<(const Triangle &other) const {
    if (u_ < other.u_) {
        return true;
    } else if (u_ > other.u_) {
        return false;
    }

    if (v_ < other.v_) {
        return true;
    } else if (v_ > other.v_) {
        return false;
    }

    if (w_ < other.w_) {
        return true;
    } else if (w_ > other.w_) {
        return false;
    }

    return false;
}

void Triangle::rotate_indices() {
    unsigned int a = u_;
    u_ = v_;
    v_ = w_;
    w_ = a;
}

Triangle::Edge::Name rotate_edge_name(Triangle::Edge::Name edge) {
    switch (edge) {
        case Triangle::Edge::Name::uv:
            return Triangle::Edge::Name::vw;
        case Triangle::Edge::Name::vw:
            return Triangle::Edge::Name::wu;
        case Triangle::Edge::Name::wu:
            return Triangle::Edge::Name::uv;
        default:
            qDebug() << "Default case should be unreachable";
            return Triangle::Edge::Name::uv;
    }
}

QDebug operator<<(QDebug d, const Triangle &triangle) {
    QDebugStateSaver saver(d);
    d.nospace() << "Triangle(" << triangle.u_idx() << ", " << triangle.v_idx() << ", " << triangle.w_idx() << "; " << triangle.u().pos << ", " << triangle.v().pos << ", " << triangle.w().pos << ")";
    return d;
}
