package cn.sagacloud.server.algorithm.common.utils

import cn.sagacloud.server.algorithm.models.dataCheck.GridCheckResult
import cn.sagacloud.server.algorithm.models.entities.Grid
import cn.sagacloud.server.algorithm.models.jsonAnalyzer.Location
import cn.sagacloud.server.algorithm.models.jsonAnalyzer.Point
import cn.sagacloud.server.algorithm.models.modelFile.FloorModelView
import org.apache.commons.collections4.CollectionUtils
import org.apache.commons.collections4.MapUtils
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.BeanUtils
import java.math.BigDecimal
import java.util.*

/**
 * Grid分组工具
 * 基于python代码改写:relations.src.grid.check_grid
 * @author Administrator
 * @date 2021-10-21
 */
object GridGroupUtil {
    /**
     * 获取分组信息
     * @param views
     * @param gridMap
     * @return
     */
    fun checkGridUpright(views: List<FloorModelView?>, gridMap: Map<String?, List<Grid>?>): List<GridCheckResult>? {
        if (CollectionUtils.isEmpty(views)) {
            return null
        }
        // 组装名称位置信息
        val gridNameMap = convertGridMap(gridMap)
        // 获取分组
        val groupMap = groupGrids(gridNameMap)
        // 转换结果
        val rsList: MutableList<GridCheckResult> = ArrayList()
        for (view in views) {
            val rs = GridCheckResult()
            BeanUtils.copyProperties(view, rs)
            rs.groupId = MapUtils.getInteger(groupMap, view!!.currentModelId)
            rsList.add(rs)
        }
        return rsList
    }

    /**
     * 将GridMap转换为名称、位置对照
     * @param gridMap
     * @return
     */
    private fun convertGridMap(gridMap: Map<String?, List<Grid>?>): Map<String?, Map<String?, Location>?>? {
        if (MapUtils.isEmpty(gridMap)) {
            return null
        }
        // 组装名称位置信息
        val gridNameMap: MutableMap<String?, Map<String?, Location>?> = HashMap(16)
        for (modelId in gridMap.keys) {
            val gridList: List<Grid?>? = gridMap[modelId]
            if (CollectionUtils.isEmpty(gridList)) {
                continue
            }
            val nameMap: MutableMap<String?, Location> = HashMap(16)
            gridNameMap[modelId] = nameMap
            // 获取Name, Location存入Map
            for (grid in gridList!!) {
                val name = grid!!.name
                val location = grid.location
                if (StringUtils.isBlank(name) || location == null) {
                    continue
                }
                nameMap[name] = location
            }
        }
        return gridNameMap
    }

    /**
     * 按Grid冲突关系，将ModelId分组
     * 分组号从1开始.
     * @param gridMap
     * @return
     */
    private fun groupGrids(gridMap: Map<String?, Map<String?, Location>?>?): Map<String?, Int?>? {
        if (MapUtils.isEmpty(gridMap)) {
            return null
        }
        val modelIdList = groupGridToList(gridMap)
        val modelIdMap: MutableMap<String?, Int?> = HashMap(16)
        var i = 0
        val j = modelIdList.size
        while (i < j) {
            val idList: List<String?> = modelIdList[i]
            for (modelId in idList) {
                modelIdMap[modelId] = i + 1
            }
            i++
        }
        return modelIdMap
    }

    /**
     * 将grid按modelId分组
     * 每组的ModelId都是不冲突的
     * @param gridMap
     * @return List<[id1,id2,id5],[id3,id4]>
     */
    private fun groupGridToList(gridMap: Map<String?, Map<String?, Location>?>?): List<MutableList<String?>> {
        val modelIdList: MutableList<MutableList<String?>> = ArrayList()
        // 按ModelId循环
        for (modelId in gridMap!!.keys) {
            // 是否所有分组都有冲突
            var isAllConflict = true
            // 按分组循环
            var i = 0
            val j = modelIdList.size
            while (i < j) {
                val idList = modelIdList[i]
                // 当前分组内是否有冲突
                var isConflict = false
                for (toId in idList) {
                    val fromMap: Map<String?, Location?>? = gridMap[modelId]
                    val toMap: Map<String?, Location?>? = gridMap[toId]
                    // 如果有冲突，则不再比较
                    if (isConflict(fromMap, toMap)) {
                        isConflict = true
                        break
                    }
                }
                // 如果没有冲突，则放入此队列中
                if (!isConflict) {
                    idList.add(modelId)
                    isAllConflict = false
                    break
                }
                i++
            }
            // 如果所有分组内都有冲突，则新增分组
            if (isAllConflict) {
                val idList: MutableList<String?> = ArrayList()
                idList.add(modelId)
                modelIdList.add(idList)
            }
        }
        return modelIdList
    }

    /**
     * 模型中的Grid是否有冲突
     * @param fromMap
     * @param toMap
     * @return
     */
    private fun isConflict(fromMap: Map<String?, Location?>?, toMap: Map<String?, Location?>?): Boolean {
        if (MapUtils.isEmpty(fromMap) || MapUtils.isEmpty(toMap)) {
            return false
        }
        // 按名称循环处理
        for (name in fromMap!!.keys) {
            if (!toMap!!.containsKey(name)) {
                continue
            }
            // 只处理名称相同的比较
            val from = fromMap[name]
            val to = toMap[name]
            if (from == null || to == null) {
                continue
            }
            // 位置有冲突
            if (isConflict(from, to)) {
                return true
            }
        }
        return false
    }

    /**
     * 位置是否冲突
     * @param from
     * @param to
     * @return
     */
    fun isConflict(from: Location, to: Location): Boolean {
        // 只有都是线的类型才判断冲突
        if (!(StringUtils.contains(from.type, "Line")
                        && StringUtils.contains(to.type, from.type))) {
            return false
        }
        // 点位应在2个以上
        if (CollectionUtils.size(from.points) < 2 || CollectionUtils.size(to.points) < 2) {
            return false
        }
        // 取开始结束点位
        val fromBegin = from.points!![0]
        val fromEnd = from.points!![1]
        val toBegin = to.points!![0]
        val toEnd = to.points!![1]
        // 计算相对角度和偏移量
        return try {
            val fromAngle = calAngle(fromBegin, fromEnd)
            val fromOffset = calOffset(fromBegin, fromAngle)
            val toAngle = calAngle(toBegin, toEnd)
            val toOffset = calOffset(toBegin, toAngle)
            // 都相同则冲突
            fromAngle == toAngle && fromOffset == toOffset
        } catch (e: Exception) {
            // 计算异常则直接判断点位X坐标
            fromEnd.x == toBegin.x && fromEnd.x == toEnd.x
        }
    }

    /**
     * 计算两个点的相对角度
     * @param begin
     * @param end
     * @return
     */
    private fun calAngle(begin: Point, end: Point): BigDecimal {
        val beginX = BigDecimal.valueOf(begin.x)
        val beginY = BigDecimal.valueOf(begin.y)
        val endX = BigDecimal.valueOf(end.x)
        val endY = BigDecimal.valueOf(end.y)
        return beginY.subtract(endY).divide(beginX.subtract(endX))
    }

    /**
     * 计算点位的偏移量
     * @param begin
     * @param angle
     * @return
     */
    private fun calOffset(begin: Point, angle: BigDecimal): BigDecimal {
        val beginX = BigDecimal.valueOf(begin.x)
        val beginY = BigDecimal.valueOf(begin.y)
        return beginY.subtract(angle.multiply(beginX))
    }
}