#pragma once
#ifndef ELLIPSOID_AD_H__
#define ELLIPSOID_AD_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 EllipseAD {
    EllipseAD() { status = 0; }

    EllipseAD(const VectorAD &C1, const VectorAD &C2, const VectorAD &C3,
        const VectorAD &_O, const VectorAD &abc, const FloatAD &tau)
        : O(_O) {
        init(C1, C2, C3, abc, tau);
    }

    void init(const VectorAD &C1, const VectorAD &C2, const VectorAD &C3,
        const VectorAD &abc, const FloatAD &tau);

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

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

    // utils
    FloatAD quadDotAD(const VectorAD &u, const VectorAD &v, const VectorAD &diag) const;
    FloatAD pointToAngleAD(const VectorAD &p) const;
    VectorAD angleToPointAD(const FloatAD &angle) const;

    VectorAD ellipsoidToEllipseAD(const VectorAD &p_ellipsoid) const;
    VectorAD ellipseToEllipsoidAD(const VectorAD &p_ellipse) const;
    VectorAD ellipsoidToCircleAD(const VectorAD &p_ellipsoid) const;
    VectorAD circleToEllipsoidAD(const VectorAD &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;

    VectorAD O, TN, UN, N;
    FloatAD theta, m1, m2;

    VectorAD dO, dTN, dUN;
    FloatAD dm1, dm2;

    FrameAD ellipse_frame;

    std::vector<Vector2AD> curves;
};

struct EllipsoidAD {
    EllipsoidAD(const VectorAD &_f1, const VectorAD &_n1, const VectorAD &_f2, const VectorAD &_n2,
        const FloatAD &_tau) : f1(_f1), f2(_f2), n1(_n1), n2(_n2), tau(_tau) {
        init();
    }

    void init();

    EllipseAD intersectTri(const VectorAD &C1, const VectorAD &C2, const VectorAD &C3);

    // utils
    VectorAD worldToEllipsoidAD(const VectorAD &p_world) const;
    VectorAD ellipsoidToWorldAD(const VectorAD &p_ellipsoid) const;
    VectorAD worldToSphereAD(const VectorAD &p_world) const;
    VectorAD sphereToWorldAD(const VectorAD &p_sphere) const;

    VectorAD f1, f2, n1, n2;
    FloatAD tau;

    VectorAD center;
    FloatAD a, b, c;

    FrameAD ellipsoid_frame;

    AABB bounds;
};

#endif
