import Constant from './Constant'
export default class MathUtil {
    constructor() {}

    getFixed(num, decimal) {
        if (!decimal) {
            decimal = 5
        }
        // return Math.floor(num * 10000) / 10000;
        return parseFloat(num.toFixed(decimal))
    }

    // 求两个点的距离
    getDistance(p1, p2) {
        const x1 = p1.x
        const y1 = p1.y
        const x2 = p2.x
        const y2 = p2.y
        const num = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))
        return this.getFixed(num)
    }

    createLine1(point1, point2) {
        if (point1.x == point2.x && point1.y == point2.y) {
            return null
        } else if (this.getFixed(Math.abs(point1.x - point2.x)) == 0) {
            return { x: point1.x }
        } else if (this.getFixed(Math.abs(point1.y - point2.y)) == 0) {
            return { y: point1.y }
        }

        const parametera = (point1.y - point2.y) / (point1.x - point2.x)
        const parameterb = (point1.x * point2.y - point2.x * point1.y) / (point1.x - point2.x)
        if (this.getFixed(parametera) == 0) {
            return { y: this.getFixed(parameterb) }
        }
        const parameter = { a: this.getFixed(parametera), b: this.getFixed(parameterb) }
        return parameter
    }

    createLine2(point, angle) {
        if (angle == Math.PI / 2 || angle == 1.5 * Math.PI) {
            return { x: point.x }
        }
        let a = Math.tan(angle)
        let b = point.y - a * point.x
        if (a != 0) {
            return { a: a, b: b }
        } else {
            return { y: point.y }
        }
    }

    // 与lineA平行并且point在线上
    createLine3(lineA, point) {
        const parameter = {}
        if (typeof lineA.a === 'undefined') {
            if (typeof lineA.x !== 'undefined') {
                parameter.x = point.x
            } else if (typeof lineA.y !== 'undefined') {
                parameter.y = point.y
            }
        } else {
            parameter.a = lineA.a
            parameter.b = point.y - point.x * lineA.a
        }
        return parameter
    }

    create2AngleLine(point, angle, driftAngle) {
        let line1 = this.createLine2(point, angle - driftAngle / 2)
        let line2 = this.createLine2(point, angle + driftAngle / 2)
        return { line1: line1, line2: line2 }
    }

    distanceForPoints(point1, point2) {
        return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2))
    }

    //与line平行且两条线直接的距离是distance的两条线
    getParallelLineForDistance(line, distance) {
        let line1 = {}
        line1.a = line.a
        line1.b = line.b

        let line2 = {}
        line2.a = line.a
        line2.b = line.b

        if (typeof line.a == 'undefined') {
            if (line.hasOwnProperty('x')) {
                let x = line.x
                line1.x = x + distance
                line2.x = x - distance
            } else if (line.hasOwnProperty('y')) {
                let y = line.y
                line1.y = y + distance
                line2.y = y - distance
            }
        } else {
            let angle = Math.atan(line.a)
            let db = Math.abs(distance / Math.cos(angle))
            let b = line.b
            line1.b = b + db
            line2.b = b - db
        }

        return { line1: line1, line2: line2 }
    }

    //获取扇形的两个端点
    getEndpoint(point, angle, sectorAngle) {
        const distance = 15
        //line1是减，line2是加
        let lines1 = this.create2AngleLine(point, angle, sectorAngle)
        let line = this.createLine2(point, angle)
        line = this.getLineForPoint(line, point)
        let lines2 = this.getParallelLineForDistance(line, distance)

        let point1 = this.getIntersectionPoint(lines1.line1, lines2.line1)
        let point2 = this.getIntersectionPoint(lines1.line1, lines2.line2)
        let point3 = this.getIntersectionPoint(lines1.line2, lines2.line1)
        let point4 = this.getIntersectionPoint(lines1.line2, lines2.line2)

        let angle1 = this.Angle(point, point1, { x: point.x + 1, y: point.y })
        let angle2 = this.Angle(point, point2, { x: point.x + 1, y: point.y })
        let angle3 = this.Angle(point, point3, { x: point.x + 1, y: point.y })
        let angle4 = this.Angle(point, point4, { x: point.x + 1, y: point.y })
        if (angle > Math.PI) {
            angle = 2 * Math.PI - angle
        }
        if (Math.abs((angle1 + angle3) / 2 - angle) < Math.abs((angle2 + angle4) / 2 - angle)) {
            return { p1: point1, p2: point3 }
        } else {
            return { p1: point2, p2: point4 }
        }
    }

    // true表示逆时针，false表示顺时针
    isClockwise(vertices) {
        let area = 0
        for (let i = 0; i < vertices.length; i++) {
            const j = (i + 1) % vertices.length
            area += vertices[i].x * vertices[j].y
            area -= vertices[j].x * vertices[i].y
        }
        const sub = area / 2
        if (sub > 0) {
            // 逆时针
            return true
        } else {
            // 顺时针
            return false
        }
    }

    reverse(points) {
        const _points = []
        for (let i = points.length - 1; i > -1; --i) {
            _points.push(points[i])
        }
        return _points
    }

    //两条线的交点
    getIntersectionPoint(parameter1, parameter2) {
        if (this.isParallel(parameter1, parameter2)) {
            return null
        }
        if (typeof parameter1.a == 'undefined' && typeof parameter2.a != 'undefined') {
            if (parameter1.x) {
                return { x: parameter1.x, y: parameter2.a * parameter1.x + parameter2.b }
            } else if (parameter1.y) {
                return { x: (parameter1.y - parameter2.b) / parameter2.a, y: parameter1.y }
            }
        } else if (typeof parameter2.a == 'undefined' && typeof parameter1.a != 'undefined') {
            if (parameter2.x) {
                return { x: parameter2.x, y: parameter1.a * parameter2.x + parameter1.b }
            } else if (parameter2.y) {
                return { x: (parameter2.y - parameter1.b) / parameter1.a, y: parameter2.y }
            }
        } else if (typeof parameter2.a == 'undefined' && typeof parameter1.a == 'undefined') {
            if (parameter1.hasOwnProperty('x') && parameter2.hasOwnProperty('y')) {
                return { x: parameter1.x, y: parameter2.y }
            } else if (parameter1.hasOwnProperty('y') && parameter2.hasOwnProperty('x')) {
                return { x: parameter2.x, y: parameter1.y }
            } else {
                return null
            }
        }

        if (parameter1.a == parameter2.a) {
            return null
        }

        let joinpointx = (parameter2.b - parameter1.b) / (parameter1.a - parameter2.a)
        let joinpointy = (parameter1.a * parameter2.b - parameter2.a * parameter1.b) / (parameter1.a - parameter2.a)

        let point = { x: joinpointx, y: joinpointy }
        return point
    }

    // 直线的交点
    getIntersectionPoint2(a, b, c, d) {
        /** 1 解线性方程组, 求线段交点. **/

        // 如果分母为0 则平行或共线, 不相交
        const denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y)
        if (denominator == 0) {
            return null
        }

        // 线段所在直线的交点坐标 (x , y)
        const x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) + (b.y - a.y) * (d.x - c.x) * a.x - (d.y - c.y) * (b.x - a.x) * c.x) / denominator
        const y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x) + (b.x - a.x) * (d.y - c.y) * a.y - (d.x - c.x) * (b.y - a.y) * c.y) / denominator

        return { x: x, y: y }
    }

    //两条线段交点
    getIntersectionPoint3(a, b, c, d) {
        const join = this.getIntersectionPoint2(a, b, c, d)
        if (join) {
            const x = join.x
            const y = join.y // 交点在线段1上 且交点也在线段2上
            /** 2 判断交点是否在两条线段上 **/
            if ((x - a.x) * (x - b.x) <= 0.001 && (y - a.y) * (y - b.y) <= 0.001 && (x - c.x) * (x - d.x) <= 0.001 && (y - c.y) * (y - d.y) <= 0.001) {
                // 返回交点p
                return {
                    x: x,
                    y: y,
                }
            }
            return null
        }
        return null
    }

    // 线段和直线是否相交
    getIntersectionPoint4(point1, point2, line) {
        const line1 = this.createLine1(point1, point2)
        const join = this.getIntersectionPoint(line1, line)
        if (join == null) {
            return null
        }
        if (this.PointInSegment(join, point1, point2)) {
            return join // 相交
        } else {
            return null
        }
    }

    //返回true表示平行
    isParallel(line1, line2) {
        if (typeof line1.a == 'undefined' && typeof line2.a == 'undefined') {
            if (line1.hasOwnProperty('x') && line2.hasOwnProperty('x')) {
                return true
            } else if (line1.hasOwnProperty('y') && line2.hasOwnProperty('y')) {
                return true
            } else {
                return false
            }
        } else if (typeof line1.a == 'undefined' || typeof line2.a == 'undefined') {
            return false
        } else if (this.getFixed(line1.a) == this.getFixed(line2.a)) {
            return true
        } else {
            return false
        }
    }

    //两条相交的线段的夹角，永远小于180度
    Angle(o, s, e) {
        let cosfi = 0,
            fi = 0,
            norm = 0
        let dsx = s.x - o.x
        let dsy = s.y - o.y
        let dex = e.x - o.x
        let dey = e.y - o.y

        cosfi = dsx * dex + dsy * dey
        norm = (dsx * dsx + dsy * dsy) * (dex * dex + dey * dey)
        cosfi /= Math.sqrt(norm)

        if (cosfi >= 1.0) return 0
        //if (cosfi <= -1.0) return Math.PI;
        if (cosfi <= -1.0) return Math.PI
        fi = Math.acos(cosfi)

        if ((180 * fi) / Math.PI < 180) {
            //return 180 * fi / Math.PI;
            return fi
        } else {
            //return 360 - 180 * fi / Math.PI;
            return 2 * Math.PI - fi
        }
    }

    //经过point且与line垂直的线
    getLineForPoint(line, point) {
        let parameter = {}
        if (line.a == 0 || typeof line.a == 'undefined') {
            if (line.hasOwnProperty('x')) {
                parameter.y = point.y
                parameter.x = line.x
            } else if (line.hasOwnProperty('y')) {
                parameter.x = point.x
                parameter.y = line.y
            }
        } else {
            parameter.a = -1 / line.a
            parameter.b = point.y - point.x * parameter.a
        }
        return parameter
    }

    // 经过point且与line垂直的直线，该直线与line的交点
    getJoinLinePoint(point, line) {
        const verticalLine = this.getVerticalLine(line, point)
        const join = this.getIntersectionPoint(line, verticalLine)
        return join
    }

    // 点到直线的距离
    getDisForPoinLine(point, line) {
        const join = this.getJoinLinePoint(point, line)
        return this.getDistance(point, join)
    }

    // 垂直线
    getVerticalLine(line, point) {
        if (typeof line.a === 'undefined') {
            if (line.hasOwnProperty('x')) {
                return { y: point.y }
            } else if (line.hasOwnProperty('y')) {
                return { x: point.x }
            } else {
                return null
            }
        } else if (line.a == 0) {
            return { x: point.x }
        } else {
            const tl = {}
            tl.a = -1 / line.a
            const result = this.createLine3(tl, point)
            return result
        }
    }

    //point在直线上，只是不确定是否在线段上
    //方法：point到startPoint和endPoint的距离之和与startPoint和endPoint之间的距离对比
    isContainForSegment(point, startPoint, endPoint, minDis) {
        if (!minDis) {
            minDis = Constant.minRealDis
        }
        let dis1 = this.getDistance(startPoint, point) + this.getDistance(endPoint, point)
        let dis2 = this.getDistance(startPoint, endPoint)
        if (Math.abs(dis1 - dis2) < minDis) {
            return true
        } else {
            return false
        }
    }

    /*
    //minDis
    isPointInPoly(point, points, minDis) {
        if (!minDis) {
            minDis = Constant.minRealDis
        }
        const x = point.x
        const y = point.y

        let inside = false

        // 是否在顶点附近
        for (let i = 0; i < points.length; ++i) {
            let distance = this.getDistance(point, points[i])
            if (distance < minDis) {
                return true
            }
        }

        // 是否在边沿
        for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
            let pt1 = points[i]
            let pt2 = points[j]
            const flag = this.isContainForSegment(point, pt1, pt2, minDis)
            if (flag) {
                return true
            }
        }

        for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
            let pt1 = points[i]
            let pt2 = points[j]
            const xi = pt1.x
            const yi = pt1.y
            const xj = pt2.x
            const yj = pt2.y

            const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
            if (intersect) inside = !inside
        }

        return inside
    }
    */

    isPointInPoly(point, points) {
        const x = point.x
        const y = point.y

        let inside = false

        for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
            let pt1 = points[i]
            let pt2 = points[j]
            const xi = pt1.x
            const yi = pt1.y
            const xj = pt2.x
            const yj = pt2.y

            const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
            if (intersect) inside = !inside
        }

        return inside
    }

    // 点到线段的距离
    // 在minDistance范围内，会吸附到point1/point2上
    // 返回值：type是1表示吸附在point1，是2表示吸附在point2，是0表示在线段point1-point2上；
    getDisForPoinSegment(point, point1, point2, minDistance) {
        const line = this.createLine1(point1, point2)
        const join = this.getJoinLinePoint(point, line)
        const dis = this.getDistance(point1, point2)
        const dis1 = this.getDistance(join, point1)
        const dis2 = this.getDistance(join, point2)
        if (this.getDistance(join, point1) > dis || this.getDistance(join, point2) > dis) {
            // 在线段外
            if (dis1 < dis2 && dis1 < minDistance) {
                return { type: 1, join: point1 }
            } else if (dis2 < dis1 && dis2 < minDistance) {
                return { type: 2, join: point2 }
            } else {
                return null
            }
        } else {
            if (dis1 < minDistance) {
                return { type: 1, join: point1 }
            } else if (dis2 < minDistance) {
                return { type: 2, join: point2 }
            } else if (this.getDistance(point, join) < minDistance) {
                return { type: 0, join: join }
            }
        }
    }

    PointInSegment(Q, pi, pj, minDis) {
        if (this.getDistance(Q, pi) < Constant.minRealDis || this.getDistance(Q, pj) < Constant.minRealDis) {
            return true
        }

        if (!minDis) {
            minDis = 0.1
        }
        minDis = minDis / 2

        const offset1 = (Q.x - pi.x) * (pj.y - pi.y) - (pj.x - pi.x) * (Q.y - pi.y)
        const offset2 = Math.min(pi.x, pj.x) - Q.x
        const offset3 = Q.x - Math.max(pi.x, pj.x)
        const offset4 = Math.min(pi.y, pj.y) - Q.y
        const offset5 = Q.y - Math.max(pi.y, pj.y)

        if (
            Math.abs(offset1) < minDis &&
            (offset2 <= 0 || Math.abs(offset2) < minDis) &&
            (offset3 <= 0 || Math.abs(offset3) < minDis) &&
            (offset4 <= 0 || Math.abs(offset4) < minDis) &&
            (offset5 <= 0 || Math.abs(offset5) < minDis)
        ) {
            return true
        } else {
            return false
        }
    }

    clonePoint(p1, p2) {
        p1.x = p2.x
        p1.y = p2.y
    }

    equalPoint(p1, p2) {
        if (p1.x == p2.x && p1.y == p2.y) {
            return true
        } else {
            return false
        }
    }

    crossTwoLines(point1, point2, point3, point4, dis) {
        if (typeof dis == 'undefined') {
            dis = Constant.minRealDis
        }
        const join = this.getIntersectionPoint2(point1, point2, point3, point4)
        if (join != null) {
            if (this.getDistance(point1, join) > dis && this.getDistance(point2, join) > dis && this.getDistance(point3, join) > dis && this.getDistance(point4, join) > dis) {
                if (
                    this.getDistance(point1, join) < this.getDistance(point1, point2) &&
                    this.getDistance(point2, join) < this.getDistance(point1, point2) &&
                    this.getDistance(point3, join) < this.getDistance(point3, point4) &&
                    this.getDistance(point4, join) < this.getDistance(point3, point4)
                ) {
                    return true
                } else {
                    return false
                }
            }
        } else {
            if (this.PointInSegment(point1, point3, point4) || this.PointInSegment(point2, point3, point4)) {
                return true
            }
        }
        return false
    }

    getDisPointsLine(line, point, distance1, distance2) {
        const newpoint1 = {}
        const newpoint2 = {}
        const result = {}
        if (line.hasOwnProperty('x')) {
            newpoint1.x = line.x
            newpoint1.y = point.y - distance1
            newpoint2.x = line.x
            newpoint2.y = point.y + distance2
        } else if (line.hasOwnProperty('y')) {
            newpoint1.y = line.y
            newpoint1.x = point.x - distance1
            newpoint2.y = line.y
            newpoint2.x = point.x + distance2
        } else {
            const a = Math.atan(line.a)
            const t_line = { a: -1 / line.a }
            const line_ab2 = this.createLine3(t_line, point)
            const join = this.getIntersectionPoint(line, line_ab2)

            newpoint1.x = join.x - distance1 * Math.cos(a)
            newpoint1.y = join.y - distance1 * Math.sin(a)

            newpoint2.x = join.x + distance2 * Math.cos(a)
            newpoint2.y = join.y + distance2 * Math.sin(a)
        }
        result.newpoint1 = newpoint1
        result.newpoint2 = newpoint2
        return result
    }

    getBoundingBox(points) {
        let minX = points[0].x
        let maxX = points[0].x
        let minY = points[0].y
        let maxY = points[0].y

        for (let i = 1; i < points.length; ++i) {
            const point = points[i]
            if (minX > point.x) {
                minX = point.x
            }
            if (minY > point.y) {
                minY = point.y
            }
            if (maxX < point.x) {
                maxX = point.x
            }
            if (maxY < point.y) {
                maxY = point.y
            }
        }

        const box = {}
        box.minX = minX
        box.minY = minY
        box.maxX = maxX
        box.maxY = maxY

        return box
    }

    ComputePolygonArea(points) {
        const point_num = points.length
        if (point_num < 3) {
            return 0
        }
        let s = points[0].y * (points[point_num - 1].x - points[1].x)
        for (let i = 1; i < point_num; ++i) s += points[i].y * (points[i - 1].x - points[(i + 1) % point_num].x)
        return Math.abs(s / 2.0)
    }

    // 获取多边形重心
    getPolygonCore(points) {
        function Area(p0, p1, p2) {
            let area = 0.0
            area = p0.x * p1.y + p1.x * p2.y + p2.x * p0.y - p1.x * p0.y - p2.x * p1.y - p0.x * p2.y
            return area / 2
        }

        let sum_x = 0
        let sum_y = 0
        let sum_area = 0
        let p1 = points[1]
        for (let i = 2; i < points.length; i++) {
            const p2 = points[i]
            const area = Area(points[0], p1, p2)
            sum_area += area
            sum_x += (points[0].x + p1.x + p2.x) * area
            sum_y += (points[0].y + p1.y + p2.y) * area
            p1 = p2
        }
        const xx = sum_x / sum_area / 3
        const yy = sum_y / sum_area / 3
        return {
            x: xx,
            y: yy,
        }
    }

    // points1是否在points2里
    isPolyInPoly(points1, points2, minDis) {
        for (let i = 0; i < points1.length; ++i) {
            let flag = false
            for (let j = 0; j < points2.length; ++j) {
                if (this.equalPoint(points1[i], points2[j])) {
                    flag = true
                    break
                }
            }
            if (!flag) {
                if (!this.isPointInPoly(points1[i], points2, minDis)) {
                    return false
                }
            } else {
                const nextIndex = i == points1.length - 1 ? 0 : i + 1
                const mid = {
                    x: (points1[i].x + points1[nextIndex].x) / 2,
                    y: (points1[i].y + points1[nextIndex].y) / 2,
                }
                if (!this.isPointInPoly(mid, points2, minDis)) {
                    return false
                }
            }
        }
        return true
    }

    dotPoints(pt1, pt2, point1, point2) {
        let vt1 = {}
        let vt2 = {}
        vt1.start = {}
        vt1.end = {}
        vt1.start.x = 0
        vt1.start.y = 0
        vt1.end.x = pt2.x - pt1.x
        vt1.end.y = pt2.y - pt1.y

        vt2.start = {}
        vt2.end = {}
        vt2.start.x = 0
        vt2.start.y = 0
        vt2.end.x = point2.x - point1.x
        vt2.end.y = point2.y - point1.y

        let result = vt1.end.x * vt2.end.x + vt1.end.y * vt2.end.y
        return result
    }
}

const mathUtil = new MathUtil()
export { mathUtil }
