package com.waytous.anticollision.ui

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.CreationExtras
import com.blankj.utilcode.util.LogUtils
import com.mapbox.geojson.Point
import com.waytous.anticollision.repository.MainRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
import javax.inject.Inject

class MainViewModel @Inject constructor(private val mainRepository: MainRepository) : ViewModel() {

    val zoom: MutableLiveData<BigDecimal> by lazy {
        MutableLiveData(BigDecimal(16.0))
    }

    val center: MutableLiveData<Point> by lazy {
        MutableLiveData(Point.fromLngLat(LONGITUDE, LATITUDE))
    }

    fun obstaclesSource() =
        flowOf(mainRepository.localObstaclesVersion(), mainRepository.remoteObstaclesVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun electronicFenceSource() = flowOf(
        mainRepository.localElectronicFenceVersion(),
        mainRepository.remoteElectronicFenceVersion()
    ).flattenMerge()
        .transformWhile {
            emit(it.mapDataSource)
            !it.abort
        }.map {
            it?.geojson
        }.flowOn(Dispatchers.IO)
        .catch {
            LogUtils.e(it.message)
            emit("")
        }

    fun wetAreaSource() =
        flowOf(mainRepository.localWetAreaVersion(), mainRepository.remoteWetAreaVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun laneNodeSource() =
        flowOf(mainRepository.localLaneNodeVersion(), mainRepository.remoteLaneNodeVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun laneSource() = flowOf(mainRepository.localLaneVersion(), mainRepository.remoteLaneVersion())
        .flattenMerge()
        .transformWhile {
            emit(it.mapDataSource)
            !it.abort
        }.map {
            it?.geojson
        }.flowOn(Dispatchers.IO)
        .catch {
            LogUtils.e(it.message)
            emit("")
        }

    fun runnableAreaSource() = flowOf(
        mainRepository.localRunnableAreaVersion(),
        mainRepository.remoteRunnableAreaVersion()
    ).onStart {
        LogUtils.dTag("MainViewModel","runnableAreaSource start")
    }
        .flattenMerge()
        .transformWhile {
            emit(it.mapDataSource)
            !it.abort
        }.map {
            it?.geojson
        }.flowOn(Dispatchers.IO)
        .catch {
            LogUtils.e(it.message)
            emit("")
        }

    fun dumpAreaSource() =
        flowOf(mainRepository.localDumpAreaVersion(), mainRepository.remoteDumpAreaVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun diggingWorkAreaSource() = flowOf(
        mainRepository.localDiggingAreaVersion(),
        mainRepository.remoteDiggingWorkAreaVersion()
    ).flattenMerge()
        .transformWhile {
            emit(it.mapDataSource)
            !it.abort
        }.map {
            it?.geojson
        }.flowOn(Dispatchers.IO)
        .catch {
            LogUtils.e(it.message)
            emit("")
        }

    fun barricadeSource() =
        flowOf(mainRepository.localBarricadeVersion(), mainRepository.remoteBarricadeVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun stationAreaSource() =
        flowOf(mainRepository.localStationAreaVersion(), mainRepository.remoteStationAreaVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun parkSpotSource() =
        flowOf(mainRepository.localParkSpotVersion(), mainRepository.remoteParkSpotVersion())
            .flattenMerge()
            .transformWhile {
                emit(it.mapDataSource)
                !it.abort
            }.map {
                it?.geojson
            }.flowOn(Dispatchers.IO)
            .catch {
                LogUtils.e(it.message)
                emit("")
            }

    fun staticObjectsSource() = flowOf(
        mainRepository.localStaticObjectsVersion(),
        mainRepository.remoteStaticObjectsVersion()
    ).flattenMerge()
        .transformWhile {
            emit(it.mapDataSource)
            !it.abort
        }.map {
            it?.geojson
        }.flowOn(Dispatchers.IO)
        .catch {
            LogUtils.e(it.message)
            emit("")
        }

    companion object {

        const val MY_MAP_BOX_STYLE = "asset://style.json"

        const val DUMP_AREA_SOURCE = "dumpAreaSource"
        const val DUMP_AREA_LAYER = "dumpAreaLayer"
        const val DUMP_AREA_TEXT_LAYER = "dumpAreaTextLayer"

        const val OBSTACLES_SOURCE = "obstaclesSource"
        const val OBSTACLES_LAYER = "obstaclesLayer"
        const val OBSTACLES_TEXT_LAYER = "obstaclesTextLayer"

        const val ELECTRONIC_FENCE_SOURCE = "electronicFenceSource"
        const val ELECTRONIC_FENCE_LAYER = "electronicFenceLayer"
        const val ELECTRONIC_FENCE_TEXT_LAYER = "electronicFenceTextLayer"

        const val WET_AREA_SOURCE = "wetAreaSource"
        const val WET_AREA_LAYER = "wetAreaLayer"
        const val WET_AREA_TEXT_LAYER = "wetAreaTextLayer"

        const val LANE_NODE_SOURCE = "laneNodeSource"
        const val LANE_NODE_LAYER = "laneNodeLayer"
        const val LANE_NODE_TEXT_LAYER = "laneNodeTextLayer"

        const val LANE_SOURCE = "laneSource"
        const val LANE_LAYER = "laneLayer"
        const val LANE_TEXT_LAYER = "laneTextLayer"

        const val RUNNABLE_AREA_SOURCE = "runnableAreaSource"
        const val RUNNABLE_AREA_LAYER = "runnableAreaLayer"
        const val RUNNABLE_AREA_TEXT_LAYER = "runnableAreaTextLayer"

        const val DIGGING_WORK_AREA_SOURCE = "diggingWorkAreaSource"
        const val DIGGING_WORK_AREA_LAYER = "diggingWorkAreaLayer"
        const val DIGGING_WORK_AREA_TEXT_LAYER = "diggingWorkAreaTextLayer"

        const val BARRICADE_SOURCE = "barricadeSource"
        const val BARRICADE_LAYER = "barricadeLayer"
        const val BARRICADE_TEXT_LAYER = "barricadeTextLayer"

        const val STATION_AREA_SOURCE = "stationAreaSource"
        const val STATION_AREA_LAYER = "stationAreaLayer"
        const val STATION_AREA_TEXT_LAYER = "stationAreaTextLayer"

        const val STATIC_OBJECTS_SOURCE = "staticObjectsSource"
        const val STATIC_OBJECTS_LAYER = "staticObjectsLayer"
        const val STATIC_OBJECTS_TEXT_LAYER = "staticObjectsTextLayer"

        const val PARK_SPOT_SOURCE = "parkSpotSource"
        const val PARK_SPOT_LAYER = "parkSpotLayer"
        const val PARK_SPOT_TEXT_LAYER = "parkSpotTextLayer"

        const val LONGITUDE = 119.7622539014
        const val LATITUDE = 49.3795031825
        const val ZOOM = 16.0

    }

    class MainViewModelFactory(private val mainRepository: MainRepository) :
        ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            return MainViewModel(mainRepository) as T
        }
    }
}