package net.pokeranalytics.android.util

import android.content.Context
import android.preference.PreferenceManager
import java.util.*

class Preferences {

	interface PreferenceKey {
		var identifier: String
	}

	enum class DBPatch(var key: String) : PreferenceKey {
		LONE_COMPUTABLE_RESULTS("loneComputableResult");

		override var identifier: String = ""
			get() {
				return "dbpatch." + this.key
			}
	}

    enum class Keys(override var identifier: String) : PreferenceKey {
        CURRENCY_CODE("CurrencyCode"),
        LOCALE_CODE("LocaleCode"),
        FIRST_LAUNCH("firstLaunch"),
	    STOP_SHOWING_DISCLAIMER("stopShowingDisclaimer"),
        ACTIVE_FILTER_ID("ActiveFilterId"),
        LATEST_PURCHASE("latestPurchase"),
        PATCH_BREAK("patchBreaks"),
        PATCH_TRANSACTION_TYPES_NAMES("patchTransactionTypesNames")
    }

    companion object {

        fun setString(key: PreferenceKey, value: String, context: Context) {
	        val preferences = PreferenceManager.getDefaultSharedPreferences(context)
	        val editor = preferences.edit()
	        editor.putString(key.identifier, value)
	        editor.apply()
        }

        private fun removeKey(key: Keys, context: Context) {
            val preferences = PreferenceManager.getDefaultSharedPreferences(context)
            val editor = preferences.edit()
            editor.remove(key.identifier)
            editor.apply()
        }

        fun getString(key: Keys, context: Context) : String? {
            val preferences = PreferenceManager.getDefaultSharedPreferences(context)
            return preferences.getString(key.identifier, null)
        }

	    fun setBoolean(key: PreferenceKey, value: Boolean, context: Context) {
		    val preferences = PreferenceManager.getDefaultSharedPreferences(context)
		    val editor = preferences.edit()
		    editor.putBoolean(key.identifier, value)
		    editor.apply()
	    }

	    fun getBoolean(key: PreferenceKey, context: Context, defaultValue: Boolean? = false) : Boolean {
		    val preferences = PreferenceManager.getDefaultSharedPreferences(context)
		    return preferences.getBoolean(key.identifier, defaultValue ?: false)
	    }

        fun setCurrencyCode(currencyCode: String, context: Context) {
            setString(Keys.CURRENCY_CODE, currencyCode, context)
            UserDefaults.setCurrencyValues(context)
        }

        fun setActiveFilterId(filterId: String, context: Context) {
            setString(Keys.ACTIVE_FILTER_ID, filterId, context)
        }

        fun getActiveFilterId(context: Context) : String? {
            return getString(Keys.ACTIVE_FILTER_ID, context)
        }

        fun removeActiveFilterId(context: Context) {
            removeKey(Keys.ACTIVE_FILTER_ID, context)
        }

        private fun getCurrencyCode(context: Context) : String? {
            return getString(Keys.CURRENCY_CODE, context)
        }

        private fun getCurrencyLocale(context : Context) : Locale? {
            getCurrencyCode(context)?.let { currencyCode ->
                Locale.getAvailableLocales().filter {
                    try {
                        Currency.getInstance(it).currencyCode == currencyCode
                    } catch (e: Exception) {
                        false
                    }
                }.firstOrNull()?.let {
                    return it
                }
            }
            return null
        }

        fun getDefaultCurrency(context: Context) : Currency? {
            getCurrencyLocale(context)?.let {
                return Currency.getInstance(it)
            }
            return null
        }

	    fun setStopShowingDisclaimer(context: Context) {
		    setBoolean(Keys.STOP_SHOWING_DISCLAIMER, true, context)
	    }

	    fun shouldShowDisclaimer(context: Context) : Boolean {
		    return !getBoolean(Keys.STOP_SHOWING_DISCLAIMER, context)
	    }

        fun executeOnce(key: Keys, context: Context, executable: () -> Unit) {
            if (!getBoolean(key, context)) {
                executable.invoke()
                setBoolean(key, true, context)
            }
        }

    }

}

class UserDefaults private constructor(context: Context) {
    init {
        setCurrencyValues(context)
    }

    companion object : SingletonHolder<UserDefaults, Context>(::UserDefaults) {
        lateinit var currency : Currency

        fun setCurrencyValues(context: Context) {
            currency = Preferences.getDefaultCurrency(context) ?: getLocaleCurrency()
        }

        /**
         * Return the locale currency, or en_US if there
         */
        fun getLocaleCurrency() : Currency {
            return try {
                Currency.getInstance(Locale.getDefault())
            } catch (ex: Exception) {
                when (Locale.getDefault().language) {
                    "en" -> Currency.getInstance(Locale("en", "US"))
                    "fr" -> Currency.getInstance(Locale("fr", "FR"))
                    "es" -> Currency.getInstance(Locale("es", "ES"))
                    "de" -> Currency.getInstance(Locale("de", "DE"))
                    "ja" -> Currency.getInstance(Locale("ja", "JP"))
                    "zh" -> Currency.getInstance(Locale("zh", "CN"))
                    else -> Currency.getInstance(Locale("en", "US"))
                }
            }
        }
    }
}


open class SingletonHolder<out T, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator
    @Volatile private var instance: T? = null

    fun init(context: A): T {
        val i = instance
        if (i != null) {
            return i
        }

        return synchronized(this) {
            val i2 = instance
            if (i2 != null) {
                i2
            } else {
                val created = creator!!(context)
                instance = created
                creator = null
                created
            }
        }
    }
}