/**
 * Copyright (c) 2018 NXP
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nxp.vizncompanionapp.utility

import android.content.Context
import android.os.Handler
import android.os.Looper
import com.nxp.vizncompanionapp.model.Device
import org.json.JSONObject
import android.net.wifi.WifiManager
import android.util.Log
import java.net.InetAddress
import java.net.UnknownHostException
import java.io.IOException
import java.net.NetworkInterface
import java.util.*

class DeviceManagement {

    private var count: Int = 0
    private var mTimer: Timer? = null

    /**
     * check the device is already in list or not
     *
     * @param mDeviceList DeviceList
     * @param device Device which is coming from UDP listener
     */
    fun checkDeviceInList(mDeviceList: MutableList<Device>?, device: Device): Boolean {
        return (0..(mDeviceList?.size?.minus(1))!!).any { mDeviceList[it].mDeviceAddress == device.mDeviceAddress }
    }

    /**
     * check the device is already in list or not
     *
     * @param mDeviceList DeviceList
     * @param state Confirm state of device
     */
    fun checkDeviceState(mDeviceList: MutableList<Device>?, state: String): Boolean {
        return (0..(mDeviceList?.size?.minus(1))!!).any { mDeviceList[it].mState == state }
    }

    /**
     * get already existing device index
     *
     * @param mDeviceList DeviceList
     * @param device Device which is coming from UDP listener
     */
    fun getDeviceIndexInList(mDeviceList: MutableList<Device>?, device: Device): Int{
        var size = mDeviceList?.size!! -1
        var index = -1
           for(i in 0..(size)){
                if(mDeviceList[i].mDeviceAddress == device.mDeviceAddress){
                    index = i
                    break
                }
           }
        return index
    }

    /**
     * get already existing device index
     *
     * @param mDeviceList DeviceList
     * @param state Confirm state of device
     */
    fun getConfirmDeviceIndexInList(mDeviceList: MutableList<Device>?, state: String): Int{
        var size = mDeviceList?.size!! -1
        var index = -1
        for(i in 0..(size)){
            if(mDeviceList[i].mState == state){
                index = i
                break
            }
        }
        return index
    }
    /**
     * Update the existing device info in list with updated values
     *
     *  @param device Device which is already in list
     * @param device device1 which is coming from UDP listener
     */
    fun mergeDeviceValueInList(device: Device?, device1: Device): Device? {
        var newDevice: Device = device1
        if(device1.mProductID != ""){
            newDevice.mProductID = device1.mProductID
        } else{
            newDevice.mProductID = device?.mProductID!!
        }
        if(device1.mDsn != ""){
            newDevice.mDsn = device1.mDsn
        } else{
            newDevice.mDsn = device?.mDsn!!
        }
        if(device1.mCodeChallenge != ""){
            newDevice.mCodeChallenge = device1.mCodeChallenge
        } else{
            newDevice.mCodeChallenge = device?.mCodeChallenge!!
        }
        if(device1.mState != ""){
            newDevice.mState = device1.mState
        } else{
            newDevice.mState = device?.mState!!
        }
        if(device1.mDeviceStatus != ""){
            newDevice.mDeviceStatus = device1.mDeviceStatus
        } else{
            newDevice.mDeviceStatus = device?.mDeviceStatus!!
        }
        return newDevice
    }

    fun updateStateValueInList(get: Device): Device? {
        var newDevice: Device = get
        newDevice.mState = ""
        return newDevice
    }

    /**
     * Send UDP Broadcast message
     */
    fun sendBroadcast(context: Context){
        var json = JSONObject()
        json.put("State",AppConstant.BROADCAST_MSG)
        json.put("Mode","Broadcast")
        try {
            var br = getBroadcastAddress(context)
            Handler().postDelayed({
                if(br != null) {
                    callBroadcastTask(json,br.hostAddress)
                } else{
                    var address = getBroadcastAdd(context)
                    if(address == null) {
                        callBroadcastTask(json,AppConstant.BROADCAST_ADDRESS)
                    } else {
                        callBroadcastTask(json,address)
                    }
                }
            }, 100)
        } catch (e: Exception){
            Log.e("TAG Exception",e.toString())
        }

    }

    /**
     * stop timer to check wifi connectivity
     */
    fun stopTimer() {
        if (mTimer != null) {
            mTimer!!.cancel()
            mTimer = null
        }
    }

    fun callBroadcastTask(json: JSONObject, address: String){
        stopTimer()
        mTimer = Timer()
        count = 0
        mTimer!!.scheduleAtFixedRate(sendBroadcastTask(json,address), 0, 5000)
    }

    private inner class sendBroadcastTask(json: JSONObject, address: String) : TimerTask() {

        private var mJson : JSONObject? = null
        private var mAddress: String? = null

        init {
            mJson = json
            mAddress = address
        }
        override fun run() {
            Log.e("TAG", "count $count")
            if(count < AppConstant.NO_OF_BROADCAST){
                UdpSendProtocol(mJson.toString(), mAddress!!).execute()
                count++
            } else {
                stopTimer()
            }
        }
    }

    /**
     * get Broadcast address of LAN network
     */
    @Throws(UnknownHostException::class)
    fun getBroadcastAddress(c: Context): InetAddress? {
        val wifi = c.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val dhcp = wifi.dhcpInfo ?: return null

        return if(dhcp.netmask == 0){
            null
        } else {

            val broadcast = dhcp.ipAddress and dhcp.netmask or dhcp.netmask.inv()

            val quads = ByteArray(4)
            for (k in 0..3) {
                quads[k] = (broadcast shr k * 8 and 0xFF).toByte()
            }

            InetAddress.getByAddress(quads)
        }
    }

    fun getBroadcastAdd(context: Context) : String? {
        val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val dhcpInfo = wifiManager.dhcpInfo
        if(null == dhcpInfo){
            return null
        } else {
            try {
                var ipAddress = dhcpInfo.ipAddress
                val addressBytes = byteArrayOf((0xff and ipAddress).toByte(), (0xff and (ipAddress shr 8)).toByte(), (0xff and (ipAddress shr 16)).toByte(), (0xff and (ipAddress shr 24)).toByte())
                val inetAddress = InetAddress.getByAddress(addressBytes)
                val networkInterface = NetworkInterface.getByInetAddress(inetAddress)
                return if(null == networkInterface){
                    null
                } else {
                    if (null == networkInterface.interfaceAddresses[1]) {
                        null
                    } else {
                        if (null == networkInterface.interfaceAddresses[1].broadcast) {
                            null
                        } else {
                            networkInterface.interfaceAddresses[1].broadcast.hostAddress
                        }
                    }
                }
            } catch (e: IOException) {
                Log.e("TAG", e.toString())
                return null
            }
        }

    }


    /**
     * send Authentication details to particular device
     */
    fun sendAuthCode(json: JSONObject, mAddress: String) {
        Log.d("CMLTD", "sending to" + mAddress)
        Handler(Looper.getMainLooper()).postDelayed({
            UdpSendProtocol(json.toString(),mAddress).execute()
        }, 100)
    }

    /**
     * send confirmation request to particular device
     */
    fun sendConfirmCode(json: JSONObject, mAddress: String) {
        Handler(Looper.getMainLooper()).postDelayed({
            UdpSendProtocol(json.toString(),mAddress).execute()
        }, 100)
    }

    fun sendMsgToWiFiDevice(json: JSONObject, mAddress: String){
        Handler(Looper.getMainLooper()).postDelayed({
            UdpSendProtocol(json.toString(),mAddress).execute()
        }, 100)
    }

    fun sendCredToWiFiDevice(json: JSONObject, mAddress: String){
        Handler(Looper.getMainLooper()).postDelayed({
            UdpSendProtocol(json.toString(),mAddress).execute()
        }, 100)
    }
}