#ifndef POLYGON_H
#define POLYGON_H

#include "fastmath.hpp"
#include <iostream>
struct polygon {

    vec3 points[3];
    decimal delta[9];
    bool small = false;
    decimal baryFactor;

    decimal dot00;
    decimal dot01;
    decimal dot11;
    vec2 v0;
    vec2 v1;

    decimal bounding[4]; // min x, max x, min y, max y
    vec3 normals[3];
    vec3 colors[3];
    vec3 barycentrics;
    vec3 boundingBarycentrics;

    void calcDelta() {

        // for (int i = 0; i < 3; i++) {
        //     int n = (i + 1) % 3;
        //
        //     delta[i * 3] = points[i].y() - points[n].y();
        //     delta[i * 3 + 1] = points[n].x() - points[i].x();
        //     delta[i * 3 + 2] =
        //         points[i].x() * points[n].y() - points[i].y() *
        //         points[n].x();
        //     if (delta[i * 3].i == 0 && delta[i * 3 + 1].i == 0)
        //         small = true;
        // }

        v0 = vec2(points[2]) - vec2(points[0]);
        v1 = vec2(points[1]) - vec2(points[0]);

        dot00 = v0 * v0;
        dot01 = v1 * v0;
        dot11 = v1 * v1;

        bounding[0] = points[0].x();
        bounding[1] = points[0].x();
        bounding[2] = points[0].y();
        bounding[3] = points[0].y();
        for (int i = 1; i < 3; i++) {
            if (bounding[0] > points[i].x())
                bounding[0] = points[i].x();
            if (bounding[1] < points[i].x())
                bounding[1] = points[i].x();
            if (bounding[2] > points[i].y())
                bounding[2] = points[i].y();
            if (bounding[3] < points[i].y())
                bounding[3] = points[i].y();
        }

        // baryFactor =
        //     (points[1].x() - points[0].x()) * (points[2].y() - points[1].y())
        //     - (points[1].y() - points[0].y()) * (points[2].x() -
        //     points[1].x());
        std::cout << dot00 << " " << dot01 << " " << dot11 << std::endl;
        baryFactor = dot00 * dot11 - dot01 * dot01;
        std::cout << "baryFactor:" << baryFactor << std::endl;
        if (baryFactor.i == 0) {
            small = true;
        } else
            baryFactor = decimal(1.0) / baryFactor;
        // std::cout << baryFactor << std::endl;
        /*if ((bounding[1].i - bounding[0].i < 1 << HALF_SHIFT) &&
            (bounding[3].i - bounding[2].i < 1 << HALF_SHIFT))
            small = true;*/
    }

    vec3 avgNormal() {
        vec3 result = normals[0] + normals[1] + normals[2];
        // for (int i = 0; i < 3; i++) {
        //     result += normals[i];
        // }
        return result * decimal(0.3333);
    }

    const bool depContains(const vec3 &p) {
        // if (skip)
        //    return false;
        for (int i = 0; i < 3; i++) {
            if (small)
                return false;
            vec3 d = p;
            if ((d.x() * delta[i * 3] + d.y() * delta[i * 3 + 1] +
                 delta[i * 3 + 2]) > decimal(0.2))
                return false;
        }
        return true;
    }
    const bool contains(const vec3 &p) {
        if (small)
            return true;
        else
            return (barycentrics[0] >= decimal(-0.0)) &&
                   (barycentrics[1] >= decimal(-0.0)) &&
                   (barycentrics[0] + barycentrics[1] <= decimal(1.0));
    }
    friend std::ostream &operator<<(std::ostream &os, const polygon &p) {
        for (int i = 0; i < 3; i++) {
            os << p.points[i];
        }
        return os;
    }

    vec3 calcNormal() {
        return normals[0] * boundingBarycentrics[0] +
               normals[1] * boundingBarycentrics[1] +
               normals[2] * boundingBarycentrics[2];
    }

    vec3 calcColor() {
        return colors[0] * boundingBarycentrics[0] +
               colors[1] * boundingBarycentrics[1] +
               colors[2] * boundingBarycentrics[2];
    }

    decimal calcDepth() {
        return points[0].z() * boundingBarycentrics[0] +
               points[1].z() * boundingBarycentrics[1] +
               points[2].z() * boundingBarycentrics[2];
    }

    void calcBarycentric(vec3 s) {
        // if (small)
        //     return vec3(decimal(0.333), decimal(0.333), decimal(0.333));

        vec2 v2 = vec2(s) - vec2(points[0]);
        decimal dot02 = v0 * v2;
        decimal dot12 = v1 * v2;

        barycentrics = {};
        barycentrics[0] = dot11 * dot02 - dot01 * dot12;
        barycentrics[1] = dot00 * dot12 - dot01 * dot02;

        // barycentrics[0] = (points[1].x() - s.x()) * (points[2].y() -
        // s.y()) -
        //                   (points[2].x() - s.x()) * (points[1].y() -
        //                   s.y());
        // barycentrics[1] = (points[2].x() - s.x()) * (points[0].y() -
        // s.y()) -
        //                   (points[0].x() - s.x()) * (points[2].y() -
        //                   s.y());
        barycentrics = barycentrics * baryFactor;
        barycentrics[2] = decimal(1.0) - barycentrics[1] - barycentrics[0];
        // barycentrics[0]; return result;
        boundingBarycentrics = vec3::max(
            vec3::min(barycentrics, vec3(1.0, 1.0, 1.0)), vec3(0.0, 0.0, 0.0));
    }
};

#endif
