#include "triangle.hpp"
#include "vector.hpp"
#include <unordered_set>
#include <iostream>

using namespace visual_engine::detail;
using namespace visual_engine::detail::linalg;

void triangle_::console_dump() const {
    std::cout << "----triangle----" << std::endl;
    std::cout << "a=" << a_ << ",b=" << b_ << ",c=" << c_ << std::endl;
    norm_.console_dump();
}

std::optional<float> triangle_::line_intersect_with_triag_plane_param(const std::vector<point> &p_buff, const point &p, const vector &v) const {
    auto v_n_proj = v.scal_product(norm_);
    if (equal(v_n_proj, 0)) return std::nullopt;
    vector r0(p_buff[a_]), rc(p);
    return (r0-rc).scal_product(norm_) / v_n_proj;
}

std::optional<float> triangle_::line_intersect_triag_param(const std::vector<point> &p_buff, const point &p, const vector &v) const {
    std::optional<float> ret = line_intersect_with_triag_plane_param(p_buff, p, v);
    if (!ret.has_value()) return ret;
    if (is_in_triag(p_buff, p, v, ret.value())) return ret;
    return std::nullopt;
}

bool triangle_::is_in_triag(const std::vector<point> &p_buff, const point &p, const vector &v, coord_t t) const {
    vector r = vector{p} + v * t;
    vector va(p_buff[a_]), vb(p_buff[b_]), vc(p_buff[c_]);
    vector v1{(vb-va).product(r-va)},
           v2{(vc-vb).product(r-vb)},
           v3{(va-vc).product(r-vc)};

    return all_by_one_side(v1, v2, v3);
}

bool triangle_::all_by_one_side(const vector& v1, const vector& v2, const vector& v3) const {
    coord_t n1 = v1.scal_product(norm_),
            n2 = v2.scal_product(norm_),
            n3 = v3.scal_product(norm_);
    int zeros = 0;
    if (equal(n1, 0)) {++zeros; n1 = 0;}
    if (equal(n2, 0)) {++zeros; n2 = 0;}
    if (equal(n3, 0)) {++zeros; n3 = 0;}

    if (zeros == 2) return true;
    if (zeros == 3) return false;

    if ((n1 >= 0 && n2 >= 0 && n3 >= 0) ||
        (n1 <= 0 && n2 <= 0 && n3 <= 0)) return true;
    return false;
}

void triangle_::norm_rotate(const vector &v) { norm_.rotate(v); }

// std::optional<double> line_intersect_with_triag_plane_param(const std::vector<point> &p_buff, const triangle_ &tr, const point &p, const vector &v) {
//     auto v_n_proj = v.scal_product(tr.get_norm());
//     if (equal(v_n_proj, 0)) return std::nullopt;
//     vector r0(p_buff[tr.get_point()]), rc(p);
//     return (r0-rc).scal_product(tr.get_norm()) / v_n_proj;
// }

// std::optional<double> linalg::line_intersect_triag_param(const std::vector<point> &p_buff, const triangle_ &tr, const point &p, const vector &v) {
//     std::optional<double> ret = line_intersect_with_triag_plane_param(p_buff, tr, p, v);
//     if (!ret.has_value())               return ret;
//     if (is_in_triag(p, v, ret.value())) return ret;
//     return std::nullopt;
// }
