/*
 * *********************************************************************************************************************
 *
 *          !!
 *        .F88X
 *        X8888Y
 *      .}888888N;
 *        i888888N;        .:!              .I$WI:
 *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
 *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
 *            +888888N;  .8888888Y                                  "&&8Y.}8,
 *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
 *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
 *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
 *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
 *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
 *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
 *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
 *      .:R888888I
 *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
 *        ~8888'
 *        .!88~                                                                     All rights reserved.
 *
 * *********************************************************************************************************************
 */

package com.persagy.server.datacenter.services.relation_calc

import com.persagy.base.extensions.toJson
import com.persagy.database.SFilter
import com.persagy.mybatis.SMybatisDao
import com.persagy.server.datacenter.models.entities.graphtype.RelationTypeProject
import com.persagy.server.datacenter.datacenter.models.entities.graphtype.block.ConnectedBlock
import com.persagy.server.datacenter.wanda.rel.REq2Eq
import com.persagy.server.mappers.ICalcSpecialMapper
import com.persagy.server.datacenter.models.entities.Connector
import com.persagy.server.datacenter.models.wrapperobj.ClassCodeEnum
import com.persagy.server.datacenter.services.objects.BuildingService
import com.persagy.server.datacenter.services.objects.EquipmentService
import com.persagy.server.datacenter.wanda.obj.Equipment
import com.persagy.server.services.assistant.RUpDateEquipAndSapceService
import com.persagy.server.services.objects.FloorService
import com.persagy.server.utils.IdUtils
import com.persagy.service.SBaseService
import com.persagy.service.SObjectService
import com.persagy.service.SPageContext
import com.persagy.service.models.enums.SResponseType
import com.persagy.service.models.requests.SCreateRequest
import com.persagy.service.models.responses.SBaseResponse
import org.json.JSONObject
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

/**
 * 关系计算-管网
 *
 * @author 邓婷婷
 */
@Service
open class CalcSpecialService {


    /** 实例化 mapper 对象 */
    @Autowired
    lateinit var mapper: ICalcSpecialMapper

    @Autowired
    lateinit var mepSystemCalcService: MepSystemCalcService

    val connectedBlockService = SObjectService(SMybatisDao(ConnectedBlock::class.java))
    /** 关系总览服务 */
    val relationTypeProjectservice = SBaseService(SMybatisDao(RelationTypeProject::class.java))
    val rEq2EqService = SObjectService(SMybatisDao(REq2Eq::class.java))

    /**
     * 管网系统设备分块
     *@param calcName   计算关系名称
     *@param systemName 需要计算的系统名称
     *@param domain 需要计算的管道系统类型，枚举（水管或风管）：DomainPiping，DomainHvac
     */
    fun sysBolck(relationType: String,buildingId: String): SBaseResponse {
        try {
//            val buildingId: String? = null
            val projectId = SPageContext.getHeader("projectId").toString()
            upDateBiaoji(projectId,relationType ,3)
            val entity = relationTypeProjectservice.select(SFilter.eq("projectId", projectId), SFilter.eq("relationType", relationType)).entity()
            if (entity != null) {
                if (!entity.mepSystemType.isNullOrEmpty()){
                    val systemName = entity.mepSystemType!!
                    val calcName = entity.relationTypeName!!
                    // 分块
                    for (name in systemName) {
                        val domain = toolsMap(name)
                        val result = mapper.sysBolck(projectId, buildingId, name, domain)
                        if (!result) {
                            upDateBiaoji(projectId,relationType ,5)
                            return SBaseResponse(SResponseType.success)
                        }
                    }
                    // 设置源
//                    val sourceType = mepSystemCalcService.setSourceType(projectId, calcName, null, systemName)
//
//                    if (!sourceType) {
//                        upDateBiaoji(projectId,relationType ,5)
//                        return SBaseResponse(SResponseType.success)
//                    }
                    // 计算流向
//                    val exec = connectedBlockService.select(SFilter.eq("projectId", projectId), SFilter.inList("mepSystemType", systemName)).group("mepSystemType", "blockId").projection(arrayListOf("mepSystemType", "blockId")).exec()
//                    exec.forEach {
//                        val domain = toolsMap(it.mepSystemType!!)
//                        val result = mapper.sysDirection(projectId, buildingId, it.blockId!!, it.mepSystemType!!, domain, true)
//                        if (!result) {
//                            upDateBiaoji(projectId,relationType ,5)
//                            return SBaseResponse(SResponseType.success)
//                        }
//                    }
                    try {
                        // 生成数据中心使用的关系
//                        val generateRelation = mepSystemCalcService.generateRelation(projectId, relationType, null, systemName)
//                        generateRelation.addAll(mepSystemCalcService.generateNonDirectionalRelation(projectId, relationType, null, systemName))
                        val generateRelation = mepSystemCalcService.generateNonDirectionalRelation(projectId, relationType, buildingId, systemName)
                        if (generateRelation.size>0){
                            val delete = rEq2EqService.delete(SFilter.eq("projectId", projectId), SFilter.eq("type", relationType), SFilter.eq("sign", 2))

                            val rEqForSps = rEq2EqService.select(SFilter.eq("projectId", projectId), SFilter.eq("type", relationType), SFilter.eq("sign", 2)).exec()
                            val jsonObject = JSONObject()
                            val mapList = ArrayList<HashMap<String,String>>()
                            if(rEqForSps != null){
                                for (rEqForSp in rEqForSps) {
                                    val map = HashMap<String,String>()
                                    map["id1"] = rEqForSp.id1!!
                                    map["id2"] = rEqForSp.id2!!
                                    mapList.add(map)
                                }
                            }
                            jsonObject.put("type", "12")
                            jsonObject.put("projectId", projectId)
                            jsonObject.put("relation",mapList)
                            jsonObject.put("obj_type", relationType)

                            /** 发送消息 */
//                            RService.messageHandler.sendMessage(jsonObject.toString())

                            val sCreateRequest = SCreateRequest<REq2Eq>()
                            sCreateRequest.content = generateRelation
                            rEq2EqService.createList(sCreateRequest)

                            val createJsonObject = JSONObject()
                            val createMapList = ArrayList<HashMap<String,String>>()
                            for (rEq2Eq in generateRelation) {
                                val map = HashMap<String,String>()
                                map["id1"] = rEq2Eq.id1!!
                                map["id2"] = rEq2Eq.id2!!
                                createMapList.add(map)
                            }
                            createJsonObject.put("type", "11")
                            createJsonObject.put("projectId", projectId)
                            createJsonObject.put("obj_type", relationType)
                            createJsonObject.put("relation",createMapList)
                            /** 发送消息 */
//                            RService.messageHandler.sendMessage(createJsonObject.toString())
                        }
                    } catch (e: Exception) {
                        upDateBiaoji(projectId,relationType ,5)
                        return SBaseResponse(SResponseType.success)
                    }
                }
            }
            upDate(projectId,relationType)
            return SBaseResponse(SResponseType.success)
        } catch (e: Exception) {
            e.printStackTrace()
            return SBaseResponse(SResponseType.failure, e.message!!)
        }
    } // Fun sysBolck

    /**
     * 管网系统确定流向
     *@param buildingId 需要计算的building_id，如果没有提供时请传入null，不能传""
     *@param blockId 需要计算的块id
     *@param systemName 需要计算的系统名称
     *@param domain 需要计算的管道系统类型，枚举（水管或风管）：DomainPiping，DomainHvac
     */
    fun sysDirection(buildingId: String?, blockId: String, systemName: String, domain: String, isSource: Boolean): SBaseResponse {
        try {
            val projectId = SPageContext.getHeader("projectId").toString()
            val result = mapper.sysDirection(projectId, buildingId, blockId, systemName, domain, isSource)
            if (result) {
                return SBaseResponse(SResponseType.success)
            }
            return SBaseResponse(SResponseType.failure)
        } catch (e: Exception) {
            e.printStackTrace()
            return SBaseResponse(SResponseType.failure, e.message!!)
        }
    } // Fun sysDirection

    /**
     *
     */
    fun toolsMap(name: String): String {

        if (name == "补风管"||name == "空调送风风管"|| name == "空调排风风管"||name == "通风送风风管"
                ||name == "正压送风风管"|| name == "新风风管"||name == "排烟风管"||name == "通风排风风管"
                ||name == "送风兼消防补风风管"|| name == "排风兼排烟管"||name == "排油烟管"||name == "回风风管"
                ){
            return "DomainHvac"
        }
        return "DomainPiping"
    } //

    /**
     * 修改标记
     */
    fun upDate(projectId: String,type: String){
        try {
            val entity = relationTypeProjectservice.select(SFilter.eq("projectId", projectId), SFilter.eq("relationType", type)).entity()
            if (entity!=null){
                entity.computationalState = 1
                entity.computingTime = IdUtils.time()
                RUpDateEquipAndSapceService.relationTypeProjectservice.update(entity)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    } // Fun

    /**
     * 计算中标记
     */
    fun upDateBiaoji(projectId: String,type: String,sing: Int){
        try {
            if (sing == 3){
                val entity = relationTypeProjectservice.select(SFilter.eq("projectId", projectId), SFilter.eq("relationType", type)).entity()
                if (entity!=null){
                    entity.computationalState = 3
                    entity.computingTime = IdUtils.time()
                    RUpDateEquipAndSapceService.relationTypeProjectservice.update(entity)
                }
            }else{
                val entity = relationTypeProjectservice.select(SFilter.eq("projectId", projectId), SFilter.eq("relationType", type)).entity()
                if (entity!=null){
                    entity.computationalState = 5
                    entity.computingTime = IdUtils.time()
                    RUpDateEquipAndSapceService.relationTypeProjectservice.update(entity)
                }
            }

        } catch (e: Exception) {
            e.printStackTrace()
        }
    } // Fun

    /**
     * @Description: 管网计算
     * @Param:
     * @Return:
     */
    fun calcNetworks(projectId: String): SBaseResponse{
        val buildings = BuildingService.select(SFilter.eq("projectId",projectId)).exec()
        for (building in buildings) {
            val buildingId = building.id!!
            val floors = FloorService.select(SFilter.eq("projectId",projectId), SFilter.eq("buildingId",buildingId)).exec()
            val connectedBlocks = ArrayList<ConnectedBlock>()
            for (floor in floors) {
                if (!floor.modelId.isNullOrEmpty()) {
                    val connectors = mapper.connectorOrder(floor.modelId!!)
                    val dcEquipments = EquipmentService.select(SFilter.eq("projectId",projectId), SFilter.eq("floorId",floor.id!!)).exec()
                    val dcEquipMap = HashMap<String, Equipment>(1024)
                    for (equipment in dcEquipments) {
                        if(!equipment.bimId.isNullOrEmpty()){
                            dcEquipMap[equipment.bimId!!] = equipment
                        }

                    }

                    val connectorMap = HashMap<String, Connector>(100000)
                    val typeMap = HashMap<String, String>(20000)
                    for (connector in connectors) {
                        connectorMap[connector.revitId!!] = connector

                        val belong = connector.belongBimId ?: continue
                        val belongType = connector.belongType
                        if (belongType != null) {
                            typeMap[belong] = belongType
                        }

                    }
                    val belongMap = HashMap<String, HashSet<String>>()
                    val eqBelongSet = HashSet<String>()
                    //遍历连接件
                    for (connector in connectors) {
                        //拿到所属类
                        val belongType = connector.belongType
                        //拿到所属id
                        val belong = connector.belongBimId
                        if(belong != null){
                            eqBelongSet.add(belong)
                            //如果连接件有连接
                            if (connector.connectedIds!!.size > 0) {
                                val revitId = connector!!.connectedIds?.get(0)
                                var currentConnector = connectorMap[revitId!!]
                                val cBelong = currentConnector?.belongBimId
                                if (cBelong != null) {
//                                if(belongType == "Equipment"){
//                                    eqBelongSet.add(belong)
//                                }

                                    if (belongMap.containsKey(belong)) {
                                        val belongs = belongMap[belong]!!
                                        belongs.add(cBelong)
                                        belongMap[belong] = belongs
                                    } else {
                                        belongMap[belong] = hashSetOf(cBelong)
                                    }
                                }else{
                                    belongMap[belong] = HashSet()
                                }
                            }else {
                                belongMap[belong] = HashSet()
                            }
                        }
                    }
                    var fBlockId: Int =0
                    var dBlockId: Int =0
                    var cBlockId = 0
                    val fBlockMap = HashMap<Int,HashSet<String>>()
//                val dBlockMap = HashMap<Int,HashSet<String>>()
                    val enumList = ClassCodeEnum.toSourceList(null)
                    var fSet: HashSet<String>? = null
                    var dSet: HashSet<String>
                    println(eqBelongSet.size)
                    for (key in eqBelongSet) {
                        val e1 = dcEquipMap[key]
                        if(e1 == null || !enumList.contains(e1.classCode)){
                            continue
                        }

                        val belongSet = belongMap[key]!!

                        dSet = directDist(HashSet(belongSet),belongMap,typeMap)
                        dBlockId ++

                        for (s in dSet!!) {
                            if(typeMap[s] == "Equipment" && key != s){
                                val e2 = dcEquipMap[s]
                                if(e2 == null || !enumList.contains(e2.classCode)){
                                    continue
                                }
                                if(e1.classCode == e2.classCode){
                                    continue
                                }

                                val connectedBlock = ConnectedBlock()
                                connectedBlock.id1 = e1.id!!
                                connectedBlock.id2 = e2.id!!
                                connectedBlock.type1 = "Equipment"
                                connectedBlock.type2 = "Equipment"
                                connectedBlock.modelId1 = floor.modelId
                                connectedBlock.modelId2 = floor.modelId
//                                connectedBlock.domain = connector.domain
//                                connectedBlock.mepSystemType = connector.mepSystemType
                                connectedBlock.projectId = projectId
                                connectedBlock.buildingId = buildingId
                                connectedBlock.depth = 0
                                connectedBlock.blockId = dBlockId.toString()
                                connectedBlock.typeCode1 = e1?.classCode
                                connectedBlock.typeCode2 = e2?.classCode
                                if (!connectedBlocks.contains(connectedBlock)) {
                                    connectedBlocks.add(connectedBlock)
                                }

                            }
                        }
//                    if(listOf("Eq4403070003129ce8d657c94c648d2d164ccfc6802c",
//                                    "Eq4403070003db010203b5e54a0e8e4de56234cb6ac9",
//                                    "Eq4403070003666459bd6a7e434f8c091392bada823a",
//                                    "Eq4403070003956fbc1b234a4007bdce0653ae7faba2").contains(e1.id!!)){
//                        fSet = fullDist(HashSet(belongSet),belongMap)
//                    }

                        for (block in fBlockMap.keys) {
                            val set = fBlockMap[block]!!
                            if(set.contains(key)){
                                fSet = set
                                cBlockId = block
                                break
                            }
                        }
                        if(fSet == null){
                            fSet = fullDist(HashSet(belongSet),belongMap)
                            fBlockId ++
                            cBlockId = fBlockId
                            fBlockMap[cBlockId] = HashSet(fSet)
                            if(cBlockId == 1){
                                println(fBlockMap[cBlockId]!!.toJson())
                            }
                        }
//                    fSet = fullDist(HashSet(belongSet),belongMap)
//                    println(fSet.contains(key))
//                    println(fSet.size)
                        for (s in fSet!!) {
                            if(typeMap[s] == "Equipment" && key != s){
                                val e2 = dcEquipMap[s]
                                if(e2 == null || !enumList.contains(e2.classCode)){
                                    continue
                                }
                                if(e1.classCode == e2.classCode){
                                    continue
                                }

                                val connectedBlock = ConnectedBlock()

                                connectedBlock.id1 = e1.id!!
                                connectedBlock.id2 = e2.id!!
                                connectedBlock.type1 = "Equipment"
                                connectedBlock.type2 = "Equipment"
                                connectedBlock.modelId1 = floor.modelId
                                connectedBlock.modelId2 = floor.modelId
//                                connectedBlock.domain = connector.domain
//                                connectedBlock.mepSystemType = connector.mepSystemType
                                connectedBlock.projectId = projectId
                                connectedBlock.buildingId = buildingId
                                connectedBlock.depth = 1
                                connectedBlock.blockId = cBlockId.toString()
                                connectedBlock.typeCode1 = e1?.classCode
                                connectedBlock.typeCode2 = e2?.classCode
                                if (!connectedBlocks.contains(connectedBlock)) {
                                    connectedBlocks.add(connectedBlock)
                                }

                            }
                        }
//                    for (block in dBlockMap.keys) {
//                        val set = dBlockMap[block]!!
//                        if(set.contains(key)){
//                            dSet = set
//                            cBlockId = block
//                            break
//                        }
//                    }
//                    if(dSet == null){
//                        dSet = directDist(HashSet(belongSet),belongMap,typeMap)
//                        dBlockId ++
//                        cBlockId = dBlockId
//                        dBlockMap[cBlockId] = dSet
//                    }


//                    val newWork = dist(ArrayList(belongSet),ArrayList(belongSet), belongMap,typeMap,ArrayList(belongSet),0)
//                    val newWork = dist(HashSet(belongSet),HashSet(belongSet), belongMap,typeMap)
//                    newWork.fullConnectors!!.removeAll(newWork.directConnectors!!)
//                    for (s in newWork.fullConnectors!!) {
//
//                    }
//                    for (s in newWork.directConnectors!!) {
//
//                    }

                    }
                }


            }
            connectedBlockService.delete(SFilter.eq("projectId",projectId), SFilter.eq("buildingId",buildingId))
            val request = SCreateRequest<ConnectedBlock>()
            request.content = connectedBlocks
            connectedBlockService.createList(request)
        }


        return SBaseResponse(SResponseType.success, "")

    }



    /**
     * @Description:递归查找连接
     * @Param:
     * @Return:
     */
     fun searchConnector(connector: Connector, currentConnector: Connector, map: HashMap<String,ArrayList<Connector>>, connectorMap: HashMap<String, Connector>): Connector?{
        val belong = currentConnector.belong ?: return null
        if(currentConnector.belongType == "Equipment" && currentConnector != connector){
            return currentConnector
        }else{
//            println(currentConnector.belong)
            val connectors = map[belong] ?: return null
            for (connector in connectors) {
                if(currentConnector.revitId != connector.revitId && connector.isConnected == true){
                    val revitId = connector.connectedIds!![0]!!
                    val ccConnector = connectorMap[revitId]!!
                    return searchConnector(connector,ccConnector,map,connectorMap)
                }else{
                    continue
                }
            }
            return null
        }
     }

//    fun dist(list: ArrayList<String>,directList:ArrayList<String>, belongMap: HashMap<String,HashSet<String>>,typeMap: HashMap<String,String>,beforeList: ArrayList<String>,index: Int): NetWork {
//        var belong: String? = null
//        for(i in index until beforeList.size){
//            belong = beforeList[i]
//            if(belongMap[belong] != null){
//                val belongList = belongMap[belong]!!
//                for (s in belongList) {
////                    if(!directList.contains(s)){
////
////                    }
//                    if(!list.contains(s)){
//                        list.add(s)
//                        if(typeMap[belong] != "Equipment"){
//                            directList.add(s)
//                        }
//                    }
//                }
//            }
//        }
//
//        return if(beforeList.containsAll(list)){
//            NetWork(list, directList)
//        }else{
////            newList.clear()
////            newList.addAll(list)
////            newList.removeAll(beforeList)
//            println(list.size)
//            println("${HashSet<String>(list).size}=====")
//            dist(list,beforeList,belongMap,typeMap,ArrayList(list),beforeList.size)
//        }
//    }

    fun fullDist(list: HashSet<String>, belongMap: HashMap<String,HashSet<String>>): HashSet<String>{
        val beforeList = HashSet<String>(list)
        for (belong in beforeList) {
            if(belongMap[belong] == null){
                continue
            }
            val belongs =  HashSet<String>(belongMap[belong]!!)
//            if(typeMap[belong] != "Equipment"){
//                directList.addAll(belongs)
//            }
            list.addAll(belongs)
        }
        if(beforeList.containsAll(list)){
            return list
        }else{
            return fullDist(list,belongMap)
        }
    }

    fun directDist(directList:HashSet<String>, belongMap: HashMap<String,HashSet<String>>,typeMap: HashMap<String,String>): HashSet<String>{
        val beforeList = HashSet<String>(directList)
        for (belong in beforeList) {
            if(belongMap[belong] == null){
                continue
            }
            val belongs =  HashSet<String>(belongMap[belong]!!)
            if(typeMap[belong] != "Equipment"){
                directList.addAll(belongs)
            }
        }
        if(beforeList.containsAll(directList)){
            return directList
        }else{
            return directDist(directList,belongMap,typeMap)
        }
    }



//    fun calcNetworks(projectId: String,buildingId: String): SBaseResponse{
//        val connectedBlocks = connectedBlockService.select(SFilter.eq("projectId",projectId), SFilter.eq("buildingId",buildingId)).exec()
//        val classCodeList = ArrayList<String>()
//        for (classCodeEnum in ClassCodeEnum.values()) {
//            classCodeList.add(classCodeEnum.name)
//        }
//        val cb = connectedBlocks.filter { s -> classCodeList.contains(s.typeCode1)  || classCodeList.contains(s.typeCode2) }
//    }

} // Class CalcSpecialService