package com.sybotan.android.demo.viewmodel

import android.app.Application
import android.graphics.PointF
import android.text.TextUtils
import androidx.lifecycle.viewModelScope
import cn.hutool.core.util.IdUtil
import cn.sagacloud.android.cadengine.items.TunableSpaceItem
import cn.sagacloud.android.cadengine.types.FloorData
import cn.sagacloud.android.cadengine.types.PointZ
import cn.sagacloud.android.cadengine.types.TunableSpace
import com.framework.mvvm.model.db.entity.task.InfosforBuild
import com.framework.mvvm.model.db.entity.task.ObjectEntity
import com.framework.mvvm.model.db.entity.task.RelationEntity
import com.framework.mvvm.model.db.entity.task.ServeAreaEntity
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.RelConfigEntityPackClass
import com.sybotan.android.demo.bean.RelationPackClass
import com.sybotan.android.demo.bean.SpaceInfo
import com.sybotan.android.demo.entitiies.Floor
import com.sybotan.android.demo.entitiies.Info
import com.sybotan.android.demo.entitiies.Statistics
import com.sybotan.android.demo.retrofit.BaseViewModelInterface
import com.sybotan.android.demo.tools.CommonUtils
import com.sybotan.android.demo.tools.ObjectNameUtils
import com.sybotan.android.demo.tools.ToastUtils
import com.sybotan.base.utils.SJsonUtil
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.io.File


/**
 * Created by lihao.
 * Date: 2021/6/9
 */
class ChooseSpaceVM(
    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()
    private val repo: AdmRepository by instance()


    fun getMap() {
        viewModelScope.launch {
            val floors = repo.getFloors()
            for (floor in floors) {
                if (floor.id == floorId) {
                    if (TextUtils.isEmpty(floor.floorMap)) {
                        ToastUtils.showMyToast("没有地图")
                        break
                    }
                    val mapFile = File("${floor.floorMap}")
                    val map = mapFile.bufferedReader().use { it.readText() }
                    val floorOfflineData = SJsonUtil.fromJson(map, FloorData::class.java)
                    mEmitter.SendDirective(MAP_LOAD, gson.toJson(floorOfflineData))
                    break
                }
            }

        }
    }


    fun insServeAreas(deviceId: String, spaces: ArrayList<TunableSpaceItem>) {
        viewModelScope.launch {
            val geom = ArrayList<ArrayList<ArrayList<PointZ>>>()
            for (space in spaces) {
                geom.addAll(space.data.outLine!!)
            }
            var entity = repo.getServeAreaByObjId(deviceId)
            if (entity == null) {
                entity = ServeAreaEntity(
                    id = IdUtil.simpleUUID(),
                    projectId = projectId,
                    buildingId = buildingId,
                    floorId = floorId,
                    state = 1,
                    valid = 1,
                    objId = deviceId,
                    geomType = "polygon",
                    geom = gson.toJsonTree(geom),
                    serveType = null, remark = null
                )
            } else {
                entity.geom = gson.toJsonTree(geom)
                entity.state = 1
            }
            repo.insServeArea(entity)
            mActivity.finish()
        }
    }

    fun getServeArea(deviceId: String) {
        viewModelScope.launch {
            val entity = repo.getServeAreaByObjId(deviceId)
            if (entity != null) {
                val array = entity.geom!!.asJsonArray
                if (array[0].isJsonObject) {
                    val ar = gson.fromJson<List<PointF>>(
                        gson.toJson(entity.geom),
                        object : TypeToken<List<PointF?>?>() {}.type
                    )
                    mEmitter.SendDirective(SERVE_AREA, ar)
                } else {
                    val ar = gson.fromJson<List<ArrayList<ArrayList<PointZ>>>>(
                        gson.toJson(entity.geom),
                        object : TypeToken<List<ArrayList<ArrayList<PointZ>>>>() {}.type
                    )
                    mEmitter.SendDirective(SERVE_AREAS, ar)
                }
            }
        }
    }


    fun getFloorList() {
        viewModelScope.launch {
            val floors = repo.getFloorsByBuildingId(buildingId)
            var floorList = ArrayList<Floor>()
            for (floor in floors) {
                val infos = Gson().fromJson(floor.infos, InfosforBuild::class.java)
                var floorMap = ""
                if (TextUtils.isEmpty(infos.floorMap)) {
                    floorMap = ""
                } else {
                    floorMap = infos.floorMap
                }
                var localId = ""
                if (TextUtils.isEmpty(floor.localId)) {
                    localId = ""
                } else {
                    localId = floor.localId!!
                }
                floorList.add(
                    Floor(
                        bimIdPre = "",
                        buildingId = floor.buildingId!!,
                        count = 0.0,
                        createTime = "",
                        floorSequenceID = infos.floorSequenceID,
                        id = floor.id,
                        lastUpdate = "",
                        localId = localId,
                        localName = floor.localName!!,
                        modelId = "",
                        name = floor.name ?: "",
                        objectType = "floor",
                        projectId = floor.projectId!!,
                        statistics = Statistics(),
                        infos = Info("", floorMap)
                    )
                )
            }
            floorList.sortByDescending { floor -> floor.floorSequenceID }
            mEmitter.SendDirective(FLOORS_LIST, floorList)
        }

    }

    fun getFloorMap(floorId: String) {
        viewModelScope.launch {
            val floors = repo.getFloors()
            for (floor in floors) {
                if (floor.id == floorId) {
                    if (TextUtils.isEmpty(floor.floorMap)) {
                        ToastUtils.showMyToast("没有地图")
                        break
                    }
                    val mapFile = File("${floor.floorMap}")
                    val map = mapFile.bufferedReader().use { it.readText() }
                    val floorOfflineData = SJsonUtil.fromJson(map, FloorData::class.java)
                    mEmitter.SendDirective(FLOOR_MAP, gson.toJson(floorOfflineData))
                    break
                }
            }

        }
    }

    fun insRel(
        deviceId: String,
        spaceIds: ArrayList<String>,
        groupRelConfigEntity: RelConfigEntityPackClass,
        direction: String
    ) {
        viewModelScope.launch {
            val rels = getRelations(groupRelConfigEntity, deviceId)
            for (rel in rels) {
                rel.entity.valid = 0
                repo.insRelation(rel.entity)
            }
            for (id in spaceIds) {
                val objFrom: String
                val objto: String
                if (direction == "->") {
                    objFrom = deviceId
                    objto = id
                } else {
                    objto = deviceId
                    objFrom = id
                }
                val en = repo.getRelByGraphCodeAndRelCodeAndObjFromAndObjTo(
                    groupRelConfigEntity.entity.graphCode!!,
                    groupRelConfigEntity.entity.relCode!!,
                    objFrom,
                    objto
                )
                if (en == null) {
                    val relEntity = RelationEntity(
                        id = IdUtil.simpleUUID(),
                        projectId = projectId,
                        buildingId = buildingId,
                        floorId = floorId,
                        graphCode = groupRelConfigEntity.entity.graphCode,
                        graphId = null,
                        groupCode = null,
                        relCode = groupRelConfigEntity.entity.relCode,
                        relValue = null,
                        objFrom = objFrom,
                        objTo = objto,
                        clientId = null,
                        clientDevice = null,
                        deliveryState = -1,
                        valid = 1,
                        d2mState = -1,
                        objFromLocalId = null,
                        objToLocalId = null,
                        state = 1
                    )
                    repo.insRelation(relEntity)
                }
            }
            mActivity.finish()
        }
    }

    fun getRel(deviceId: String, groupRelConfigEntity: RelConfigEntityPackClass) {
        viewModelScope.launch {
            val rels = getRelations(groupRelConfigEntity, deviceId)
            val spaces = ArrayList<TunableSpace>()
            val equips = ArrayList<ObjectEntity>()
            for (rel in rels) {
                val objEntity: ObjectEntity
                if (rel.entity.objFrom == deviceId) {
                    objEntity = repo.getObject(rel.entity.objTo!!)!!
                } else {
                    objEntity = repo.getObject(rel.entity.objFrom!!)!!
                }
                val obj = objEntity.infos!!.asJsonObject
                val info = gson.fromJson(obj, SpaceInfo::class.java)
                equips.add(objEntity)
//                spaces.add(info.outline[0])

                val tunableSpace = TunableSpace()
                tunableSpace.outLine = info.outline
                tunableSpace.name = ObjectNameUtils.getEquipName(objEntity)
                tunableSpace.id = objEntity.id
                val floor = repo.getFloorById(objEntity.floorId!!)
                tunableSpace.floorName = floor.localName
                tunableSpace.floorId = objEntity.floorId
                spaces.add(tunableSpace)
            }
            mEmitter.SendDirective(SERVE_AREAS, spaces)
            mEmitter.SendDirective(EQUIP_RELATION, rels)
        }
    }

    private suspend fun getRelations(
        groupRelConfigEntity: RelConfigEntityPackClass,
        deviceId: String
    ): ArrayList<RelationPackClass> {
        val rels = ArrayList<RelationPackClass>()
        for (child in groupRelConfigEntity.targetTypes) {
            if (child.canFrom) {
                val to = repo.getRelByCodeTo(
                    groupRelConfigEntity.entity.graphCode!!,
                    groupRelConfigEntity.entity.relCode!!,
                    deviceId
                )
                loadRelation(to, child, groupRelConfigEntity, rels)
            }

            if (child.canTo) {
                val from = repo.getRelByCodeFrom(
                    groupRelConfigEntity.entity.graphCode!!,
                    groupRelConfigEntity.entity.relCode!!,
                    deviceId
                )
                loadRelation(from, child, groupRelConfigEntity, rels)
            }
        }
        return rels
    }

    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)
            }
        }
    }

    fun getSpaces(floorId: String) {
        viewModelScope.launch {
            val spaces = repo.getSpaceByFloorId(floorId)
            mEmitter.SendDirective(SPACES, spaces)
        }
    }

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

    fun insRel(
        relationEntitys: ArrayList<RelationEntity>,
        groupRelConfigEntity: RelConfigEntityPackClass,
        deviceId: String
    ) {
        viewModelScope.launch {
            val rels = getRelations(groupRelConfigEntity, deviceId)
            for (rel in rels) {
                rel.entity.valid = 0
                repo.insRelation(rel.entity)
            }


            for (rel in relationEntitys) {
                repo.insRelation(rel)
            }
            mActivity.finish()
        }
    }


    companion object {
        const val MAP_LOAD = "MAP_LOAD"

        //服务区域
        const val SERVE_AREA = "SERVE_AREA"
        const val SERVE_AREAS = "SERVE_AREAS"
        const val FLOORS_LIST = "FLOORS_LIST"
        const val FLOOR_MAP = "FLOOR_MAP"
        const val SPACES = "SPACES"
        const val EQUIPS = "EQUIPS"
        const val EQUIP_RELATION = "EQUIP_RELATION"

    }
}