//
// Created by Bram on 10/11/2024.
//

#include <cassert>
#include "HitTest.h"

float min(float a, float b, float c) {
    return std::min(a, std::min(b, c));
}

float max(float a, float b, float c) {
    return std::max(a, std::max(b, c));
}

float CrossZ(const Vector3& p0, const Vector3& p1, const Vector3& point)
{
    return (point.x - p0.x) * (p1.y - p0.y)
           - (point.y - p0.y) * (p1.x - p0.x);
}


HitTest::HitTestSample HitTest::Triangle(const Vector3 &pos, const std::vector<Vertex> &vertices) {
    float minX = min(vertices[0].position.x, vertices[1].position.x, vertices[2].position.x);
    float maxX = max(vertices[0].position.x, vertices[1].position.x, vertices[2].position.x);

    float minY = min(vertices[0].position.y, vertices[1].position.y, vertices[2].position.y);
    float maxY = max(vertices[0].position.y, vertices[1].position.y, vertices[2].position.y);

    if (pos.x < minX || pos.x > maxX || pos.y < minY || pos.y > maxY) {
        return HitTestSample{ColorRGB{1, 0, 0}, 0};
    }

    // first edge
    double w0{ CrossZ(vertices[1].position, vertices[2].position, pos) };
    const bool inEdge0 = w0 <= 0;

    double w1{ CrossZ(vertices[2].position, vertices[0].position, pos) };
    const bool inEdge1 = w1 <= 0;

    double w2{ CrossZ(vertices[0].position, vertices[1].position, pos) };
    const bool inEdge2 = w2 <= 0;

    if(!inEdge0 || !inEdge1 || !inEdge2){
        return {ColorRGB{0, 0, 1}, 0};
    }

    const double totalWeight{ w0 + w1 + w2 };
    w0 /= totalWeight;
    w1 /= totalWeight;
    w2 /= totalWeight;


    assert(std::abs(w0 + w1 + w2 - 1) < 0.0001);

    float v0z = vertices[0].position.z;
    float v1z = vertices[1].position.z;
    float v2z = vertices[2].position.z;

    float depth = w0 * v0z + w1 * v1z + w2 * v2z;

    if(depth < 0 || depth > 1) {
        return HitTestSample{ColorRGB{0, 0, 0}, 0};
    }

    Vector4 color{};

    color += Vector4(float(w0) * Vector3(vertices[0].color.r, vertices[0].color.g, vertices[0].color.b) , 0);
    color += Vector4(float(w1) * Vector3(vertices[1].color.r, vertices[1].color.g, vertices[1].color.b) , 0);
    color += Vector4(float(w2) * Vector3(vertices[2].color.r, vertices[2].color.g, vertices[2].color.b) , 0);


    return HitTestSample{ColorRGB{
            static_cast<float>(color.x),
            static_cast<float>(color.y),
            static_cast<float>(color.z)
        }, depth};
}
