package com.persagy.revit.backstage.sync

import cn.sagacloud.server.algorithm.backstage.sync.SyncObject
import com.persagy.revit.models.jsonAnalyzer.Point
import com.persagy.revit.services.BaseDataService
import com.persagy.database.SFilter
import com.persagy.revit.backstage.model.DCEquipment
import com.persagy.revit.backstage.model.DCFloor
import com.persagy.revit.models.entities.Equipment
import com.persagy.revit.models.modelFile.FloorModel
import com.persagy.revit.services.CommonService
import com.persagy.revit.services.datacenter.DataCenterService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component


/**
 * 同步设备
 *
 * @author jxing
 */
@Suppress("UNCHECKED_CAST")
@Component
@Scope("prototype")
class SyncEquipment : SyncObject {
    var modelId: String? = null
    var floor: FloorModel? = null
    var dcFloors : ArrayList<DCFloor>? = null
    var bimIdPrefix : String? = null
    val bimMap = HashMap<String, Equipment>()

    @Autowired
    lateinit var dataCenterService: DataCenterService

    val dataService = BaseDataService

    /**
     * 初始化
     */
    override fun init(modelId: String, obj: FloorModel, dcFloors: ArrayList<DCFloor>, bimIdPrefix: String): SyncObject {
        this.modelId = modelId
        this.floor = obj
        this.dcFloors = dcFloors
        this.bimIdPrefix = bimIdPrefix
        return this
    }

    // 模型内设备
    var modelArr = ArrayList<Equipment>()
    // 数据中心设备
    var dataCenter = HashMap<String, DCEquipment>()
    var cadIdDataCenter = HashMap<String, DCEquipment>()
    // 交集(BIMID)
//    val intersection = HashSet<String>()

    var createList = ArrayList<DCEquipment>()
    var createSyncResultList = ArrayList<DCEquipment>()

    var deleteList = ArrayList<DCEquipment>()

    var updateList = ArrayList<DCEquipment>()

    val propertyMap = HashMap<String, String>()   // propertyId --> EquipId
    private val bimIdPropertyMap = HashMap<String, String>()      // bimid --> propertyId

    /**
     * 准备模型数据
     */
    override fun prepareModelData() : SyncObject {
        modelArr = dataService.equipmentService.select(SFilter.eq("modelId", modelId!!)).exec()
        return this
    }

    /**
     * 准备数据中心数据
     */
    override fun prepareDataCenterData(): SyncObject {
        if(floor == null || floor!!.projectId == null)
            return this
        val result = dataCenterService.getEquipmentByBIMIDPrefix(bimIdPrefix!!, floor!!.projectId!!)
        val cadIdResult = dataCenterService.getEquipmentByCADID(floor!!.projectId!!)
        if(result == null || cadIdResult == null)
            throw Exception("从数据中心获取设备失败!")
        if(result.isNotEmpty()) {
            dataCenter = result
        }
        if(cadIdResult.isNotEmpty()){
            cadIdDataCenter = cadIdResult
        }
        return this
    }

    /**
     * 构建bimidMap
     */
    override fun buildBIMIDMap(): SyncObject {
        val sb = StringBuilder()
        modelArr.forEach {
            if(it.sourceId != null) {
                sb.clear()
                sb.append(bimIdPrefix).append(it.sourceId)
                bimMap[sb.toString()] = it
            }
        }
        return this
    }

    /**
     * 开始匹配并分类
     */
    override fun matchClassify(): SyncObject {
        val sb = StringBuilder()
        dataCenter.forEach { (_, dcEquip) ->
            if(dcEquip.bimId != null && bimMap.contains(dcEquip.bimId!!)){
                val equip = bimMap[dcEquip.bimId!!]
                if(!equip!!.family.equals(dcEquip.category)){
                    // 需要先删除, 后添加
                    deleteList.add(dcEquip)
                    return@forEach
                }
                // 交集, 更新
                bimMap.remove(dcEquip.bimId!!)
                // 绑定数据
                addToUpdate(equip, sb, dcEquip)
            }else{
                // 需要被删除的
                deleteList.add(dcEquip)
            }
        }
        // 继续用CADID匹配
        // 逻辑 --> 获取数据中心所有CADID不为空, BIMID为null的设备, 如果用CADID识别到唯一的对象, 则认为是同一对象, 执行update操作
        val cadIdMap = buildCADIDMap(cadIdDataCenter)
        val tobeDelete = HashSet<String>()
        bimMap.forEach{
            val cadId = CommonService.generateCADIDStr(it.value.localId, it.value.localName)
            if(cadId.isNullOrBlank())
                return@forEach
            // CADID找到匹配, 更新该对象
            if(cadIdMap.containsKey(cadId)){
                val dcEquip = cadIdMap[cadId]!!
                addToUpdate(it.value, sb, dcEquip)
                dcEquip.bimId ="${bimIdPrefix}${it.value.sourceId}"
                tobeDelete.add(it.key)
            }
        }
        for(id in tobeDelete)
            bimMap.remove(id)


        // 最后剩余在map里的就是需要创建的
        bimMap.forEach {
            val dcEquip = DCEquipment()
            dcEquip.category = it.value.family
            dcEquip.localName = it.value.localName
            dcEquip.localId = it.value.localId
            dcEquip.bimId = it.key
//            dcEquip.floorId = dcFloor!!.id
            dcEquip.familyName = it.value.familyName
            dcEquip.familySymbol = it.value.familySymbol
            dcEquip.modelId = it.value.modelId
            dcEquip.bimTypeID = it.value.bimTypeID
            //添加信息点
            dcEquip.infos = it.value.infos
            generateCADID(dcEquip)
            val point = it.value.location?.points?.get(0)
            addLocationAndFloorId(point, dcEquip, sb)
            createList.add(dcEquip)
            if(it.value.propertyId != null)
                bimIdPropertyMap[dcEquip.bimId!!] = it.value.propertyId!!
        }
        return this
    }

    /**
     * 获取一个以CADID为key的DCEquipment Map
     */
    private fun buildCADIDMap(cadIdDataCenter: HashMap<String, DCEquipment>): HashMap<String, DCEquipment> {
        val map = HashMap<String, DCEquipment>()
        val duplicatedCADID = HashSet<String>()
        for(id in cadIdDataCenter.keys){
            val equipment = cadIdDataCenter[id]
            if(!equipment!!.cadId.isNullOrBlank()){
                if(duplicatedCADID.contains(equipment.cadId))
                    continue
                if(map.containsKey(equipment.cadId)){
                    duplicatedCADID.add(equipment.cadId!!)
                    map.remove(equipment.cadId!!)
                    continue
                }
                map[equipment.cadId!!] = equipment
            }
        }
        return map
    }

    /**
     * 将需要更新的对象添加到updateList里 (localId, localName为空才更新)
     */
    private fun addToUpdate(equip: Equipment, sb: StringBuilder, dcEquip: DCEquipment) {
        val point = equip.location?.points?.get(0)
        addLocationAndFloorId(point, dcEquip, sb)
        if(dcEquip.localId.isNullOrBlank())
            dcEquip.localId = equip.localId
        if(dcEquip.localName.isNullOrBlank())
            dcEquip.localName = equip.localName
        dcEquip.familyName = equip.familyName
        dcEquip.familySymbol = equip.familySymbol
        dcEquip.modelId = equip.modelId
        dcEquip.bimTypeID = equip.bimTypeID
        //更新信息点
        dcEquip.infos = equip.infos
        updateList.add(dcEquip)
        if(equip.propertyId != null && dcEquip.id != null)
            propertyMap[equip.propertyId!!] = dcEquip.id!!
    }

    /**
     * 给DCEquipment对象添加BIMLocation和FloorId关系
     */
    private fun addLocationAndFloorId(point: Point?, dcEquip: DCEquipment, sb: StringBuilder) {
        if (point != null) {
            if (dcFloors!!.isNotEmpty()) {
                var defaultDCFloor: DCFloor? = null
                // 找到一个没有划分过的楼层作为默认所属楼层(没有outline)
                for (dcFloor in dcFloors!!){
                    if(dcFloor.outline == null) {
                        defaultDCFloor = dcFloor
                        break
                    }
                }
                for (dcFloor in dcFloors!!) {
                    if (dcFloor.outline != null) {
                        // 如果数据中心的楼层设置了outline, 判断是否在outline内
                        if (CommonService.isPointInPolygonCheckWithJdkGeneralPath(point, dcFloor.outline!!)) {
                            dcEquip.floorId = dcFloor.id
                            dcEquip.buildingId = dcFloor.buildId
                            break
                        }
                    }
                }
                // 如果没找到所属楼层, 则使用默认楼层作为所属楼层
                if(dcEquip.floorId == null && defaultDCFloor != null){
                    dcEquip.floorId = defaultDCFloor.id
                    dcEquip.buildingId = defaultDCFloor.buildId
                }
            }
            sb.clear()
            sb.append(point.x).append(",").append(point.y).append(",").append(point.z)
            dcEquip.bimLocation = sb.toString()
        }
    }

    /**
     * 同步创建到数据中心
     */
    override fun syncCreate(): SyncObject {
        if(createList.isNotEmpty()) {
            dataCenterService.sync(createList as ArrayList<Any>, createSyncResultList as ArrayList<Any>, floor!!.projectId!!, DataCenterService.DataCenterOperator.equipCreate)
        }
        if(!bimIdPropertyMap.isEmpty()){
            createSyncResultList.forEach {
                if(it.bimId!= null && bimIdPropertyMap.containsKey(it.bimId!!)){
                    val propertyId = bimIdPropertyMap[it.bimId!!]
                    propertyMap[propertyId!!] = it.id!!
                }
            }
        }
        return this
    }

    /**
     * 同步在数据中心删除
     */
    override fun syncDelete(): SyncObject {
        if(deleteList.isNotEmpty())
            dataCenterService.sync(deleteList as ArrayList<Any>, null, floor!!.projectId!!, DataCenterService.DataCenterOperator.equipDelete)
        return this
    }

    /**
     * 同步在数据中心更新
     */
    override fun syncUpdate(): SyncObject {
        if(updateList.isNotEmpty()) {
//            updateList.forEach {
//                it.localId = null
//                it.cadId = null
//                it.localName = null
//            }
            dataCenterService.sync(updateList as ArrayList<Any>, null, floor!!.projectId!!, DataCenterService.DataCenterOperator.equipUpdate)
        }
        return this
    }

    fun syncPropertyId() {
        val propertyList = dataCenterService.getPropertyById(propertyMap.keys.toMutableList() as ArrayList<String>, floor!!.projectId!!)
        if(propertyList == null)
            return
        propertyList.forEach {
            if(propertyMap.containsKey(it.id)){
                it.equipId = propertyMap[it.id]
            }
        }
        dataCenterService.propertyUpdate(propertyList, floor!!.projectId!!)
    }

    companion object {
        fun generateCADID(dcEquip: DCEquipment) {
            if (dcEquip.localId.isNullOrBlank() || dcEquip.localName.isNullOrBlank()) {
                if (dcEquip.localId.isNullOrBlank() && dcEquip.localName.isNullOrBlank()) {
                    dcEquip.cadId = null
                } else
                    dcEquip.cadId = "${if(dcEquip.localId == null)"" else dcEquip.localId}${if(dcEquip.localName == null)"" else dcEquip.localName}"
            } else {
                dcEquip.cadId = "${dcEquip.localId}:${dcEquip.localName}"
            }
        }
    }
}