#include "Triangle.h"
#include "Ray.h"
#include <Eigen/Geometry> // hint

bool Triangle::intersect(
    const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const
{
    /* Based on
     * Fundamentals in Computer Graphics - Fourth Edition, Chapter 4.4.2. Ray-Triangle Intersection.
     * Steve Marschner & Peter Shirley
     */

    Eigen::Vector3d a = std::get<0>(corners);
    Eigen::Vector3d b = std::get<1>(corners);
    Eigen::Vector3d c = std::get<2>(corners);
    Eigen::Vector3d e = ray.origin;
    Eigen::Vector3d d = ray.direction;

    /* Using Cramer's rule. */

    /* Calculate the determinant of A. */
    Eigen::Matrix3d A;
    A.col(0) = a - b;
    A.col(1) = a - c;
    A.col(2) = d;
    double determinant_A = A.determinant();

    Eigen::Matrix3d t_matrix;
    t_matrix.col(0) = a - b;
    t_matrix.col(1) = a - c;
    t_matrix.col(2) = a - e;
    t = t_matrix.determinant() / determinant_A;

    if (t < min_t) return false;

    Eigen::Matrix3d gamma_matrix;
    gamma_matrix.col(0) = a - b;
    gamma_matrix.col(1) = a - e;
    gamma_matrix.col(2) = d;
    double gamma = gamma_matrix.determinant() / determinant_A;

    if (gamma < 0 || gamma > 1) return false;

    Eigen::Matrix3d beta_matrix;
    beta_matrix.col(0) = a - e;
    beta_matrix.col(1) = a - c;
    beta_matrix.col(2) = d;
    double beta = beta_matrix.determinant() / determinant_A;

    if (beta < 0 || beta + gamma > 1) return false;

    /* Calculate the surface normal vector using the cross product of two edges.
     * See https://en.wikipedia.org/wiki/Cross_product#Computational_geometry */
    Eigen::Vector3d edge1 = a - b;
    Eigen::Vector3d edge2 = a - c;
    n = edge1.cross(edge2).normalized(); /* Don't forget to normalize the vector. */

    return true;
}