package app.furmeet.events.system.schedule.managers

import android.content.Context
import android.util.Log
import app.furmeet.events.system.helper.doAsync
import app.furmeet.events.system.helper.lastPathPart
import app.furmeet.events.system.helper.uiThread
import com.google.gson.GsonBuilder
//import org.jetbrains.anko.doAsync
//import org.jetbrains.anko.uiThread
import java.io.BufferedOutputStream
import java.io.PrintWriter
import java.io.Serializable
import java.io.StringWriter
import java.net.HttpURLConnection
import java.net.HttpURLConnection.HTTP_NOT_MODIFIED
import java.net.HttpURLConnection.HTTP_OK
import java.net.URL
import java.util.concurrent.Future
import java.util.zip.GZIPInputStream

class UpdateChecker(context: Context) : Serializable {
    val cacheManager = CacheManager(context)

    fun deleteGarbage() = cacheManager.removeGarbageFiles()

    fun downloadBaseJsonWithWatcher(watcher: (DownloadState, ByteArray?, Throwable?) -> Any?) =
            downloadWithWatcher(RemoteAssetsHolder.remoteAssets.json, watcher)

    fun downloadWithWatcher(httpThingToFetch: String, watcher: (DownloadState, ByteArray?, Throwable?) -> Any?) =
            if (cacheManager.goesOnline)
                doAsync {
                    try {
                        val url = URL(httpThingToFetch)
                        val diskIdentifier = url.toString().lastPathPart()
                        val connection = url.openConnection() as HttpURLConnection
                        cacheManager.getStamp(diskIdentifier)?.let {
                            connection.setRequestProperty("If-Modified-Since", it)
                        }
                        connection.useCaches = false
                        connection.connectTimeout = 3000
                        connection.readTimeout = 5000
                        connection.allowUserInteraction = false
                        connection.doOutput = false
                        connection.requestMethod = "GET"
                        connection.setRequestProperty("Accept-Encoding", "gzip")
                        connection.setRequestProperty("User-Agent", "Mozilla/5.0")
                        uiThread { watcher(DownloadState.CONNECTING, null, null) }
                        connection.connect()
                        try {
                            val resCode = connection.responseCode
                            when (resCode) {
                                HTTP_OK -> {
                                    uiThread { watcher(DownloadState.CACHE_MISS, null, null) }
                                    val lastModified = connection.headerFields["last-modified"]?.firstOrNull()
                                    val data = connection.inputStream.buffered().let {
                                        if (connection.headerFields["content-encoding"]?.firstOrNull() == "gzip")
                                            GZIPInputStream(it)
                                        else
                                            it
                                    }.readBytes()
                                    cacheManager[diskIdentifier] = Pair(data, lastModified ?: "")
                                    uiThread { watcher(DownloadState.SUCCESS, data, null) }
                                }
                                HTTP_NOT_MODIFIED -> {
                                    uiThread { watcher(DownloadState.CACHE_HIT, null, null) }
                                    val bytes = cacheManager[diskIdentifier]?.first?.readBytes()
                                    uiThread { watcher(DownloadState.SUCCESS, bytes, null) }
                                }
                                else -> {
                                    throw IllegalStateException("Server replied with code $resCode. Expected either $HTTP_OK or $HTTP_NOT_MODIFIED.")
                                }
                            }
                        } catch (t: Throwable) {
                            uiThread { watcher(DownloadState.ERROR, null, t) }
                        } finally {
                            connection.disconnect()
                        }
                    } catch (t: Throwable) {
                        uiThread { watcher(DownloadState.ERROR, null, t) }
                    }
                }
            else
                doAsync {
                    uiThread {
                        watcher(DownloadState.CONNECTING, null, null)
                        watcher(DownloadState.CACHE_HIT, null, null)
                        watcher(DownloadState.SUCCESS, cacheManager[httpThingToFetch.lastPathPart()]!!.first.readBytes(), null)
                    }
                }


    enum class DownloadState {
        CONNECTING,
        CACHE_MISS,
        CACHE_HIT,
        ERROR,
        SUCCESS
    }
}