package com.sybotan.android.demo.viewmodel

/**
 * Created by lihao.
 * Date: 2021/11/9
 */

import android.app.Activity.RESULT_OK
import android.app.Application
import android.text.TextUtils
import androidx.lifecycle.viewModelScope
import cn.hutool.core.util.IdUtil
import cn.sagacloud.android.cadengine.types.*
import com.framework.mvvm.model.db.entity.*
import com.framework.mvvm.model.db.entity.task.*
import com.framework.mvvm.model.db.entity.dict.EquipmentEntity
import com.framework.mvvm.model.db.entity.dict.PipeConfigEntity
import com.framework.mvvm.model.db.entity.dict.RelConfigEntity
import com.framework.mvvm.model.repository.AdmRepository
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.sybotan.android.demo.activities.BaseActivity
import com.sybotan.android.demo.bean.request.*
import com.sybotan.android.demo.bean.result.*
import com.sybotan.android.demo.bean.result.Position
import com.sybotan.android.demo.retrofit.BaseViewModelInterface
import com.sybotan.android.demo.tools.CommonUtils
import com.sybotan.android.demo.tools.ToastUtils
import kotlinx.coroutines.launch
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.instance
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import com.sybotan.android.demo.bean.*
import com.sybotan.android.demo.tools.ObjectNameUtils


/**
 * Created by lihao.
 * Date: 2021/6/9
 */
class EquipDetailVM(
    app: Application,
    b: BaseViewModelInterface?, activity: BaseActivity?, //建筑id
    private val buildingId: String, //楼层id
    private val floorId: String,
    private val projectId: String
) : BaseViewModel(app, b, activity), DIAware {
    private val gson = Gson()

    override val di: DI by closestDI()
    val repo: AdmRepository by instance()

    fun getEquipment(deviceId: String) {
        viewModelScope.launch {
            val equips = repo.getEquipment()
            for (equip in equips) {
                if (!TextUtils.isEmpty(equip.systemCode)) {
                    val systemEntity = repo.getSysByCode(equip.systemCode!!)
                    equip.systemName = systemEntity?.name
                }
            }
            mEmitter.SendDirective(EQUIP_CLASS, equips)

            if (!TextUtils.isEmpty(deviceId)) {
                var equip = repo.getObject(deviceId)
                val infos = gson.fromJson(equip!!.infos, HashMap::class.java) as HashMap<String, Any>
                if (!TextUtils.isEmpty(equip.localId)) {
                    infos.put("localId", equip.localId.toString())
                }
                if (!TextUtils.isEmpty(equip.localName)) {
                    infos.put("localName", equip.localName.toString())
                }
                mEmitter.SendDirective(SPACE_INFOS, infos)
                mEmitter.SendDirective(Equip_INFOS, equip)
            }
        }
    }

    fun getEquipInfosConfig(equipmentEntity: EquipmentEntity) {
        viewModelScope.launch {
            val infosConfig = repo.getInfosConfigByEquipCode(equipmentEntity.code!!)
            val equipConfig = repo.getInfosConfigByEquipCode("equipment")
            var configs = ArrayList<Infos>()
            if (!CommonUtils.IsNull(equipConfig)) {
                configs.addAll(gson.fromJson(equipConfig[0].infos, object : TypeToken<ArrayList<Infos?>?>() {}.type))
            }
            if (!CommonUtils.IsNull(infosConfig)) {
                configs.addAll(gson.fromJson<List<Infos>>(infosConfig[0].infos, object : TypeToken<List<Infos?>?>() {}.type))
            }

            if (CommonUtils.IsNull(configs)) {
                mEmitter.SendDirective(EQUIP_INFO, null)
                return@launch
            }

            mergeInfo(equipmentEntity, configs)
            mEmitter.SendDirective(EQUIP_INFO, configs)

        }
    }

    private suspend fun mergeInfo(equipmentEntity: EquipmentEntity, infoList: ArrayList<Infos>) {
        val iterator = infoList.iterator()
        val codeList = ArrayList<String>()
        while (iterator.hasNext()) {
            val item = iterator.next()
            if (codeList.contains(item.code)) {
                iterator.remove()
            } else {
                codeList.add(item.code)
            }
        }
        for (info in infoList) {
            val infoEntity =
                repo.getInfoByClassCodeAndCode(equipmentEntity.code!!, "equipment", info.code)
            if (infoEntity != null) {
                info.dataType =
                    if (TextUtils.isEmpty(infoEntity?.dataType)) "" else infoEntity?.dataType!!
                info.unit = if (TextUtils.isEmpty(infoEntity?.unit)) "" else infoEntity?.unit!!
                if (infoEntity!!.dataSource != null) {
                    info.options = gson.fromJson(
                        infoEntity.dataSource,
                        object : TypeToken<List<Options?>?>() {}.type
                    )
                }
                if (TextUtils.isEmpty(info.inputType)) {
                    when {
                        info.dataType.equals("BOOLEAN") -> {
                            if (CommonUtils.IsNull(info.options)) {
                                info.options = ArrayList()
                                info.options.add(Options("off", "否"))
                                info.options.add(Options("on", "是"))
                            }
                        }
                        info.dataType.equals("ATTACHMENT") -> {
                            info.inputType = "photo"
                        }
                        info.dataType.equals("DATETIME") -> {
                            info.inputType = "date"
                        }
                        info.dataType.equals("DOUBLE") -> {
                            info.inputType = "double"
                        }
                        info.dataType.equals("INTEGER") -> {
                            info.inputType = "text"
                        }
                        else -> {
                            info.inputType = "text"
                        }
                    }
                    if (!CommonUtils.IsNull(info.options)) {
                        info.inputType = "radio"
                    }

                }
            }
        }
    }

    fun insEquip(
        uuid: String,
        deviceId: String?,
        hashMap: HashMap<String, Any>?,
        equipContent: String,
        beans: ArrayList<Photos>,
        equipEntity: EquipmentEntity,
        checked: Int,
        position: Position,
        containerId: String,
    ) {
        viewModelScope.launch {
            var localId = ""
            if (hashMap!!["localId"] != null && !TextUtils.isEmpty(hashMap["localId"].toString())) {
                localId = hashMap["localId"].toString()
            }
            var localName = ""
            if (hashMap!!["localName"] != null && !TextUtils.isEmpty(hashMap["localName"].toString())) {
                localName = hashMap["localName"].toString()
            }
            hashMap!!.put("equipContent", equipContent)
            var photos = ArrayList<Photos>()
            beans.filter { bean -> bean.itemType != 1 }.forEach { photos.add(it) }
            hashMap!!.put("equipPhoto", gson.toJson(photos))
            hashMap!!.put("equipCheck", checked)
            val z = PointZ()
            z.x = position.x.toFloat()
            z.y = -position.y.toFloat()
            z.z = position.z.offset.toFloat()
            hashMap!!.put("location", z)
            hashMap!!.put("zRegion", position.z.region)
            if (TextUtils.isEmpty(deviceId)) {
                val objectEntity = ObjectEntity(
                    id = uuid,
                    name = localName,
                    projectId = projectId,
                    objType = "equipment",
                    classCode = equipEntity.code!!,
                    localId = localId,
                    localName = localName,
                    buildingId = buildingId,
                    floorId = floorId,
                    clientId = uuid,
                    infos = gson.toJsonTree(hashMap),
                    state = 0,
                    containerId = if (TextUtils.isEmpty(containerId)) "" else containerId
                )
                repo.insObject(objectEntity)
            } else {
                val equip = repo.getObject(deviceId!!)
//                equip!!.infos = JSONObject(hashMap as Map<*, *>)
                equip!!.classCode = equipEntity.code!!
                equip.localId = localId
                equip.localName = localName
                equip.infos = gson.toJsonTree(hashMap)
                equip.state()
                repo.insObject(equip)
            }
            //插入文件数据
            var order = 0
            for (photo in beans) {
                if (photo.itemType != 1) {
                    if (order != 0) {
                        var phUuid = ""
                        if (TextUtils.isEmpty(photo.id)) {
                            phUuid = IdUtil.simpleUUID()
                        } else {
                            phUuid = photo.id
                        }

                        val file = FileEntity(
                            id = phUuid,
                            bizType = "problem_arch",
                            filePath = photo.key,
                            fileType = "photo",
                            clientPath = photo.path,
                            refObjId = if (TextUtils.isEmpty(deviceId)) uuid else deviceId,
                            orderNum = order,
                            projectId = projectId,
                            buildingId = buildingId,
                            floorId = floorId,
                            state = 0
                        )
                        repo.insFile(file)
                    }
                    order++
                }
            }
            mActivity.setResult(RESULT_OK)
            mActivity.finish()
        }
    }


    fun getRelConfig(entity: EquipmentEntity, deviceId: String, uuid: String) {
        viewModelScope.launch {
            val equipId = if (TextUtils.isEmpty(deviceId)) uuid else deviceId
            //获取关系二级列表
            val configParent = ArrayList<RelConfigEntityPackClass>()
            val relList = mutableListOf<List<RelationPackClass>>()
            val fromList = repo.getRelConfigByCodeFrom(entity.code!!)
            val toList = repo.getRelConfigByCodeTo(entity.code!!)
            loadCfgs(entity.code, fromList, true, configParent)
            loadCfgs(entity.code, toList, false, configParent)
            for (parent in configParent) {
                var rel = ArrayList<RelationPackClass>()
                for (child in parent.targetTypes) {
                    if (child.canFrom) {
                        val to = repo.getRelByCodeTo(
                            parent.entity.graphCode!!,
                            parent.entity.relCode!!,
                            equipId
                        )
                        loadRelation(to, child, parent, rel)
                    }

                    if (child.canTo) {
                        val from = repo.getRelByCodeFrom(
                            parent.entity.graphCode!!,
                            parent.entity.relCode!!,
                            equipId
                        )
                        loadRelation(from, child, parent, rel)
                    }
                }
                relList.add(rel)
            }
            val hashMap = HashMap<String, Any>()
            hashMap.put("parent", configParent)
            hashMap.put("child", relList)
            mEmitter.SendDirective(EQUIP_REL, hashMap)

            //获取管道二级列表
            val pipeParent = ArrayList<PipeConfigEntityPackClass>()
            val pipeChildren = mutableListOf<List<PipeEntity>>()
            val pipeFromList = repo.getPipeConfigByCodeFrom(entity.code)
            val pipeToList = repo.getPipeConfigByCodeTo(entity.code)
            loadPipeCfgs(entity.code, pipeFromList, true, pipeParent)
            loadPipeCfgs(entity.code, pipeToList, false, pipeParent)
            val floorPipes = repo.getPipeByFloorId(floorId)
            for (parent in pipeParent) {
                var pipes = ArrayList<PipeEntity>()
                for (floorPipe in floorPipes) {
                    if ((floorPipe.objFrom.equals(equipId) || floorPipe.objTo.equals(equipId)) && floorPipe.pipeType.equals(
                            parent.entity.pipeType
                        ) && floorPipe.pipeSubType.equals(parent.entity.pipeSubType)
                    ) {
                        bindPipeName(floorPipe)
                        pipes.add(floorPipe)
                    }
                }
                pipeChildren.add(pipes)
            }
            val hashMap1 = HashMap<String, Any>()
            hashMap1.put("parent", pipeParent)
            hashMap1.put("child", pipeChildren)
            mEmitter.SendDirective(PIPE, hashMap1)

        }
    }

    private suspend fun bindPipeName(floorPipe: PipeEntity) {
        val from = repo.getObject(floorPipe.objFrom!!)
        if (from == null) {
            floorPipe.objFromLocalId = "该设备"
        } else {
            floorPipe.objFromLocalId = ObjectNameUtils.getEquipName(from)
        }
        val to = repo.getObject(floorPipe.objTo!!)
        if (to == null) {
            floorPipe.objToLocalId = "该设备"
        } else {
            floorPipe.objToLocalId = ObjectNameUtils.getEquipName(to)
        }
    }

    private suspend fun loadPipeCfgs(
        srcCode: String,
        cfgs: List<PipeConfigEntity>,
        targetAsTo: Boolean,
        groups: ArrayList<PipeConfigEntityPackClass>
    ) {
        for (cfg in cfgs) {
            var group: PipeConfigEntityPackClass? = null
            for (g in groups) {
                if (g.samePipeConfig(cfg)) {
                    group = g
                    break
                }
            }
            if (group == null) {
                group = PipeConfigEntityPackClass()
                group.entity = cfg
                val equipment = repo.getEquipByCode(group.entity.pipeType!!)
                group.pipeTypeName = equipment?.name
                groups.add(group)
            }
            val cfgTarget = if (targetAsTo) cfg.classCodeTo else cfg.classCodeFrom

            var targetType: PipeConfigEntityPackClass.TargetPipeType? = null
            for (t in group.targetTypes) {
                if (t.targetCode.equals(cfgTarget)) {
                    targetType = t
                    break
                }
            }
            if (targetType == null) {
                targetType = PipeConfigEntityPackClass.TargetPipeType()
                targetType.srcCode = srcCode
                targetType.pipeType = group.entity.pipeType
                targetType.pipeSubType = group.entity.pipeSubType
                targetType.targetCode = cfgTarget
                group.targetTypes.add(targetType)
            }
            if (cfg.pipeDirection == 1) {
                if (targetAsTo) {
                    targetType.canTo = true
                } else {
                    targetType.canFrom = true
                }
            } else if (cfg.pipeDirection == -1) {
                if (targetAsTo) {
                    targetType.canFrom = true
                } else {
                    targetType.canTo = true
                }
            } else if (cfg.pipeDirection == 0) {
                targetType.canConnect = true
            }
        }
    }

    private suspend fun loadRelation(
        to: List<RelationEntity>,
        child: RelConfigEntityPackClass.TargetRelType,
        parent: RelConfigEntityPackClass,
        rel: ArrayList<RelationPackClass>
    ) {
        bindRelationEntityName(to)
        if (!CommonUtils.IsNull(to)) {
            for (t in to) {
                if (child.targetCode.equals(parent.entity.classCodeTo)) {
                    val pack = RelationPackClass()
                    pack.entity = t
                    pack.targetPipeType = child
                    rel.add(pack)
                }
            }
        }
    }

    private suspend fun bindRelationEntityName(
        entitys: List<RelationEntity>
    ) {
        for (rel in entitys) {
            val from = repo.getObject(rel.objFrom!!)
            if (from == null) {
                rel.objFromLocalId = "该设备"
            } else {
                rel.objFromLocalId = ObjectNameUtils.getEquipName(from)
            }
            val to = repo.getObject(rel.objTo!!)
            if (to == null) {
                rel.objToLocalId = "该设备"
            } else {
                rel.objToLocalId = ObjectNameUtils.getEquipName(to)
            }
        }
    }

    private fun loadCfgs(
        srcCode: String,
        cfgs: List<RelConfigEntity>,
        targetAsTo: Boolean,
        groups: ArrayList<RelConfigEntityPackClass>
    ) {
        for (cfg in cfgs) {
            var group: RelConfigEntityPackClass? = null
            for (g in groups) {
                if (g.sameRelConfig(cfg)) {
                    group = g
                    break
                }
            }
            if (group == null) {
                group = RelConfigEntityPackClass()
                group.entity = cfg
                groups.add(group)
            }
            val cfgTarget = if (targetAsTo) cfg.classCodeTo else cfg.classCodeFrom
            var tartgetType: RelConfigEntityPackClass.TargetRelType? = null
            for (t in group.targetTypes) {
                if (t.targetCode.equals(cfgTarget)) {
                    tartgetType = t
                    break
                }
            }
            if (tartgetType == null) {
                tartgetType = RelConfigEntityPackClass.TargetRelType()
                tartgetType.srcCode = srcCode
                tartgetType.graphCode = group.entity.graphCode
                tartgetType.relCode = group.entity.relCode
                tartgetType.targetCode = cfgTarget
                tartgetType.direction = cfg.direction
                group.targetTypes.add(tartgetType)
            }
            if (targetAsTo) {
                tartgetType.canTo = true
            } else {
                tartgetType.canFrom = true
            }

            if ("fixed".equals(cfg.direction)) {
                if (targetAsTo)
                    tartgetType.canTo = true
                else
                    tartgetType.canFrom = true
            } else if ("optional".equals(cfg.direction)) {
                tartgetType.canTo = true
                tartgetType.canFrom = true
            } else {
                tartgetType.connect = true
                // !targetCanBeTo && !targetCanBeFrom 表示无方向选择
            }

        }
    }

    fun getRelObj(group: RelConfigEntityPackClass, code: String) {
        viewModelScope.launch {
            val objects: ArrayList<ObjectEntity> = ArrayList()
            for (type in group.targetTypes) {
                val objs = repo.getObjectByCode(type.targetCode!!, buildingId)
                if (!CommonUtils.IsNull(objs)) {
                    objects.addAll(objs)
                }
            }
            mEmitter.SendDirective(REL_OBJECT, objects)
        }
    }

    /**
     * 获取可建立管道对象列表
     */
    fun getPipeObj(group: PipeConfigEntityPackClass, code: String) {
        viewModelScope.launch {
            val objects: ArrayList<ObjectEntity> = ArrayList()
            for (type in group.targetTypes) {
                val objs = repo.getObjectByCode(type.targetCode!!, buildingId)
                if (!CommonUtils.IsNull(objs)) {
                    objects.addAll(objs)
                }
            }
            mEmitter.SendDirective(PIPE_OBJECT, objects)
        }
    }

    /**
     * 增加关系
     */
    fun addRel(
        deviceId: String, uuid: String, equipEntity: EquipmentEntity,
        equip: ObjectEntity,
        groupRelConfigEntity: RelConfigEntity,
        eq: Boolean
    ) {
        viewModelScope.launch {
            var equipId: String
            if (TextUtils.isEmpty(deviceId)) {
                equipId = uuid
            } else {
                equipId = deviceId
            }
            val objFrom: String
            val objto: String
            if (eq) {
                objFrom = equipId
                objto = equip.id
            } else {
                objto = equipId
                objFrom = equip.id
            }

            val relEntity = RelationEntity(
                id = IdUtil.simpleUUID(),
                projectId = projectId,
                buildingId = buildingId,
                floorId = floorId,
                graphCode = groupRelConfigEntity.graphCode,
                relCode = groupRelConfigEntity.relCode,
                objFrom = objFrom,
                objTo = objto,
                valid = 1,
                state = 0
            )
            repo.insRelation(relEntity)
            getRelConfig(equipEntity, deviceId, uuid)
        }
    }

    /**
     * 增加管道
     */
    fun addPipe(
        deviceId: String,
        uuid: String,
        equipEntity: EquipmentEntity,
        equip: ObjectEntity,
        groupPipeConfigEntity: PipeConfigEntity,
        direction: String
    ) {
        viewModelScope.launch {
            var equipId: String
            if (TextUtils.isEmpty(deviceId)) {
                equipId = uuid
            } else {
                equipId = deviceId
            }
            val objFrom: String
            val objto: String
            if (groupPipeConfigEntity.classCodeFrom.equals(equipEntity!!.code)) {
                objFrom = equipId
                objto = equip.id
            } else {
                objto = equipId
                objFrom = equip.id
            }
            var pipeDirection: Int
            if (direction.equals("->")) {
                pipeDirection = 1
            } else if (direction.equals("<-")) {
                pipeDirection = -1
            } else {
                pipeDirection = 0
            }

            val relEntity = PipeEntity(
                id = IdUtil.simpleUUID(),
                projectId = projectId,
                buildingId = buildingId,
                floorId = floorId,
                objFrom = objFrom,
                objTo = objto,
                valid = 1,
                direction = pipeDirection,
                typeFrom = "equipment",
                typeTo = "equipment",
                pipeType = groupPipeConfigEntity.pipeType,
                pipeSubType = groupPipeConfigEntity.pipeSubType,
                state = 0
            )
            repo.insPipe(relEntity)
            getRelConfig(equipEntity, deviceId, uuid)
        }
    }

    /**
     * 删除关系
     */
    fun deleteRel(
        item: RelationEntity,
        equipEntity: EquipmentEntity?,
        deviceId: String,
        uuid: String
    ) {
        viewModelScope.launch {
            var rel = repo.getRelById(item.id)
            rel.valid = 0
            repo.insRelation(rel)
            getRelConfig(equipEntity!!, deviceId, uuid)
        }
    }

    /**
     * 删除管道
     */
    fun deletePipe(
        item: PipeEntity,
        equipEntity: EquipmentEntity?,
        deviceId: String,
        uuid: String
    ) {
        viewModelScope.launch {
            var rel = repo.getPipeById(item.id)
            rel.valid = 0
            repo.insPipe(rel)
            getRelConfig(equipEntity!!, deviceId, uuid)
        }
    }

    /**
     * 修改设备位置
     */
    fun insEquip(ob: ObjectEntity, position: Position) {
        viewModelScope.launch {
            var obj = repo.getObject(ob.id)
            val hashMap = gson.fromJson(obj!!.infos, HashMap::class.java) as HashMap<String, Any>
            val z = PointZ()
            z.x = position.x.toFloat()
            z.y = -position.y.toFloat()
            z.z = position.z.offset.toFloat()
            hashMap.put("location", z)
            hashMap.put("zRegion", position.z.region)
            obj.infos = gson.toJsonTree(hashMap)
            if (obj.state == 2) obj.state = 1
            repo.insObject(obj)
            mEmitter.SendDirective(UPDATE_LOCATION, null)
        }

    }

    /**
     * 获取设备服务区域
     */
    fun getServeArea(deviceId: String) {
        viewModelScope.launch {
            val entity = repo.getServeAreaByObjId(deviceId)
            if (entity == null) {
                mEmitter.SendDirective(SERVE_AREA, "无")
            } else {
                mEmitter.SendDirective(SERVE_AREA, "有")
            }
        }

    }

    /**
     * 获取无模型设备
     */
    fun getContainer(deviceId: String) {
        viewModelScope.launch {
            val objs = repo.getContainerObjs(deviceId)
            val equip = repo.getObject(deviceId)
            val configs = repo.getContainerConfig(equip!!.classCode!!)
            val hashMap = HashMap<String, Any>()
            hashMap.put("configs", configs)
            hashMap.put("objs", objs)
            mEmitter.SendDirective(CONTAINER_EQUIP, hashMap)
        }
    }

    fun getCompType(code: String) {
        viewModelScope.launch {
            val compTypes = repo.getCompType(code)
            mEmitter.SendDirective(COMP_TYPE, compTypes)
        }
    }

    companion object {

        //查询设备类
        const val EQUIP_CLASS = "EQUIP_CLASS"

        //设置空间的任务状态(新建和修改)
        const val SPACE_EQ = "SPACE_EQ"

        //查询空间详情查看info值
        const val SPACE_INFOS = "SPACE_INFOS"

        //查询设备详情
        const val EQUIP_INFO = "EQUIP_INFO"

        //设备对象实例
        const val Equip_INFOS = "Equip_INFOS"

        //关系二级列表
        const val EQUIP_REL = "EQUIP_REL"

        //可建立关系对象实例列表
        const val REL_OBJECT = "REL_OBJECT"

        //可建立管道对象实例列表
        const val PIPE_OBJECT = "PIPE_OBJECT"

        //管道二级列表
        const val PIPE = "PIPE"

        //编辑设备位置
        const val UPDATE_LOCATION = "UPDATE_LOCATION"

        //设备服务区域
        const val SERVE_AREA = "SERVE_AREA"

        //无模型设备
        const val CONTAINER_EQUIP = "CONTAINER_EQUIP"

        //构件类型
        const val COMP_TYPE = "COMP_TYPE"
    }

}