package llc.arma.ble.app.ui.screen.inspection.accelerometer

import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.mapper.BleViewMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.RealtimeViewMode
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import llc.arma.ble.domain.usecase.WriteBle
import javax.inject.Inject

@HiltViewModel
class AccelerometerViewModel @Inject constructor(
    private val bleMapper: BleMapper,
    private val bleViewMapper: BleViewMapper,
    private val writeBle: WriteBle
) : BaseViewModel<AccelerometerContract.State, AccelerometerContract.Event, AccelerometerContract.Effect>() {

    override fun setInitialState() = AccelerometerContract.State.Loading

    override fun handleEvents(event: AccelerometerContract.Event) {
        when(event){
            is AccelerometerContract.Event.OnBleChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHideAccelerometerAccel -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnShowAccelerometerAccel -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnPowerChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnPowerEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnShowWriteBlePreview -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHideWriteBlePreview -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnWriteBle -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHideAccelerometerSpectre -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnAccelViewModeEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnAccelViewModelChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftFrequencyEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftAxisChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftFrequencyChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftModeChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftAxisEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnFftModeEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnSaveHistoryChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHideAccelerometerHistory -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnShowAccelerometerHistory -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnChangePassword -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnSaveIntervalEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnAccelScaleChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnAccelScaleEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnAccelEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnRealtimeViewModeEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnRealtimeViewModeChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHistoryEdit -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHistoryScaleChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHistoryViewModeChanged -> reduce(viewState.value, event)
            is AccelerometerContract.Event.OnHideHistoryEdit -> reduce(viewState.value, event)
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHideHistoryEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.HideHistoryEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHistoryViewModeChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            var saveHistory = state.accelerometer.accelerometerState.saveHistory

            if(saveHistory is Ble.Accelerometer.HistorySettings.Enabled){
                saveHistory = Ble.Accelerometer.HistorySettings.Enabled(
                    mode = event.mode,
                    scale = saveHistory.scale,
                    detailed = saveHistory.detailed
                )
            }

            state.accelerometer.accelerometerState.saveHistory = saveHistory

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHistoryScaleChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            var saveHistory = state.accelerometer.accelerometerState.saveHistory

            if(saveHistory is Ble.Accelerometer.HistorySettings.Enabled){
                saveHistory = saveHistory.copy(scale = event.scale)
            }


            state.accelerometer.accelerometerState.saveHistory = saveHistory

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHistoryEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowHistoryEdit
        }

    }


    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnAccelScaleChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {
                state.copy(
                    accelScale = event.scale
                )
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnAccelScaleEdit
    ) {
        setEffect {
            AccelerometerContract.Effect.ShowAccelScaleEdit(
                event.next
            )
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnSaveIntervalEdit
    ) {
        setEffect {
            AccelerometerContract.Effect.ShowIntervalPicker
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnSaveIntervalChanged
    ) {
        if(state is AccelerometerContract.State.Display) {

            state.accelerometer.accelerometerState.historyInterval = event.interval

        }

        setEffect {
            AccelerometerContract.Effect.HideIntervalPicker
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnChangePassword
    ) {
        setEffect {
            AccelerometerContract.Effect.Navigation.NavigateToChangePassword
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnSaveHistoryChanged
    ) {

        if(state is AccelerometerContract.State.Display) {

            if(event.save){

                state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.HistorySettings.Enabled(
                    scale = AccelScale.S_2,
                    mode = AccelViewMode.ACCELERATION,
                    detailed = true
                )

                setEffect {
                    AccelerometerContract.Effect.ShowHistoryEdit
                }

            } else {

                state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.HistorySettings.Disabled

            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftModeEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowFftModeEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftAxisEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowFftAxisEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftAxisChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {
                state.copy(
                    fftAxis = event.axis
                )
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftModeChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {
                state.copy(
                    fftViewMode = event.mode
                )
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftFrequencyChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {
                state.copy(
                    fftFrequency = event.frequency
                )
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnFftFrequencyEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowFftFrequencyEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnAccelEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowAccelEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnRealtimeViewModeEdit
    ) {

        setEffect {
            AccelerometerContract.Effect.ShowRealtimeViewModeEdit
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnRealtimeViewModeChanged
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {

                state.copy(
                    accelRealtimeViewMode = event.mode
                )

            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHideAccelerometerSpectre
    ) {



    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnShowAccelerometerHistory
    ) {

        /*setEffect {
            AccelerometerContract.Effect.ShowAccelerometerHistory
        }*/


        if (state is AccelerometerContract.State.Display &&
            state.origin.accelerometerState.saveHistorySettings is Ble.Accelerometer.HistorySettings.Enabled
        ) {
            setEffect {

                AccelerometerContract.Effect.Navigation.NavigateToAccelHistory(
                    ble = state.accelerometer.info,
                    accelMode = state.origin.accelerometerState.saveHistorySettings.mode,
                    fftAxis = state.fftAxis,
                    fftMode = state.fftViewMode,
                    frequency = state.fftFrequency,
                    accelScale = state.accelScale
                )
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHideAccelerometerHistory
    ) {



    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHideWriteBlePreview
    ) {

        if(state is AccelerometerContract.State.Display){

            setState {
                state.copy(
                    writeState = null
                )
            }

        }

        setEffect {
            AccelerometerContract.Effect.HideWriteBle
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnShowWriteBlePreview
    ) {

        if(state is AccelerometerContract.State.Display){

            val newBle = bleViewMapper.map(state.accelerometer) as Ble.Accelerometer

            val writeRequest = Ble.Accelerometer.WriteRequest(
                tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx,
                saveHistorySettings = if(newBle.accelerometerState.saveHistorySettings == state.origin.accelerometerState.saveHistorySettings) null else newBle.accelerometerState.saveHistorySettings,
                historyInterval = if(newBle.accelerometerState.historyInterval == state.origin.accelerometerState.historyInterval) null else newBle.accelerometerState.historyInterval,
                )

            setState {
                state.copy(
                    writeState = AccelerometerContract.State.Display.WriteState.DisplayPreview(
                        writeRequest
                    )
                )
            }

            setEffect {
                AccelerometerContract.Effect.ShowWriteBle
            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnPowerChanged
    ) {

        if(state is AccelerometerContract.State.Display) {

            state.accelerometer.state.tx = event.tx

        }

        setEffect {
            AccelerometerContract.Effect.HidePowerPicker
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnAccelViewModelChanged
    ) {

        if(state is AccelerometerContract.State.Display) {

            setState {
                state.copy(
                    accelViewMode = event.mode
                )
            }

        }

        setEffect {
            AccelerometerContract.Effect.HidePowerPicker
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnPowerEdit
    ) {
        setEffect {
            AccelerometerContract.Effect.ShowPowerPicker
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnAccelViewModeEdit
    ) {
        setEffect {
            AccelerometerContract.Effect.ShowAccelViewEdit(event.next)
        }
    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnHideAccelerometerAccel
    ) {



    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnShowAccelerometerAccel
    ) {

        viewModelScope.launch {

            if(state is AccelerometerContract.State.Display){

                /*setEffect {

                    AccelerometerContract.Effect.ShowAccelerometerAccel

                }*/

                setEffect {

                    AccelerometerContract.Effect.HideAccelEdit

                }

                setEffect {

                    when (state.accelRealtimeViewMode) {
                        is RealtimeViewMode.Accel -> {

                            AccelerometerContract.Effect.Navigation.NavigateToAccelRealtime(
                                ble = state.accelerometer.info,
                                accelMode = state.accelRealtimeViewMode.accelViewMode,
                                fftAxis = state.fftAxis,
                                fftMode = state.fftViewMode,
                                frequency = state.fftFrequency,
                                accelScale = state.accelScale
                            )

                        }

                        is RealtimeViewMode.Spectre -> {

                            AccelerometerContract.Effect.Navigation.NavigateToAccelSpectre(
                                ble = state.accelerometer.info,
                                accelMode = state.accelViewMode,
                                fftAxis = state.fftAxis,
                                fftMode = state.fftViewMode,
                                frequency = state.fftFrequency,
                                accelScale = state.accelScale
                            )

                        }
                    }

                }

            }

        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnBleChanged
    ) {

        when (state) {
            is AccelerometerContract.State.Display -> setState {
                state.copy(
                    origin = Ble.Accelerometer(
                        info = event.ble.info,
                        state = event.ble.state,
                        accelerometerState = state.origin.accelerometerState
                    )
                )
            }

            is AccelerometerContract.State.Loading -> setState {
                AccelerometerContract.State.Display(
                    origin = event.ble,
                    accelerometer = bleMapper.map(event.ble) as BleView.Accelerometer,
                    writeState = null,
                    accelViewMode = AccelViewMode.ACCELERATION,
                    accelRealtimeViewMode = RealtimeViewMode.Accel(AccelViewMode.ACCELERATION),
                    fftAxis = FftAxis.AUTO,
                    fftFrequency = FftFrequency.F_400,
                    fftViewMode = FftViewMode.SPECTRE,
                    accelScale = AccelScale.S_2
                )
            }
        }

    }

    private fun reduce(
        state: AccelerometerContract.State,
        event: AccelerometerContract.Event.OnWriteBle
    ) {

        if(state is AccelerometerContract.State.Display){

            state.writeState?.let { request ->

                if(request is AccelerometerContract.State.Display.WriteState.DisplayPreview) {

                    viewModelScope.launch {

                        setState {
                            state.copy(
                                writeState = AccelerometerContract.State.Display.WriteState.Writing(
                                    request.writeRequest
                                )
                            )
                        }

                        writeBle(state.accelerometer.info.serial, request.writeRequest).fold(
                            onSuccess = {

                                val currentState = viewState.value

                                if(currentState is AccelerometerContract.State.Display) {

                                    val newBleObject = Ble.Accelerometer(
                                        info = currentState.origin.info,
                                        state = currentState.origin.state.copy(
                                            tx = request.writeRequest.tx ?: state.origin.state.tx
                                        ),
                                        accelerometerState = currentState.origin.accelerometerState.copy(
                                            saveHistorySettings = request.writeRequest.saveHistorySettings
                                                ?: currentState.origin.accelerometerState.saveHistorySettings
                                        )
                                    )

                                    setState {
                                        currentState.copy(
                                            origin = newBleObject,
                                            writeState = AccelerometerContract.State.Display.WriteState.Success
                                        )
                                    }

                                }

                            },
                            onFailure = {
                                setState {
                                    state.copy(
                                        writeState = AccelerometerContract.State.Display.WriteState.Failure
                                    )
                                }
                            }
                        )

                    }

                }

            }

        }

    }

}