package com.marvinelsen.willow.config

import javafx.beans.property.BooleanProperty
import javafx.beans.property.IntegerProperty
import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import java.util.Locale
import java.util.prefs.Preferences

class Config {
    companion object {
        private const val LOCALE_KEY = "locale"
        private const val THEME_KEY = "theme"
        private const val SCRIPT_KEY = "script"

        private val DEFAULT_THEME = Theme.SYSTEM
        private val DEFAULT_SCRIPT = Script.SIMPLIFIED
        private val DEFAULT_LOCALE = Locale.ENGLISH
    }

    private val preferences = Preferences.userNodeForPackage(this::class.java)
    val searchResults = SearchResultsConfig(preferences)
    val details = DetailsConfig(preferences)

    val locale: ObjectProperty<Locale> = SimpleObjectProperty(DEFAULT_LOCALE)
    val theme: ObjectProperty<Theme> = SimpleObjectProperty(DEFAULT_THEME)
    val script: ObjectProperty<Script> = SimpleObjectProperty(DEFAULT_SCRIPT)

    fun save() {
        preferences.put(LOCALE_KEY, locale.value.toLanguageTag())
        preferences.put(THEME_KEY, theme.value.name)
        preferences.put(SCRIPT_KEY, script.value.name)

        searchResults.save()
        details.save()

        preferences.flush()
    }

    fun load() {
        preferences.sync()

        theme.value = Theme.valueOf(
            preferences.get(
                THEME_KEY,
                DEFAULT_THEME.name
            )
        )

        script.value = Script.valueOf(
            preferences.get(
                SCRIPT_KEY,
                DEFAULT_SCRIPT.name
            )
        )

        locale.value = Locale.forLanguageTag(preferences.get(LOCALE_KEY, DEFAULT_LOCALE.toLanguageTag()))

        searchResults.load()
        details.load()
    }
}

class SearchResultsConfig(private val preferences: Preferences) {
    companion object {
        private const val PRONUNCIATION_KEY = "searchResultsPronunciation"
        private const val HEADWORD_FONT_SIZE_KEY = "searchResultsHeadwordFontSize"
        private const val PRONUNCIATION_FONT_SIZE_KEY = "searchResultsPronunciationFontSize"
        private const val DEFINITION_FONT_SIZE_KEY = "searchResultsDefinitionFontSize"
        private const val SHOULD_SHOW_PRONUNCIATION_KEY = "searchResultsShouldShowPronunciation"
        private const val SHOULD_SHOW_DEFINITION_KEY = "searchResultsShouldShowDefinition"

        private val DEFAULT_PRONUNCIATION = Pronunciation.PINYIN_WITH_TONE_MARKS
        private const val DEFAULT_HEADWORD_FONT_SIZE = 20
        private const val DEFAULT_PRONUNCIATION_FONT_SIZE = 14
        private const val DEFAULT_DEFINITION_FONT_SIZE = 14
        private const val DEFAULT_SHOULD_SHOW_PRONUNCIATION = true
        private const val DEFAULT_SHOULD_SHOW_DEFINITION = true
    }

    val pronunciation: ObjectProperty<Pronunciation> = SimpleObjectProperty(DEFAULT_PRONUNCIATION)
    val headwordFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_HEADWORD_FONT_SIZE)
    val pronunciationFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_PRONUNCIATION_FONT_SIZE)
    val definitionFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_DEFINITION_FONT_SIZE)
    val shouldShowPronunciation: BooleanProperty = SimpleBooleanProperty(DEFAULT_SHOULD_SHOW_PRONUNCIATION)
    val shouldShowDefinition: BooleanProperty = SimpleBooleanProperty(DEFAULT_SHOULD_SHOW_DEFINITION)

    fun save() {
        preferences.put(PRONUNCIATION_KEY, pronunciation.value.name)
        preferences.putInt(HEADWORD_FONT_SIZE_KEY, headwordFontSize.value)
        preferences.putInt(PRONUNCIATION_FONT_SIZE_KEY, pronunciationFontSize.value)
        preferences.putInt(DEFINITION_FONT_SIZE_KEY, definitionFontSize.value)
        preferences.putBoolean(SHOULD_SHOW_PRONUNCIATION_KEY, shouldShowPronunciation.value)
        preferences.putBoolean(SHOULD_SHOW_DEFINITION_KEY, shouldShowDefinition.value)
    }

    fun load() {
        headwordFontSize.value = preferences.getInt(
            HEADWORD_FONT_SIZE_KEY,
            DEFAULT_HEADWORD_FONT_SIZE
        )
        pronunciationFontSize.value = preferences.getInt(
            PRONUNCIATION_FONT_SIZE_KEY,
            DEFAULT_PRONUNCIATION_FONT_SIZE
        )
        definitionFontSize.value = preferences.getInt(
            DEFINITION_FONT_SIZE_KEY,
            DEFAULT_DEFINITION_FONT_SIZE
        )
        shouldShowPronunciation.value = preferences.getBoolean(
            SHOULD_SHOW_PRONUNCIATION_KEY,
            DEFAULT_SHOULD_SHOW_PRONUNCIATION
        )
        shouldShowDefinition.value = preferences.getBoolean(
            SHOULD_SHOW_DEFINITION_KEY,
            DEFAULT_SHOULD_SHOW_DEFINITION
        )
        pronunciation.value = Pronunciation.valueOf(
            preferences.get(
                PRONUNCIATION_KEY,
                DEFAULT_PRONUNCIATION.name
            )
        )
    }
}

class DetailsConfig(private val preferences: Preferences) {
    companion object {
        private const val PRONUNCIATION_KEY = "detailsPronunciation"
        private const val HEADWORD_FONT_SIZE_KEY = "detailsHeadwordFontSize"
        private const val PRONUNCIATION_FONT_SIZE_KEY = "detailsPronunciationFontSize"

        private val DEFAULT_PRONUNCIATION = Pronunciation.PINYIN_WITH_TONE_MARKS
        private const val DEFAULT_HEADWORD_FONT_SIZE = 50
        private const val DEFAULT_PRONUNCIATION_FONT_SIZE = 16
    }

    val pronunciation: ObjectProperty<Pronunciation> = SimpleObjectProperty(DEFAULT_PRONUNCIATION)
    val headwordFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_HEADWORD_FONT_SIZE)
    val pronunciationFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_PRONUNCIATION_FONT_SIZE)

    fun save() {
        preferences.put(PRONUNCIATION_KEY, pronunciation.value.name)
        preferences.putInt(HEADWORD_FONT_SIZE_KEY, headwordFontSize.value)
        preferences.putInt(PRONUNCIATION_FONT_SIZE_KEY, pronunciationFontSize.value)
    }

    fun load() {
        headwordFontSize.value = preferences.getInt(
            HEADWORD_FONT_SIZE_KEY,
            DEFAULT_HEADWORD_FONT_SIZE
        )
        pronunciationFontSize.value = preferences.getInt(
            PRONUNCIATION_FONT_SIZE_KEY,
            DEFAULT_PRONUNCIATION_FONT_SIZE
        )
        pronunciation.value = Pronunciation.valueOf(
            preferences.get(
                PRONUNCIATION_KEY,
                DEFAULT_PRONUNCIATION.name
            )
        )
    }
}
