package cn.sagacloud.server.algorithm.backstage.sync

import com.persagy.database.SFilter
import com.persagy.revit.backstage.model.DCComponent
import com.persagy.revit.backstage.model.DCEquipment
import com.persagy.revit.backstage.model.DCFloor
import com.persagy.revit.services.datacenter.DataCenterService
import com.persagy.revit.models.entities.EquipPart
import com.persagy.revit.models.jsonAnalyzer.Point
import com.persagy.revit.models.modelFile.FloorModel
import com.persagy.revit.services.BaseDataService
import com.persagy.revit.services.CommonService
import com.persagy.revit.backstage.sync.SyncEquipment
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 SyncEquipPart : SyncObject{

    var modelId: String? = null
    var floor: FloorModel? = null
    var dcFloors : ArrayList<DCFloor>? = null
    var bimIdPrefix : String? = null
    val bimMap = HashMap<String, EquipPart>()

    @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<EquipPart>()
    // 数据中心部件
    var dataCenter = HashMap<String, DCComponent>()
    var cadIdDataCenter = HashMap<String, DCComponent>()

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

    var deleteList = ArrayList<DCComponent>()

    var updateList = ArrayList<DCComponent>()

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

    override fun prepareModelData() : SyncObject{
        modelArr = dataService.componentService.select(SFilter.eq("modelId", modelId!!)).exec()
        return this
    }

    override fun prepareDataCenterData(): SyncObject {
        if(floor == null || floor!!.projectId == null)
            return this
        val result = dataCenterService.getComponentByBIMIDPrefix(bimIdPrefix!!, floor!!.projectId!!)
        val cadIdResult = dataCenterService.getComponentByCADID(floor!!.projectId!!)
        if(result == null || cadIdResult == null)
            throw Exception("从数据中心获取部件失败!")
        if(result.isNotEmpty()) {
            dataCenter = result
        }
        if(cadIdResult.isNotEmpty()) {
            cadIdDataCenter = cadIdResult
        }
        return this
    }

    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 parentBimId = HashSet<String>()
        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, parentBimId)
            }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, parentBimId)
                dcEquip.bimId ="${bimIdPrefix}${it.value.sourceId}"
                tobeDelete.add(it.key)
            }
        }
        for(id in tobeDelete)
            bimMap.remove(id)
        // 最后剩余在map里的就是需要创建的
        bimMap.forEach {
            val dcEquip = DCComponent()
            dcEquip.category = it.value.family
            dcEquip.localName = it.value.localName
            dcEquip.localId = it.value.localId
            dcEquip.bimId = it.key
//            dcEquip.floorId = dcFloor!!.id
            dcEquip.equipSourceId = it.value.ownerSourceId
            dcEquip.modelId = it.value.modelId
            dcEquip.familyName = it.value.familyName
            dcEquip.familySymbol = it.value.familySymbol
            dcEquip.bimTypeID = it.value.bimTypeID
            // cadid信息点生成逻辑
            SyncEquipment.generateCADID(dcEquip)
            val point = it.value.location?.points?.get(0)
            addLocationAndFloorId(point, dcEquip, sb)
            if(!dcEquip.equipSourceId.isNullOrEmpty())
                parentBimId.add("${bimIdPrefix}${dcEquip.equipSourceId!!}")
            createList.add(dcEquip)
            if(it.value.propertyId != null)
                bimIdPropertyMap[dcEquip.bimId!!] = it.value.propertyId!!
        }
        // 组织部件所属设备关系
        if(parentBimId.isNotEmpty()){
            val equipMap = dataCenterService.getEquipmentByBimIdSet(parentBimId, floor!!.projectId!!) ?: return this
            appendEquipRelation(updateList, equipMap)
            appendEquipRelation(createList, equipMap)
        }
        return this
    }

    /**
     * 将需要更新的对象添加到updateList里
     */
    private fun addToUpdate(equip: EquipPart, sb: StringBuilder, dcEquip: DCComponent, parentBimId: HashSet<String>) {
        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.equipSourceId = equip.ownerSourceId
        dcEquip.familyName = equip.familyName
        dcEquip.familySymbol = equip.familySymbol
        dcEquip.modelId = equip.modelId
        dcEquip.bimTypeID = equip.bimTypeID
        if(dcEquip.equipSourceId != null)
            parentBimId.add("${bimIdPrefix}${dcEquip.equipSourceId!!}")
        updateList.add(dcEquip)
        if(equip.propertyId != null && dcEquip.id != null)
            propertyMap[equip.propertyId!!] = dcEquip.id!!
    }

    /**
     * 给DCComponent对象添加BIMLocation和FloorId关系
     */
    private fun addLocationAndFloorId(point: Point?, dcEquip: DCComponent, 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) {
                        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()
        }
    }

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


    private fun appendEquipRelation(arr : ArrayList<DCComponent>, equipMap : HashMap<String, DCEquipment>){
        val sb = StringBuilder()
        arr.forEach {
            sb.clear()
            sb.append(bimIdPrefix).append(it.equipSourceId)
            if(equipMap.containsKey(sb.toString())){
                it.equipId = equipMap[sb.toString()]?.id
            }
        }
    }

    override fun syncCreate(): SyncObject {
        if(createList.isNotEmpty())
            dataCenterService.sync(createList as ArrayList<Any>, createSyncResultList as ArrayList<Any>, floor!!.projectId!!, DataCenterService.DataCenterOperator.componentCreate)
        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.componentDelete)
        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.componentUpdate)
        }
        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!!)
    }
}