# ObservationsTableDataSet.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 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.journal.sections.measurements.table.models.common

    import dk.nsp.mingraviditet.util.extensions.toMediumFormat
    import dk.nsp.mingraviditet.util.extensions.toTextModelOrDefault
    import dk.nsp.util.TextModel

    interface ObservationsTableDataSet {

        val fetusSources: List<ObservationsTableDataSource>?
        val motherSource: ObservationsTableDataSource?
        fun generateValueColumns(
            navigateToActivity: NavigateToActivityCallback,
            navigateToInfo: NavigateToInfoCallback
        ): List<List<JournalTableCellData>>

        val sources: List<ObservationsTableDataSource>
            get() = ((fetusSources ?: emptyList()) + motherSource).mapNotNull { it }

        val uniqueListOfSourceActivities: List<ObservationsTableActivity>
            get() = sources
                .flatMap { it.activities }
                .filterForUniqueActivities()
                .sortedBy { it.sortDate }

        fun toTableColumnSet(
            getDictionaryEntry: (TextModel) -> (() -> Unit)?,
            navigateToActivity: NavigateToActivityCallback,
            navigateToInfo: NavigateToInfoCallback
        ) =
            TableColumnSet(
                generateTitleColumn(getDictionaryEntry),
                generateValueColumns(navigateToActivity, navigateToInfo)
            )

        fun <T> generateValueColumns(
            fetuses: List<T>?,
            mother: T?,
            fetusDataTypes: List<ObservationTableDataType>,
            motherDataTypes: List<ObservationTableDataType>,
            navigateToActivity: NavigateToActivityCallback,
            navigateToInfo: NavigateToInfoCallback
        ): List<List<JournalTableCellData>> where T : ObservationsTableDataSource, T : ObservationTableMeasurementRowsSource {
            return uniqueListOfSourceActivities.map { activity ->
                val fetusesCellsForActivity = fetuses?.map { fetusSource ->

                    val fetusDataGenerator =
                        fetusSource.rows.dataForActivity(activity)
                            ?: ObservationTableCellDataGenerator.emptyInstance(
                                fetusDataTypes.first(),
                                activity
                            )

                    generateValueColumnFor(
                        activity,
                        fetusDataTypes,
                        fetusDataGenerator,
                        navigateToActivity,
                        navigateToInfo
                    )
                }?.flatten() ?: emptyList()

                val motherDataGenerator =
                    mother?.rows.dataForActivity(activity)
                        ?: ObservationTableCellDataGenerator.emptyInstance(
                            motherDataTypes.first(),
                            activity
                        )

                val motherCellsForActivity = generateValueColumnFor(
                    activity,
                    motherDataTypes,
                    motherDataGenerator,
                    navigateToActivity,
                    navigateToInfo
                )

                fetusesCellsForActivity + motherCellsForActivity
            }
        }

        private fun generateValueColumnFor(
            activity: ObservationsTableActivity,
            dataTypes: List<ObservationTableDataType>,
            cellDataGenerator: ObservationTableCellDataGenerator,
            navigateToActivity: NavigateToActivityCallback,
            navigateToInfo: NavigateToInfoCallback
        ): List<JournalTableCellData> {
            val formattedDateString = activity.date?.toMediumFormat()

            val dateCellData = JournalTableCellData(
                formattedDateString.toTextModelOrDefault(),
                DataTableCellType.BoldText
            )

            val valueCellsData = dataTypes.map { type ->
                cellDataGenerator.dataTableCellForType(
                    type,
                    activity.activityId,
                    navigateToActivity,
                    navigateToInfo
                )
            }

            return listOf(dateCellData) + valueCellsData
        }

        private fun generateTitleColumn(
            getDictionaryEntry: (TextModel) -> (() -> Unit)?
        ): List<JournalTableCellData> =
            sources.flatMap { source ->
                titleCellsForSource(source, getDictionaryEntry)
            }

        private fun titleCellsForSource(
            source: ObservationsTableDataSource,
            getDictionaryEntry: (TextModel) -> (() -> Unit)?
        ): List<JournalTableCellData> {
            val sourceTitleCellData = JournalTableCellData(
                source.title,
                DataTableCellType.BoldText
            )

            val sourceValueTitles: List<JournalTableCellData> =
                source.activityDataTypes.map { type ->
                    JournalTableCellData(
                        type.title,
                        DataTableCellType.toPotentialDictionaryType(
                            getDictionaryEntry(type.title),
                            type.defaultCellType
                        )
                    )
                }

            return listOf(sourceTitleCellData) + sourceValueTitles
        }

    }