/*
 * *********************************************************************************************************************
 *
 *          !!
 *        .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.server.datacenter.models.entities.graphtype.block.EquipIdUUIDMap
import com.persagy.server.datacenter.wanda.rel.REq2Eq
import java.util.stream.Collectors

/**
 * 将revit_calc.connected_block里计算的分块和流向数据剔除到只剩设备流向到设备的数据
 *
 * @author
 */
class MepSystemTransformer {

    // 总体关联数据,    key --> 对象本身,  value --> 流向的对象集合
    private val data = HashMap<String, HashSet<String>>()
    // 计算出的管道直接连接的设备   key --> 对象id (非设备或部件)    value --> 设备或者部件id
    private val tubeEquipMap = HashMap<String, HashSet<String>>()
    // key --> 设备或者部件对象的uuid    value --> true表示是设备类型, false表示是部件类型
    private val typeMap = HashMap<String, Boolean>()


    /**
     * 输入流向数据
     */
    fun insertRelData(fromId : String, fromType : String, toId : String, toType : String){
        addToTypeMap(fromId, fromType)
        addToTypeMap(toId, toType)
        insertToDataMap(fromId, toId)
    }

    /**
     * 输入无流向数据
     */
    fun insertNonDirectionalRelData(fromId : String, fromType : String, toId : String, toType : String){
        addToTypeMap(fromId, fromType)
        addToTypeMap(toId, toType)
        insertToDataMap(fromId, toId)
        insertToDataMap(toId, fromId)
    }

    private fun insertToDataMap(fromId: String, toId: String) {
        if (!data.containsKey(fromId)) {
            data[fromId] = HashSet()
        }
        val set = data[fromId]
        if (!set!!.contains(toId)) {
            set.add(toId)
        }
    }

    /**
     * 获取 Eq2Eq的关系
     */
    fun getEq2EqRelation() : ArrayList<Pair<String, String>> {
        val tmp = ArrayList<Pair<String, String>>()
        data.forEach { (fromId, toIdSet) ->
            if(!typeMap.containsKey(fromId))
                return@forEach
            toIdSet.forEach {
                if(typeMap.containsKey(it)){
                    val pair = Pair(fromId, it)
                    tmp.add(pair)
                }
            }
        }
        return tmp
    }

    /**
     * 将 uuidRelation 中的设备之间的uuid关系  根据idMapArr 的映射关系, 转化为最终输出结果, 即 relationship.r_eq2eq表的实体对象数组
     * @param uuidRelation      设备id的流向关系(uuid)
     * @param idMapArr          设备uuid --> 设备数据中心id的转化关系 (包含部件)
     * @return                  设备id的流向关系
     */
    fun transformEquipRel(projectId : String, relType : String, uuidRelation : ArrayList<Pair<String, String>>, idMapArr : ArrayList<EquipIdUUIDMap>, hasDirection : Boolean) : ArrayList<REq2Eq>{
        val idUUIDMap = idMapArr.stream().collect(Collectors.toMap(EquipIdUUIDMap::equipId, EquipIdUUIDMap::revitUUID))
        val result = ArrayList<REq2Eq>()
        uuidRelation.forEach {
            if(idUUIDMap.containsKey(it.first) && idUUIDMap.containsKey(it.second)){
                val rel = REq2Eq()
                rel.id1 = idUUIDMap[it.first]
                rel.id2 = idUUIDMap[it.second]
                rel.projectId = projectId
                rel.sign = 2
                rel.type = relType
                rel.hasDirection = hasDirection
                result.add(rel)
            }
        }
        return result
    } // Fun

    /**
     * 数组转成map
     */
    private fun arrToMap(idMapArr: ArrayList<EquipIdUUIDMap>): HashMap<String, String> {
        val map = HashMap<String, String>()
        idMapArr.forEach {
            if(it.revitUUID != null && it.equipId != null)
                map[it.revitUUID!!] = it.equipId!!
        }
        return map
    } // Fun

    /**
     * 获取关系中的设备或者部件id集合
     * @param relArr        设备或部件uuid关系集合(trim过后的结果)
     * @param equipOrPart   true表示返回设备, false表示返回部件
     */
    fun getInvolvedEquip(relArr : ArrayList<Pair<String, String>>, equipOrPart : Boolean) : Set<String>{
        val result = HashSet<String>()
        relArr.forEach {
            if(typeMap[it.first] == equipOrPart)
                result.add(it.first)
            if(typeMap[it.second] == equipOrPart)
                result.add(it.second)
        }
        return result
    } // Fun

    /**
     * 将data里的关系删减到只有设备和部件之间的关系
     */
    fun trim(){
        data.forEach { (fromId, toIdSet) ->
            if(!typeMap.containsKey(fromId))
                return@forEach
            // 删除toIdSet中不是设备或者部件的对象
            val toDelete = HashSet<String>()
            // 添加toIdSet中通过管道直接连接的设备或部件对象
            val toAdd = HashSet<String>()
            toIdSet.forEach{toId ->
                if(!typeMap.containsKey(toId)){
                    toDelete.add(toId)
                    toAdd.addAll(getConnectedEquip(toId, HashSet<String>(), tubeEquipMap))
                }
            }
            toIdSet.removeAll(toDelete)
            // 排除自己到自己的关系
            toAdd.remove(fromId)
            toIdSet.addAll(toAdd)
        }
    } // Fun

    /**
     * 是否包含在Map里
     */
    fun isContain(nonDuplicateMap : HashMap<String, MutableSet<String>>, key : String, value : String) : Boolean{
        if(!nonDuplicateMap.containsKey(key))
            return false
        var set = nonDuplicateMap[key]
        if(!set!!.contains(value))
            return false
        return true
    } // Fun

    /**
     * 是否双向包含在Map里
     */
    fun isContainByDirectional(nonDuplicateMap : HashMap<String, MutableSet<String>>, value1 : String, value2 : String) : Boolean{
        if(isContain(nonDuplicateMap, value1, value2))
            return true
        if(isContain(nonDuplicateMap, value2, value1))
            return true
        return false
    } // Fun

    /**
     * 插入数据到Map里
     */
    private fun insertToNonDuplicateMap(nonDuplicateMap: HashMap<String, MutableSet<String>>, key: String, value: String) {
        if (!nonDuplicateMap.containsKey(key)){
            nonDuplicateMap[key] = HashSet()
        }
        var setStr = nonDuplicateMap[key]
        var set : MutableSet<String> = java.util.HashSet<String>()
        setStr!!.add(value)
    } // Fun

    /**
     * 获取无方向的设备之间的关系
     */
    fun getEq2EqNonDirectionalRelation() : ArrayList<Pair<String, String>> {
        val tmp = ArrayList<Pair<String, String>>()
        val nonDuplicateMap = HashMap<String, MutableSet<String>>()
        data.forEach { (fromId, toIdSet) ->
            if(!typeMap.containsKey(fromId))
                return@forEach
            toIdSet.forEach {
                if(typeMap.containsKey(it)){
                    if (!isContainByDirectional(nonDuplicateMap, fromId, it)){
                        insertToNonDuplicateMap(nonDuplicateMap, fromId, it)
                        val pair = Pair(fromId, it)
                        tmp.add(pair)
                    }
                }
            }
        }
        return tmp
    } // Fun

    /**
     * 获取某个管道下直接连接的设备或部件id集合
     *
     * @param pathSet 沿着管道找设备经过的路径, 防止死循环
     * @param tubeEquipMap 管道直接连着的设备 (包括通过其他管道间接直连的设备)
     */
    private fun getConnectedEquip(tubeId : String, pathSet : HashSet<String>, tubeEquipMap : HashMap<String, HashSet<String>>) : HashSet<String>{
        val connectedEquip = HashSet<String>()
        if(pathSet.contains(tubeId)){
            // 如果出现循环, 直接返回处理
            return connectedEquip
        }
        pathSet.add(tubeId)
        // 如果以前已经计算过该map
        if(tubeEquipMap.containsKey(tubeId)){
            return tubeEquipMap[tubeId]!!
        }
        if(!data.containsKey(tubeId)){
            return connectedEquip
        }

        val connectedObj = data[tubeId]

        connectedObj!!.forEach {
            // 管道连接的是设备或部件
            if(typeMap.containsKey(it)){
                connectedEquip.add(it)
            }else{
                connectedEquip.addAll(getConnectedEquip(it, pathSet, tubeEquipMap))
            }
        }
        pathSet.remove(tubeId)
        if(tubeEquipMap[tubeId] == null)
            tubeEquipMap[tubeId] = HashSet()
        tubeEquipMap[tubeId]!!.addAll(connectedEquip)
        return connectedEquip
    } // Fun

    /**
     * 将设备和部件的id和类型加入typeMap
     */
    private fun addToTypeMap(id : String, type : String){
        if("Equipment".equals(type)){
            typeMap[id] = true
        }else if("EquipPart".equals(type)){
            typeMap[id] = false
        }
    } // Fun

} // Class