/*
 *  Copyright © 2016 Oracle and/or its affiliates. All rights reserved.
 *  This software is dual-licensed to you under the MIT License (MIT) and
 *  the Universal Permissive License (UPL). See the LICENSE file in the root
 *  directory for license terms. You may choose either license, or both.
 */

import UIKit
import EnterpriseLib

class DeviceSelectionView: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var devices: [Devices] = []
    @objc var selectedTableCellIndex: Int = 1000
    @objc var listLength:Int = 0
    @objc var sensor:String = ""
    
    var deviceIds:[String?] = []
    var curValues:[String?] = []
    @objc static var first: Bool = true
        
    private class DSVSyncCallback : SyncCallback {
        override init() {
            super.init()
        }
        override public func onSync(event:SyncEvent) {
            print("#### In syncCallback ####")
        }
    }
    private var syncCallback:DSVSyncCallback = DSVSyncCallback()

    @IBOutlet var backToViewController3: UIButton! {
        didSet {
            backToViewController3.layer.cornerRadius = 6.0
            backToViewController3.layer.borderColor = UIColor.lightGray.cgColor
            backToViewController3.layer.borderWidth = 1.0
            backToViewController3.layer.backgroundColor = UIColor(white: 0.90,
                                                                  alpha: 1.0).cgColor
        }
    }
    
    @IBOutlet var FourthViewOutlet: UIView!
    
    
    @IBOutlet var exitApp: UIButton! {
        didSet {
            exitApp.layer.cornerRadius = 6.0
            exitApp.layer.borderColor = UIColor.lightGray.cgColor
            exitApp.layer.borderWidth = 1.0
            exitApp.layer.backgroundColor = UIColor(white: 0.90,
                                                    alpha: 1.0).cgColor
            // TODO unhide exit button
            exitApp.isHidden = true
        }
    }
    
    @IBOutlet weak var headLineLabelOutlet: UILabel!
    
    @IBOutlet weak var selectDeviceLabelOutlet: UILabel!
    
    @IBOutlet weak var tableView: UITableView!
    
    
    @IBAction func onBackButtonClick(_ sender: UIButton) {
        deviceIds.removeAll()
        curValues.removeAll()
        
        DeviceSelectionView.first = true
        
        maxThresholds.removeAll()
        minThresholds.removeAll()
        activeDevices.removeAll()
        
        deviceManufacturers.removeAll()
        deviceModelNumbers.removeAll()
        deviceSerialNumbers.removeAll()
        
        deviceUUID.removeAll()
        deviceMajor.removeAll()
        deviceMinor.removeAll()

        
        let thirdViewController = self.storyboard?.instantiateViewController(
            withIdentifier: "DeviceModelSelectionView") as! DeviceModelSelectionView
        self.navigationController?.pushViewController(thirdViewController, animated: false)
    }
    
    @IBAction func onExitAppClick(_ sender: UIButton) {
        exit(0)
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
        
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        // Long press action implemented for each table row to see better entire 
        // long device ids.
        /* 
        let holdToShowDeviceId = UILongPressGestureRecognizer(target: self, action: #selector(DeviceSelectionView.longShowId))
        holdToShowDeviceId.minimumPressDuration = 1.10
        self.tableView.addGestureRecognizer(holdToShowDeviceId)
         */

        listLength = activeDevices.count
        if listLength > 0 {
            let modelUrn = activeDevices[0].getDeviceModel().getURN()
            switch modelUrn {
            case HUMIDITY_SENSOR_DEVICE_MODEL_URN:
                    self.sensor = kCurHValue
                    selectDeviceLabelOutlet.text = "Humidity Sensors"
                    break
            case TEMPERATURE_SENSOR_DEVICE_MODEL_URN:
                    self.sensor = kCurTValue
                    selectDeviceLabelOutlet.text = "Temperature Sensors"
                    break
            case IBEACON_DEVICE_MODEL_URN:
                    self.sensor = kCurRssiValue
                    selectDeviceLabelOutlet.text = "iBeacon Sensors"
                    break
            case EDDYSTONEBEACONTLMUID_DEVICE_MODEL_URN:
                    self.sensor = kCurRssiValue
                    selectDeviceLabelOutlet.text = "Eddystone Sensors"
                    break
            case MOTIONACTIVATEDCAMERA_DEVICE_MODEL_URN:
                    selectDeviceLabelOutlet.text = "Motion Activated Cameras"
                    break
                default:
                    self.sensor = ""
                    selectDeviceLabelOutlet.text = "No Sensor"
                    break
            }
            
            if DeviceSelectionView.first == true {
                self.deviceIds = [String?](repeating: nil, count: listLength)
                self.curValues = [String](repeating: "", count: listLength)
            }
            else {
                let userDefaults = UserDefaults.standard
                self.curValues = userDefaults.array(forKey: "currentValues") as! [String]
                self.deviceIds = userDefaults.array(forKey: "idValues") as! [String]
                userDefaults.synchronize()
            }
            
            if maxThresholds.count == 0 {
                maxThresholds = [Int32](repeating: 0, count: listLength)
            }
            if minThresholds.count == 0 {
                minThresholds = [Int32](repeating: 0, count: listLength)
            }

            var index: Int = 0
            // Start monitor for all active devices.
            for device in activeDevices {
                
                self.deviceIds[index] = device.getEndpointId()
                
                device.setOnChange(callback: { (event: ChangeEvent) in
                    DispatchQueue.main.async(execute: {
                    if self.curValues.count > 0 {
                        var actInd: Int = 0
                        let device = event.getVirtualDevice()
                        
                        unit = ""
                        do {
                            switch self.sensor {
                                case "Humidity":
                                    unit = "%"
                                    break
                                case "Temperature":
                                    switch try device.get(attributeName: "unit")
                                                                as? String ?? "" {
                                    case "Cel", "Celsius", "\u{00B0}C":
                                        unit = "\u{00B0}C"
                                    default:
                                        unit = "\u{00B0}F"
                                    }
                                    break
                                default:
                                    break
                            }
                        } catch { }
                        
                        for dev in activeDevices {
                            if dev.getEndpointId() == device.getEndpointId() {
                                break
                            }                            
                            actInd += 1
                        }
                        
                        var namedValue = event.getNamedValue()
                        var hasMore = true
                        var reloadRows:Bool = false
                        repeat  {
                            switch namedValue.getName() {
                            case "humidity":
                                humidity = namedValue.getValue()!.intValue
                                self.curValues[actInd] =
                                        "\(humidity.description) \(unit)"
                                reloadRows = true
                            break
                            case "temp":
                                temperature = namedValue.getValue()!.doubleValue
                                self.curValues[actInd] = String(format: "%.2f \(unit)", temperature)
                                reloadRows = true
                            break
                            case "ora_rssi":
                                rssi = namedValue.getValue()!.doubleValue
                                self.curValues[actInd] = String(format: "%.1f", rssi)
                                reloadRows = true
                            break
                            case "maxThreshold":
                                maxThresholds[actInd] = namedValue.getValue()!.intValue
                                break
                            case "minThreshold":
                                minThresholds[actInd] = namedValue.getValue()!.intValue
                                break
                            // TODO: Once we figure out what the "current value" for a
                            // motion activated camera is, we can enable this onChange
                            /*
                            case "image":
                                if namedValue.getValue() is StorageObject {
                                    // The client library gives control to the application to decide
                                    // whether and where to download content from the Storage Cloud Service.
                                    let so:StorageObject = namedValue.getValue() as! StorageObject
                                    do {
                                        // TODO: Make sure directory structure is created
                                        try so.setOutputPath(outputPath: getImageDir() + so.getName())
                                        // sync happens in the background and calls the syncCallback when done
                                        so.setOnSync(callback: self.syncCallback)
                                        try so.sync()
                                    } catch {
                                        print(error)
                                    }
                                } else if namedValue.getValue() is ExternalObject {
                                    self.curValues[actInd] = (namedValue.getValue() as? ExternalObject)?.getURI()
                                } else {
                                    print("Can't convert value for image attribute")
                                    self.curValues[actInd] = namedValue.getValue()!.stringValue
                                }
                                break
                            case "imageTime":
                                // TODO: Should this have already been in ISO860 format ?
                                self.curValues[actInd] =
                                    ISO8601DateFormatter().string(from:Date(timeIntervalSince1970:namedValue.getValue() as! TimeInterval))
                                break
                            */
                            default:
                                break
                            }
                            // This only done for humidity, temp, and ora_rssi
                            // self.tableView.reloadData()   // Reload entire table view.
                            // Reload one single row in the active device table view.
                            if reloadRows {
                                let path = IndexPath(row: actInd + 1, section: 0)
                                self.tableView.reloadRows(at: [path], with: .none)
                                reloadRows = false
                            }
                            
                            if (namedValue.next() != nil) {
                                namedValue = namedValue.next()!
                            } else {
                                hasMore = false
                            }
                        } while (hasMore)
                    }
                    })
                })
                
                index += 1
            }
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return listLength + 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: DeviceSelectionCell = self.tableView.dequeueReusableCell(
            withIdentifier: DeviceSelectionCell.ID) as! DeviceSelectionCell
        
        cell.leftValue.textAlignment = .left
        cell.rightValue.textAlignment = .center
        
        if indexPath.row == 0 {
            cell.leftValue.text = kDeviceId
            cell.rightValue.text = self.sensor
            cell.accessoryType = .none
        } else {
            cell.leftValue.text = self.deviceIds[indexPath.row - 1]
            cell.rightValue.text = self.curValues[indexPath.row - 1]
            
            cell.accessoryType = .disclosureIndicator
        }
        return cell
    }
    
    // Show monitor details for selected active device.
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.tableView.cellForRow(at: indexPath)?.selectionStyle = .gray
        self.monitorDevice(index: indexPath.row)
    }
    
    override func viewWillAppear (_ animated: Bool) {
        self.navigationItem.setHidesBackButton(true, animated: false)
        headLineLabelOutlet.font = .boldSystemFont(ofSize: 18.0)
        headLineLabelOutlet.text = kEnterpriseHeadLine
    }
    
    override func viewDidAppear (_ animated: Bool) {
        super.viewDidAppear(animated)
        
        self.tableView.flashScrollIndicators()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // actions
    
    @objc func monitorDevice(index: Int) {
        if index > 0 {
            monitoredDevice = activeDevices[index - 1].getEndpointId()
            monitoredDeviceManufacturer = deviceManufacturers[index - 1]
            monitoredDeviceModelNumber = deviceModelNumbers[index - 1]
            monitoredDeviceSerialNumber = deviceSerialNumbers[index - 1]
            
            let modelUrn = activeDevices[index - 1].getDeviceModel().getURN()
            
            let userDefaults = UserDefaults.standard
            userDefaults.set(self.curValues, forKey: "currentValues")
            userDefaults.set(self.deviceIds, forKey: "idValues")
            userDefaults.synchronize()
            
            switch modelUrn {
            case HUMIDITY_SENSOR_DEVICE_MODEL_URN:
                    humidityThreshold = maxThresholds[index - 1]
                    DispatchQueue.main.async(execute: {
                    let sixthViewController = self.storyboard?.instantiateViewController(
                        withIdentifier: "HumidityDeviceMonitorView") as! HumidityDeviceMonitorView
                    self.navigationController?.pushViewController(sixthViewController,
                                                                  animated: true)
                    })
                    break
                
            case TEMPERATURE_SENSOR_DEVICE_MODEL_URN:
                    temperatureMaxThreshold = maxThresholds[index - 1]
                    temperatureMinThreshold = minThresholds[index - 1]
                    DispatchQueue.main.async(execute: {
                    let fifthViewController = self.storyboard?.instantiateViewController(
                        withIdentifier: "TemperatureDeviceMonitorView") as! TemperatureDeviceMonitorView
                    self.navigationController?.pushViewController(fifthViewController,
                                                                  animated: true)
                    })

                    break
            case IBEACON_DEVICE_MODEL_URN:
                    monitoredDeviceUUID = deviceUUID[index - 1]
                    monitoredDeviceMajor = deviceMajor[index - 1]
                    monitoredDeviceMinor = deviceMinor[index - 1]
                    DispatchQueue.main.async(execute: {
                    let seventhViewController = self.storyboard?.instantiateViewController(
                        withIdentifier: "BeaconDeviceMonitorView") as! BeaconDeviceMonitorView
                    self.navigationController?.pushViewController(seventhViewController,
                                                                  animated: true)
                    })
                    
                    break
            case EDDYSTONEBEACONTLMUID_DEVICE_MODEL_URN:
                    monitoredDeviceUUID = deviceUUID[index - 1]
                    DispatchQueue.main.async(execute: {
                    let eighthViewController = self.storyboard?.instantiateViewController(
                        withIdentifier: "EddystoneDeviceMonitorView") as! EddystoneDeviceMonitorView
                    self.navigationController?.pushViewController(eighthViewController,
                                                                  animated: true)
                    })
                    
                    break
            case MOTIONACTIVATEDCAMERA_DEVICE_MODEL_URN:
                    print("Monitoring motion activated camera")
                    DispatchQueue.main.async(execute: {
                        let v = self.storyboard?.instantiateViewController(
                            withIdentifier: "MotionActivatedCameraView") as!
                            MotionActivatedCameraView
                        self.navigationController?.pushViewController(v,
                                                                animated: true)
                    })
                    break
                default:
                    
                    break
            }
        }
    }
    
    
    @objc func longShowId(sender: UILongPressGestureRecognizer) {
        var cellDeviceId: String = ""
        
        if sender.state == UIGestureRecognizerState.began {
            let touchPoint = sender.location(in: self.tableView)
            let indexPath = self.tableView.indexPathForRow(at: touchPoint)
            if  indexPath != nil {
                if indexPath!.row == 0 {
                    cellDeviceId = "Not a valid row selected."
                }
                else {
                    cellDeviceId = self.deviceIds[indexPath!.row - 1]!
                }
                
                showError(controller: self, alertTitle: "Device ID",
                          message: cellDeviceId , actionTitle: "OK",
                          handler: nil)
            }
        }
    }

}

// Define custom table view cell
class DeviceSelectionCell : UITableViewCell {
    @objc static let ID = "DeviceCell"
    
    @IBOutlet weak var leftValue: UILabel!
    @IBOutlet weak var rightValue: UILabel!
    
}
