package cn.sagacloud.android.cadengine

import android.graphics.Point
import android.graphics.PointF
import android.util.Log
import cn.sagacloud.android.cadengine.types.PointZ
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt

/**
 * Created by lihao.
 * Date: 2021/6/4
 */
class Lasso {
    // polygon coordinates
    private var mPolyX: FloatArray
    private var mPolyY: FloatArray

    // number of size in polygon
    private var mPolySize: Int

    /**
     * default constructor
     *
     * @param px polygon coordinates X
     * @param py polygon coordinates Y
     * @param ps polygon sides count
     */
    constructor(px: FloatArray, py: FloatArray, ps: Int) {
        mPolyX = px
        mPolyY = py
        mPolySize = ps
    }

    /**
     * constructor
     *
     * @param pointFs points list of the lasso
     */
    constructor(pointFs: List<PointF>) {
        mPolySize = pointFs.size
        mPolyX = FloatArray(mPolySize)
        mPolyY = FloatArray(mPolySize)
        for (i in 0 until mPolySize) {
            mPolyX[i] = pointFs[i].x
            mPolyY[i] = pointFs[i].y
        }
        Log.d("lasso", "lasso size:$mPolySize")
    }

    /**
     * check if this polygon contains the point.
     *
     * @param x point coordinate X
     * @param y point coordinate Y
     * @return point is in polygon flag
     */
    fun contains(x: Float, y: Float): Boolean {
        var result = false
        var i = 0
        var j = mPolySize - 1
        while (i < mPolySize) {
            if (mPolyY[i] < y && mPolyY[j] >= y
                || mPolyY[j] < y && mPolyY[i] >= y
            ) {
                if (mPolyX[i] + (y - mPolyY[i]) / (mPolyY[j] - mPolyY[i])
                    * (mPolyX[j] - mPolyX[i]) < x
                ) {
                    result = !result
                }
            }
            j = i++
        }
        return result
    }

    companion object {
        fun isPolygonContainsPoint(mPoints: List<PointF>, point: Point): Boolean {
            var nCross = 0
            for (i in mPoints.indices) {
                val p1 = mPoints[i]
                val p2 = mPoints[(i + 1) % mPoints.size]
                // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数
                // p1p2是水平线段,要么没有交点,要么有无限个交点
                if (p1.y == p2.y) continue
                // point 在p1p2 底部 --> 无交点
                if (point.y < Math.min(p1.y, p2.y)) continue
                // point 在p1p2 顶部 --> 无交点
                if (point.y >= Math.max(p1.y, p2.y)) continue
                // 求解 point点水平线与当前p1p2边的交点的 X 坐标
                val x = ((point.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x).toDouble()
                if (x > point.x) // 当x=point.x时,说明point在p1p2线段上
                    nCross++ // 只统计单边交点
            }
            // 单边交点为偶数，点在多边形之外 ---
            return nCross % 2 == 1
        }


        fun isPolygonContainsPointZ(mPoints: ArrayList<PointZ>, point: Point): Boolean {
            var nCross = 0
            for (i in mPoints.indices) {
                val p1 = mPoints[i]
                val p2 = mPoints[(i + 1) % mPoints.size]
                // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数
                // p1p2是水平线段,要么没有交点,要么有无限个交点
                if (p1.y == p2.y) continue
                // point 在p1p2 底部 --> 无交点
                if (point.y < Math.min(p1.y, p2.y)) continue
                // point 在p1p2 顶部 --> 无交点
                if (point.y >= Math.max(p1.y, p2.y)) continue
                // 求解 point点水平线与当前p1p2边的交点的 X 坐标
                val x = ((point.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x).toDouble()
                if (x > point.x) // 当x=point.x时,说明point在p1p2线段上
                    nCross++ // 只统计单边交点
            }
            // 单边交点为偶数，点在多边形之外 ---
            return nCross % 2 == 1
        }

        /**
         * point3到（point1与point2的连线）的最短距离
         */
        fun beeline(
            point1: PointF,
            point2: PointF,
            point3: PointF
        ): Double {
            val l1 = sqrt(
                abs(point1.x - point2.x).toDouble()
                    .pow(2.0) + abs(point1.y - point2.y)
                    .toDouble().pow(2.0)
            ).toFloat()
            val l2 = sqrt(
                abs(point1.x - point3.x).toDouble().pow(2.0) + abs(point1.y + point3.y)
                    .toDouble().pow(2.0)
            ).toFloat()
            val l3 = sqrt(
                abs(point2.x - point3.x).toDouble().pow(2.0) + abs(point2.y + point3.y)
                    .toDouble().pow(2.0)
            ).toFloat()
            val halfP = (l1 + l2 + l3) / 2
            val s =
                sqrt(halfP * (halfP - l1) * (halfP - l2) * (halfP - l3).toDouble())
            val h = 2 * s / l1
            return h
        }
    }
}