package it.danieleverducci.openweddingapp.utils

import android.content.Context
import it.danieleverducci.openweddingapp.R
import it.danieleverducci.openweddingapp.entities.*
import org.json.JSONArray
import org.json.JSONObject

class SharedPreferencesManager {
    companion object {

        // Other keys
        private const val SP_NAME = "der_sp"
        private const val TOKEN_SP_KEY = "token"
        private const val REMOTE_SETTINGS_SP_KEY = "remote_settings"
        private const val LOCATION_SP_KEY = "location"
        private const val PRESENCE_SP_KEY = "presence"
        private const val PLACES_SP_KEY = "places"
        private const val GALLERY_CACHE_SP_KEY = "gallery_cache"
        private const val LOCAL_SETTINGS_LASTNOTCHECK_SP_KEY = "ls_last_notifications_check"
        private const val LOCAL_SETTINGS_NOTIFENABLED_SP_KEY = "ls_notifications_enabled"
        private const val LOCAL_SETTINGS_FIRSTRUN_SP_KEY = "ls_first_run"
        private const val LOCAL_SETTINGS_PLACESHINTDISPLAYED_SP_KEY = "places_hint_displayed"

        fun clear(ctx: Context) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            sharedPref.edit().clear().commit()
        }

        fun saveToken(ctx: Context, usr: Token) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(TOKEN_SP_KEY, usr.toJson().toString())
                commit()
            }
        }

        fun loadToken(ctx: Context): Token? {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            val usrString = sharedPref.getString(TOKEN_SP_KEY, null)
            if (usrString == null)
                return null
            else
                return Token(JSONObject(usrString))
        }

        fun deleteToken(ctx: Context) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                remove(TOKEN_SP_KEY)
                commit()
            }
        }

        fun saveLocation(ctx: Context, l: Location) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(LOCATION_SP_KEY, l.toJson().toString())
                commit()
            }
        }

        /**
         * Load cached location from shared preferences.
         * If no cached location is found, returns default location defined in default_json_location resource.
         */
        fun loadLocation(ctx: Context): Location {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            var locString = sharedPref.getString(LOCATION_SP_KEY, null)
            if (locString == null) {
                // Load default location from resources
                locString = ctx.resources.openRawResource(R.raw.default_json_location)
                    .bufferedReader().use { it.readText() }
            }
            return Location(JSONObject(locString))
        }

        /**
         * Saves generic content
         */
        fun saveGenericDynamicContent(ctx: Context, gdc: GenericDynamicContent, name: String) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(name, gdc.toJson().toString())
                commit()
            }
        }

        /**
         * Load cached wg from shared preferences.
         * If no cached wg is found, returns default one defined in provided raw resource.
         * @param ctx Context
         * @param name Shared preference key to load the content
         * @param default Raw resource to load default content
         */
        fun loadGenericDynamicContent(ctx: Context, name: String, default: Int): GenericDynamicContent {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            var gdcString = sharedPref.getString(name, null)
            if (gdcString == null) {
                // Load default content from resources
                gdcString = ctx.resources.openRawResource(default)
                    .bufferedReader().use { it.readText() }
            }
            return GenericDynamicContent(JSONObject(gdcString))
        }

        fun savePresence(ctx: Context, p: Presence) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(PRESENCE_SP_KEY, p.toJson().toString())
                commit()
            }
        }

        /**
         * Load presence from shared preferences.
         * If the user didn't aswer yet, returns null.
         */
        fun loadPresence(ctx: Context): Presence? {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            val pString = sharedPref.getString(PRESENCE_SP_KEY, null)
            if (pString == null)
                return null
            return Presence(JSONObject(pString))
        }


        /**
         * Load cached settings from shared preferences.
         * If no cached settings is found, returns default settings defined in default_json_remote_settings resource.
         */
        fun loadRemoteSettings(ctx: Context): RemoteSettings {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            var sString = sharedPref.getString(REMOTE_SETTINGS_SP_KEY, null)
            if (sString == null) {
                // Load default settings from resources
                sString = ctx.resources.openRawResource(R.raw.default_json_remote_settings)
                    .bufferedReader().use { it.readText() }
            }
            return RemoteSettings(JSONObject(sString))
        }

        fun saveRemoteSettings(ctx: Context, rs: RemoteSettings) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(REMOTE_SETTINGS_SP_KEY, rs.toJson().toString())
                commit()
            }
        }

        /**
         * Last notification check timestamp.
         */
        fun loadLocalSettingsLastNotificationCheck(ctx: Context): Long {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            return sharedPref.getLong(LOCAL_SETTINGS_LASTNOTCHECK_SP_KEY, 0)
        }

        fun saveLocalSettingsLastNotificationCheck(ctx: Context, lastNotificationCheck: Long) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putLong(LOCAL_SETTINGS_LASTNOTCHECK_SP_KEY, lastNotificationCheck)
                commit()
            }
        }

        /**
         * Determine if the places fragment hint has already been displayed
         */
        fun hasPlacesHintBeenDisplayed(ctx: Context): Boolean {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            val displayed = sharedPref.getBoolean(LOCAL_SETTINGS_PLACESHINTDISPLAYED_SP_KEY, false)
            if (!displayed) {
                with (sharedPref.edit()) {
                    putBoolean(LOCAL_SETTINGS_PLACESHINTDISPLAYED_SP_KEY, true)
                    commit()
                }
            }
            return displayed
        }

        /**
         * Determines if it is first app run
         */
        fun isFirstAppRun(ctx: Context): Boolean {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            val firstRun = sharedPref.getBoolean(LOCAL_SETTINGS_FIRSTRUN_SP_KEY, true)
            if (firstRun) {
                // Update value
                with(sharedPref.edit()) {
                    putBoolean(LOCAL_SETTINGS_FIRSTRUN_SP_KEY, false)
                    commit()
                }
            }
            return firstRun
        }

        /**
         * Caches gallery first page request, in case of no connection.
         */
        fun saveGalleryCache(ctx: Context, gi: ArrayList<GalleryItem>) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                val jGiArr = JSONArray()
                for (i in 0 until gi.size) {
                    jGiArr.put(gi.get(i).toJson())
                }
                putString(GALLERY_CACHE_SP_KEY, jGiArr.toString())
                commit()
            }
        }

        /**
         * Load cached gallery first page.
         * If no cached gallery first page is found, returns NULL.
         */
        fun loadGalleryCache(ctx: Context): ArrayList<GalleryItem>? {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            var giString = sharedPref.getString(GALLERY_CACHE_SP_KEY, null)
            if (giString == null)
                return null
            val jGiArr = JSONArray(giString)
            val gi = ArrayList<GalleryItem>(jGiArr.length())
            for (i in 0 until jGiArr.length()) {
                gi.add(GalleryItem(jGiArr.getJSONObject(i)))
            }
            return gi
        }

        /* Save places info */
        fun savePlaces(ctx: Context, p: JSONObject) {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            with (sharedPref.edit()) {
                putString(PLACES_SP_KEY, p.toString())
                commit()
            }
        }

        /**
         * Load cached location from shared preferences.
         * If no cached location is found, returns default location defined in default_json_location resource.
         */
        fun loadPlaces(ctx: Context): JSONObject {
            val sharedPref = ctx.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
            var placesString = sharedPref.getString(PLACES_SP_KEY, null)
            if (placesString == null) {
                // Load default location from resources
                placesString = ctx.resources.openRawResource(R.raw.default_json_places)
                    .bufferedReader().use { it.readText() }
            }
            return JSONObject(placesString)
        }

    }
}