# CareplanActivityObservationsPageViewModel.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2020 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.mingraviditet.ui.main.appointments.details.viewpager.observations

    import androidx.hilt.Assisted
    import androidx.lifecycle.*
    import com.commonsense.android.kotlin.base.extensions.collections.categorize
    import dagger.hilt.android.lifecycle.HiltViewModel
    import dk.nsp.domain.dictionary.DictionaryEntry
    import dk.nsp.domain.repository.DictionaryRepository
    import dk.nsp.mingraviditet.extensions.displayName
    import dk.nsp.mingraviditet.extensions.getMeasurementTextModel
    import dk.nsp.mingraviditet.ui.adapterviews.TableHeaderRow
    import dk.nsp.mingraviditet.ui.adapterviews.TableRow
    import dk.nsp.mingraviditet.ui.adapterviews.TableRowBindingModel
    import dk.nsp.mingraviditet.util.android.recycler.RecyclerAdapterView
    import dk.nsp.mingraviditet.util.extensions.isFetusObservation
    import dk.nsp.mingraviditet.util.extensions.toSingleEvent
    import dk.nsp.mingraviditet.util.livedata.SingleEvent
    import dk.nsp.models.Activity
    import dk.nsp.models.Observation
    import dk.nsp.util.TextModel
    import dk.nsp.util.toTextModel
    import kotlinx.coroutines.CoroutineScope
    import javax.inject.Inject
    import kotlin.collections.set

    @HiltViewModel
    class CareplanActivityObservationsPageViewModel @Inject constructor(
        private val dictionaryRepository: DictionaryRepository,
        @Assisted private val savedStateHandle: SavedStateHandle
    ) : ViewModel() {

        private val careplanActivity =
            savedStateHandle.get<Activity>(CareplanActivityObservationsPageFragment.careplanActivityBundleKey)

        private val _observationsMap: MutableLiveData<Map<String, List<Observation>>> =
            MutableLiveData()

        val rows: LiveData<List<RecyclerAdapterView<*>>> =
            MediatorLiveData<List<RecyclerAdapterView<*>>>().apply {
                addSource(_observationsMap) {
                    postValue(createRows(it))
                }
            }

        private val _onDictionaryRowClicked: MutableLiveData<SingleEvent<DictionaryEntry>> =
            MutableLiveData()
        val onDictionaryClicked: LiveData<SingleEvent<DictionaryEntry>> = _onDictionaryRowClicked

        init {
            careplanActivity?.observations?.let {
                val observationsMapSorted = sortObservationsIntoMap(it)

                _observationsMap.postValue(observationsMapSorted)
            }
        }

        private fun sortObservationsIntoMap(observations: List<Observation>): Map<String, List<Observation>> {
            // Split observations between fetus types and non-fetus types
            val fetusObservations = observations.filter { it.type?.isFetusObservation() == true }
            val motherObservations = observations.filter { it.type?.isFetusObservation() == false }

            // Put both observation sets into the same map
            val finalMap: MutableMap<String, List<Observation>> = mutableMapOf()
            finalMap.putAll(fetusObservations.categorize { it.fetus ?: "Ukendt" })
            finalMap["Mor"] = motherObservations

            return finalMap
        }

        private fun createRows(observationsMap: Map<String, List<Observation>>): List<RecyclerAdapterView<*>> {
            val rows: MutableList<RecyclerAdapterView<*>> = mutableListOf()

            observationsMap.keys.forEach { sectionKey ->
                rows.add(TableHeaderRow(sectionKey.toTextModel()))

                val measurements =
                    observationsMap[sectionKey]?.mapIndexed { sectionIndex, sectionItem ->
                        val titleModel = sectionItem.type.displayName
                        val measurements = sectionItem.getMeasurementTextModel()

                        TableRow(
                            TableRowBindingModel(
                                titleModel,
                                measurements,
                                sectionIndex,
                                dictionaryCallback(viewModelScope, titleModel)
                            )
                        )
                    } ?: emptyList()

                rows.addAll(measurements)

                observationsMap[sectionKey]?.maxByOrNull { it.dateOfMeasurement }?.let {
                    rows.add(
                        CareplanActivityTimestampRow(
                            it.measuredBy?.displayName,
                            it.dateOfMeasurement
                        )
                    )
                }
            }
            return rows
        }

        private fun dictionaryCallback(scope: CoroutineScope, key: TextModel): (() -> Unit)? {
            return dictionaryRepository.getDictionaryEntryFor(scope, key)
                ?.let { { _onDictionaryRowClicked.postValue(it.toSingleEvent()) } }
        }

    }