import { Vec3 } from "src/math/HVec3";
import Decimal from "decimal.js"
const EPSILON = new Decimal(1e-15);

enum Side {
    COPLANAR = 0,
    FRONT = 1,
    BACK = 2,
    SPANNING = 3,
}
/*
 * @Description  : 高精度多边形
 * @Author       : 赵耀圣
 * @QQ           : 549184003
 * @Date         : 2021-08-03 11:11:59
 * @LastEditTime : 2021-08-03 15:49:25
 * @FilePath     : \cga.js\src\csg\polygon.ts
 */
export class Polygon {

    w: Decimal = new Decimal(0);
    normal: Vec3 = new Vec3;
    vertices: Vec3[];
    constructor(vertices: Vec3[] = []) {
        this.vertices = vertices

        this.vertices = vertices;
        if (vertices.length > 0) {
            this.calcPlane();
        }
    }

    calcPlane() {
        var a = this.vertices[0],
            b = this.vertices[1],
            c = this.vertices[2];

        this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
        this.w = this.normal.clone().dot(a);

        return this;
    }

    flip() {
        this.normal.multiplyScalar(-1);
        this.w.mul(-1);

        this.vertices.reverse();

        return this;
    }


    /**
     * @description : 判断点与当前多边形所在的平面的位置关系
     * @param        {Vec3} vertex
     * @return       {*}
     * @example     : 
     */
    classifyVertex(vertex: Vec3) {
        var side_value: Decimal = this.normal.dot(vertex).sub(this.w);

        if (side_value.lessThan(-EPSILON)) {
            return Side.BACK;
        } else if (side_value > EPSILON) {
            return Side.FRONT;
        } else {
            return Side.COPLANAR;
        }
    }

    /**
     * @description : 判断另一个多边形与当前多边形的位置关系
     * @param        {Polygon} polygon
     * @return       {*}
     * @example     : 
     */
    classifySide(polygon: Polygon) {
        var vertex, classification,
            num_positive = 0,
            num_negative = 0;

        const vslen = polygon.vertices.length;
        let i;
        for (i = 0; i < vslen; i++) {
            vertex = polygon.vertices[i];
            classification = this.classifyVertex(vertex);
            if (classification === Side.FRONT) {
                num_positive++;
            } else if (classification === Side.BACK) {
                num_negative++;
            }
        }

        if (num_positive === vslen && num_negative === 0) {
            return Side.FRONT;
        } else if (num_positive === 0 && num_negative === vslen) {
            return Side.BACK;
        } else if (num_positive > 0 && num_negative > 0) {
            return Side.SPANNING;
        } else {
            return Side.COPLANAR;
        }
    }

    /**
     * @description : 切割多边形
     * @param        {Polygon} polygon
     * @param        {Polygon} coplanar_front
     * @param        {Polygon} coplanar_back
     * @param        {Polygon} front
     * @param        {Polygon} back
     * @return       {*}
     * @example     : 
     */
    splitPolygon(polygon: Polygon, coplanar_front: Polygon[] = [], coplanar_back: Polygon[] = [], front: Polygon[] = [], back: Polygon[] = []) {
        var classification = this.classifySide(polygon);

        if (classification === Side.COPLANAR) {

            (this.normal.dot(polygon.normal).greaterThan(0) ? coplanar_front : coplanar_back).push(polygon);

        } else if (classification === Side.FRONT) {

            front.push(polygon);

        } else if (classification === Side.BACK) {

            back.push(polygon);

        } else {

            var i: number, j: number,
                ti: Side, tj: Side,
                vi: Vec3, vj: Vec3,
                t: Decimal, v: Vec3,
                f: Vec3[] = [],
                b: Vec3[] = [];

            let vslen;

            for (i = 0, vslen = polygon.vertices.length; i < vslen; i++) {

                j = (i + 1) % vslen;
                vi = polygon.vertices[i];
                vj = polygon.vertices[j];
                ti = this.classifyVertex(vi);
                tj = this.classifyVertex(vj);

                if (ti != Side.BACK) f.push(vi);
                if (ti != Side.FRONT) b.push(vi);
                if ((ti | tj) === Side.SPANNING) {
                    t = (this.w.sub(this.normal.dot(vi))).div(this.normal.dot(vj.clone().sub(vi)));
                    v = vi.lerp(vj, t);
                    f.push(v);
                    b.push(v);
                }
            }


            if (f.length >= 3) front.push(new Polygon(f).calcPlane());
            if (b.length >= 3) back.push(new Polygon(b).calcPlane());
        }
    }

    clone(): Polygon {
        var i, vslen = this.vertices.length
        const polygon = new Polygon();

        for (i = 0; i < vslen; i++) {
            polygon.vertices.push(this.vertices[i].clone());
        }
        polygon.calcPlane();

        return polygon;
    }
}