package com.marvinelsen.willow.ui.cells

import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Pronunciation
import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.util.createContextMenuForEntry
import javafx.beans.binding.Bindings
import javafx.geometry.VPos
import javafx.scene.control.Label
import javafx.scene.control.ListCell
import javafx.scene.control.ListView
import javafx.scene.layout.FlowPane
import javafx.scene.layout.VBox
import javafx.util.Callback
import java.util.ResourceBundle

class DictionaryEntryCellFactory(private val resources: ResourceBundle, private val config: Config) :
    Callback<ListView<DictionaryEntryFx?>, ListCell<DictionaryEntryFx?>> {
    override fun call(listView: ListView<DictionaryEntryFx?>): ListCell<DictionaryEntryFx?> {
        val entryCell = EntryCell(resources, config)
        entryCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING))
        return entryCell
    }

    companion object {
        private const val CELL_PADDING = 16
    }
}

internal class EntryCell(private val resources: ResourceBundle, private val config: Config) :
    ListCell<DictionaryEntryFx?>() {
    private val labelHeadword = Label().apply {
        styleClass.add("headword")
        styleProperty().bind(
            Bindings.concat(
                "-fx-font-size: ",
                config.searchResults.headwordFontSize.asString(),
                "px;"
            )
        )
    }

    private val labelDefinition = Label().apply {
        styleClass.add("definition")
        styleProperty().bind(
            Bindings.concat(
                "-fx-font-size: ",
                config.searchResults.definitionFontSize.asString(),
                "px;"
            )
        )
        visibleProperty().bind(config.searchResults.shouldShowDefinition)
        managedProperty().bind(config.searchResults.shouldShowDefinition)
    }

    private val labelPronunciation = Label().apply {
        styleClass.add("pronunciation")
        styleProperty().bind(
            Bindings.concat(
                "-fx-font-size: ",
                config.searchResults.pronunciationFontSize.asString(),
                "px;"
            )
        )
        visibleProperty().bind(config.searchResults.shouldShowPronunciation)
        managedProperty().bind(config.searchResults.shouldShowPronunciation)
    }

    private val flowPane = FlowPane(labelHeadword, labelPronunciation).apply {
        hgap = FLOW_PANE_HGAP
        rowValignment = VPos.BASELINE
    }

    private val root = VBox(flowPane, labelDefinition).apply {
        styleClass.add("search-result")
    }

    init {
        text = null
    }

    override fun updateItem(entry: DictionaryEntryFx?, empty: Boolean) {
        super.updateItem(entry, empty)
        if (empty || entry == null) {
            graphic = null
            contextMenu = null
        } else {
            labelHeadword.textProperty().bind(
                Bindings.createStringBinding(
                    {
                        when (config.script.value!!) {
                            Script.SIMPLIFIED -> entry.simplifiedProperty.value
                            Script.TRADITIONAL -> entry.traditionalProperty.value
                        }
                    },
                    config.script
                )
            )
            labelPronunciation.textProperty().bind(
                Bindings.createStringBinding(
                    {
                        when (config.searchResults.pronunciation.value!!) {
                            Pronunciation.PINYIN_WITH_TONE_MARKS -> entry.pinyinWithToneMarksProperty.value
                            Pronunciation.PINYIN_WITH_TONE_NUMBERS -> entry.pinyinWithToneNumbersProperty.value
                            Pronunciation.ZHUYIN -> entry.zhuyinProperty.value
                        }
                    },
                    config.searchResults.pronunciation
                )
            )

            val definition = when {
                entry.cedictDefinitions.isNotEmpty() -> entry.cedictDefinitions.joinToString(
                    separator = " / "
                ) { it.joinToString(separator = "; ") }

                entry.crossStraitsDefinitions.isNotEmpty() -> entry.crossStraitsDefinitions.joinToString(
                    separator = " / "
                ) { it.definition }
                entry.moedictDefinitions.isNotEmpty() -> entry.moedictDefinitions.joinToString(
                    separator = " / "
                ) { it.definition }
                else -> error("No definition for entry")
            }
            labelDefinition.text = definition

            contextMenu = createContextMenuForEntry(entry, resources)
            graphic = root
        }
    }

    companion object {
        private const val FLOW_PANE_HGAP = 8.0
    }
}
