#pragma once
#ifndef ELLIPSOID_H__
#define ELLIPSOID_H__

#include "fwd.h"
#include "frameAD.h"
#include "ptr.h"
#include "utils.h"
#include "aabb.h"
#include <cmath>
#include <string>
#include <vector>
#include <array>
#include <cmath>

struct Ellipse {
    Ellipse() { status = 0; }

    Ellipse(const Vector &C1, const Vector &C2, const Vector &C3, 
        const Vector &_O, const Vector &abc, const Float &tau) 
        : O(_O) { init(C1, C2, C3, abc, tau); }

    void init(const Vector &C1, const Vector &C2, const Vector &C3, 
        const Vector &abc, const Float &tau);

    Vector sampleEllipticCurve(const Float &rn, Float &phi, Float &pdf, Float &jacobian);

    inline bool canSample() { return status == 3; }

    // utils
    Float quadDot(const Vector &u, const Vector &v, const Vector &diag) const;
    Float pointToAngle(const Vector &p) const;
    Vector angleToPoint(const Float &angle) const;

    Vector ellipsoidToEllipse(const Vector &p_ellipsoid) const;
    Vector ellipseToEllipsoid(const Vector &p_ellipse) const;
    Vector ellipsoidToCircle(const Vector &p_ellipsoid) const;
    Vector circleToEllipsoid(const Vector &p_circle) const;

    /*
     * 0 - The ellipsoid doesn't intersect with the plane.
     * 1 - The ellipsoid intersects with the plane but the ellipse doesn't 
     * intersect with the triangle.
     * 2 - The ellipse intersects with the triangle but none of elliptic 
     * curves lies inside the triangle. 
     * 3 - There are elliptic curves lying inside the triangle.
     */
    int status; 

    Vector O, TN, UN, N;
    Float theta, m1, m2; 

    Vector dO, dTN, dUN;
    Float dm1, dm2;

    Frame ellipse_frame;

    std::vector<Vector2> curves;
};

struct Ellipsoid {
    Ellipsoid(const Vector &_f1, const Vector &_n1, const Vector &_f2, const Vector &_n2, 
        const Float &_tau) : f1(_f1), f2(_f2), n1(_n1), n2(_n2), tau(_tau) { init(); }

    void init();

    Ellipse intersectTri(const Vector &C1, const Vector &C2, const Vector &C3);

    // utils
    Vector worldToEllipsoid(const Vector &p_world) const;
    Vector ellipsoidToWorld(const Vector &p_ellipsoid) const;
    Vector worldToSphere(const Vector &p_world) const;
    Vector sphereToWorld(const Vector &p_sphere) const;

    Vector f1, f2, n1, n2;
    Float tau;

    Vector center;
    Float a, b, c;

    Frame ellipsoid_frame;

    AABB bounds;
};

#endif
