package com.ejada.hmg.hmgwifi

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.*
import android.net.wifi.*
import android.os.Build
import android.os.PatternMatcher
import android.provider.Settings
import android.util.Log
import androidx.annotation.RequiresApi
import com.ejada.hmg.MainActivity
import com.ejada.hmg.utils.HMGUtils





class HMG_Guest(private var context: MainActivity, ssid: String) {
    private val TAG = "HMG_Guest"
    private val TEST = false
    private var SSID = ssid
//    private var SSID = "HMG-MOHEMM"

    val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?

    private lateinit var completionListener: ((status: Boolean, message: String) -> Unit)

    fun completionOnUiThread(status: Boolean, message: String){
        completionListener(status, message)
    }

    fun enableWifi(){
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            wifiManager?.setWifiEnabled(true)
            HMGUtils.popFlutterText(context,"enablingWifi");
            HMGUtils.timer(2000,false){
                connectApiLessThen29()
            }
        }else {
            val panelIntent = Intent(Settings.Panel.ACTION_WIFI)
            context.startActivityForResult(panelIntent, 1)
        }
    }
    /*
	 * Helpful:
	 * http://stackoverflow.com/questions/8818290/how-to-connect-to-a-specific-wifi-network-in-android-programmatically
	 */
    fun connectToHMGGuestNetwork(completion: (status: Boolean, message: String) -> Unit) {
        completionListener = completion
        wifiManager?.let { wm ->
            if (!wm.isWifiEnabled){
                enableWifi()
            }else{
                connectWifi()
            }
        }

    }

    private fun errorConnecting(){
        completionOnUiThread(false, "errorConnectingHmgNetwork")
    }

    fun connectWifi(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            connectApiGreaterThen28()
        }else {
            connectApiLessThen29()
        }
    }

    // I            }else{f CompileSDK is greater and equals to APILevel 29
    @RequiresApi(Build.VERSION_CODES.Q)
    private fun connectApiGreaterThen28(){
        Log.e(TAG, "connection wifi with Android Q+")

        val networkRequest: NetworkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //removeCapability added for hotspots without internet
            .setNetworkSpecifier(
                WifiNetworkSpecifier.Builder()
                    .setSsid(SSID)
                    .build()

            ).build()

        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                connectivityManager.bindProcessToNetwork(network)
                HMGUtils.timer(2000,false){
                    completionListener(true, "Success")
                }
                Log.e(TAG, "onAvailable")
            }

            override fun onLosing(network: Network, maxMsToLive: Int) {
                super.onLosing(network, maxMsToLive)
                Log.e(TAG, "onLosing")
                completionListener(false, "fail")
            }

            override fun onLost(network: Network) {
                super.onLost(network)
                Log.e(TAG, "onLosing")
                Log.e(TAG, "losing active connection")
                completionListener(false, "fail")
            }

            override fun onUnavailable() {
                super.onUnavailable()
                Log.e(TAG, "onUnavailable")
                completionListener(false, "fail")
            }

        }

        //timeout add because "No devices found" wasn't handled correct and doesn't throw Unavailable
        connectivityManager.requestNetwork(networkRequest, networkCallback, 30000)
    }



    fun connectApiLessThen29(){
        val wifi = WifiConfiguration()
        wifi.SSID = """"$SSID""""
        wifi.status = WifiConfiguration.Status.ENABLED
        wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)

        wifi.networkId = ssidToNetworkId(wifi.SSID)
        if (wifi.networkId == -1) {
            wifiManager?.addNetwork(wifi)
        } else {
            Log.v(TAG, "WiFi found - updating it.\n")
            wifiManager?.updateNetwork(wifi)
        }

        Log.v(TAG, "saving config.\n")
        wifiManager?.saveConfiguration()

        wifi.networkId = ssidToNetworkId(wifi.SSID)

        Log.v(TAG, "wifi ID in device = " + wifi.networkId)

        var supState: SupplicantState
        val networkIdToConnect = wifi.networkId
        if (networkIdToConnect >= 0) {
            Log.v(TAG, "Start connecting...\n")

            // We disable the network before connecting, because if this was the last connection before
            // a disconnect(), this will not reconnect.
            wifiManager?.disableNetwork(networkIdToConnect)
            wifiManager?.enableNetwork(networkIdToConnect, true)

            val wifiInfo: WifiInfo = wifiManager!!.connectionInfo

            HMGUtils.timer(5000,false){
                supState = wifiInfo.supplicantState
                Log.i(TAG, "Done connect to network : status =  $supState")
                val successStates = listOf(SupplicantState.COMPLETED, SupplicantState.ASSOCIATED)
                if (successStates.contains(supState))
                    completionListener(true,"Connected to internet Wifi")
                else
                    completionListener(false,"errorConnectingHmgNetwork")
            }

        } else {
            Log.v(TAG, "WifiWizard: cannot connect to network")
            completionListener(false,"errorConnectingHmgNetwork")
        }

//        val wifi = WifiConfiguration()
//        wifi.SSID = SSID
//        wifi.status = WifiConfiguration.Status.ENABLED
//        wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
//
//        wifi.networkId = ssidToNetworkId(SSID)
//
//        // Set network to highest priority (deprecated in API >= 26)
//        if(Build.VERSION.SDK_INT < 26) {
//            wifi.priority = getMaxWifiPriority(wifiManager!!) + 1;
//        }
//
//        // After processing authentication types, add or update network
//        if(wifi.networkId == -1) { // -1 means SSID configuration does not exist yet
//
//            val newNetId = wifiManager?.addNetwork(wifi)!!
//            if( newNetId > -1 ){
//                completionListener(true,"Success")
//            } else {
//                completionListener(false, "ERROR_ADDING_NETWORK" )
//            }
//
//        } else {
//
//            var updatedNetID = wifiManager?.updateNetwork(wifi)
//
//            if(updatedNetID == -1)
//                updatedNetID = wifiManager?.addNetwork(wifi)
//
//            if(updatedNetID > -1) {
//                callbackContext.success( updatedNetID )
//            } else {
//                callbackContext.error("ERROR_UPDATING_NETWORK")
//            }
//
//        }
//
//        // WifiManager configurations are presistent for API 26+
//        if(Build.VERSION.SDK_INT < 26) {
//            wifiManager?.saveConfiguration(); // Call saveConfiguration for older < 26 API
//        }
    }


    /**
     * This method takes a given String, searches the current list of configured WiFi
     * networks, and returns the networkId for the network if the SSID matches. If not,
     * it returns -1.
     */
    @SuppressLint("MissingPermission")
    private fun ssidToNetworkId(ssid: String): Int {
        val currentNetworks = wifiManager!!.configuredNetworks
        var networkId = -1

        // For each network in the list, compare the SSID with the given one
        for (test in currentNetworks) {
            if (test.SSID == ssid) {
                networkId = test.networkId
                break
            }
        }
        return networkId
    }

    companion object{

        /**
         * Figure out what the highest priority network in the network list is and return that priority
         */
        @RequiresApi(Build.VERSION_CODES.S)
        fun getMaxWifiPriority(wifiManager:WifiManager) :  Int {
            val configurations = wifiManager.callerConfiguredNetworks
            var maxPriority = 0
            configurations.forEach {
                if (it.priority > maxPriority) {
                    maxPriority = it.priority;
                }
            }
            return  maxPriority;
        }
    }
}
