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 com.waytous.anticollision.utils.LATITUDE
import com.waytous.anticollision.utils.LONGITUDE
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()
    ).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("")
        }

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