/*
 *  Copyright © 2016, 2017, 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 TemperatureDeviceMonitorView: UIViewController {
    
    var virtualDevice: VirtualDevice?
    @objc var cvs: [String] = []
    @objc var idx = 0
    
    @IBOutlet var FifthViewOutlet: UIView!
    
    @IBOutlet weak var appNameLabelOutlet: UILabel!
    
    @IBOutlet weak var deviceNameLabelOutlet: UILabel!
    
    @IBOutlet weak var tempLabelOutlet: UILabel!
    
    @IBOutlet weak var minTempLabelOutlet: UILabel!
    
    @IBOutlet weak var maxTempLabelOutlet: UILabel!
    
    @IBOutlet weak var lastOnLabelOutlet: UILabel!
    
    @IBOutlet weak var systemPowerLabelOutlet: UILabel!
    
    @IBOutlet weak var setTempThresholdsLabelOutlet: UILabel!
    
    @IBOutlet weak var alertLabelOutlet: UILabel!
    
    @IBOutlet weak var powerSwitchOutlet: UISwitch!
    
    @IBOutlet weak var modelNumberLabelOutlet: UILabel!
    
    @IBOutlet weak var manufacturerLabelOutlet: UILabel!
    
    @IBOutlet weak var serialNumberLabelOutlet: UILabel! {
        didSet {
            // TODO: unhide serial number label if necessary
            serialNumberLabelOutlet.isHidden = true
        }
    }

    @IBOutlet weak var currentLabelOutlet: UILabel!
    
    @IBOutlet weak var newLabelOutlet: UILabel!
    
    @IBOutlet weak var minimumLabelOutlet: UILabel!
    
    @IBOutlet weak var maximumLabelOutlet: UILabel!
    
    @IBOutlet weak var oldMinLabelOutlet: UILabel!
    
    @IBOutlet weak var oldMaxLabelOutlet: UILabel!
    
    @IBOutlet weak var newMinTextFieldOutlet: UITextField!
    
    @IBOutlet weak var newMaxTextFieldOutlet: UITextField!
    
    @IBOutlet weak var deviceIdLabelOutlet: UILabel!
    
    @IBOutlet weak var applyButtonOutlet: UIButton!  {
        didSet {
            applyButtonOutlet.layer.cornerRadius = 6.0
            applyButtonOutlet.layer.borderColor = UIColor.lightGray.cgColor
            applyButtonOutlet.layer.borderWidth = 1.0
            applyButtonOutlet.layer.backgroundColor = UIColor(white: 0.90,
                                                              alpha: 1.0).cgColor
        }
    }

    @IBOutlet var backToViewController4: UIButton! {
        didSet {
            backToViewController4.layer.cornerRadius = 6.0
            backToViewController4.layer.borderColor = UIColor.lightGray.cgColor
            backToViewController4.layer.borderWidth = 1.0
            backToViewController4.layer.backgroundColor = UIColor(white: 0.90,
                                                                  alpha: 1.0).cgColor
        }
    }
    
    @IBOutlet weak var resetButton: UIButton! {
        didSet {
            resetButton.layer.cornerRadius = 6.0
            resetButton.layer.borderColor = UIColor.lightGray.cgColor
            resetButton.layer.borderWidth = 1.0
            resetButton.layer.backgroundColor = UIColor(white: 0.90,
                                                        alpha: 1.0).cgColor
        }
    }
    
    @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
        }
    }
    
    @IBAction func onBackButtonClick(_ sender: UIButton) {
        if !self.cvs[self.idx].hasSuffix(" " + unit) {
            self.cvs[self.idx] = "\(self.cvs[self.idx]) \(unit)"
        }
        let userDefaults = UserDefaults.standard
        userDefaults.set(self.cvs, forKey: "currentValues")
        userDefaults.synchronize()
        
        DeviceSelectionView.first = false
        
        let fourthViewController =
            self.storyboard?.instantiateViewController(
                withIdentifier: "DeviceSelectionView") as! DeviceSelectionView
        self.navigationController?.pushViewController(fourthViewController,
                                                      animated: false)
    }
    
    @IBAction func onExitAppClick(_ sender: UIButton) {
        exit(0)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        UIGraphicsBeginImageContext(CGSize(width: self.view.frame.size.width,
                                           height: self.view.frame.size.width - 18))
        UIImage(named: "cloudes-sw.png")?.drawAsPattern(in: self.view.bounds)
        let image : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        FifthViewOutlet.backgroundColor = UIColor(patternImage: image)
        
        powerSwitchOutlet.addTarget(self,
            action: #selector(TemperatureDeviceMonitorView.onPowerSwitchChanged(_:)),
            for: .valueChanged)
        
        for dev in activeDevices {
            if dev.getEndpointId() == monitoredDevice {
                virtualDevice = dev
            }
        }
        
        // Monitor the selected device for temperature device model and update 
        // the UI view accordingly.
        if virtualDevice != nil {
            
            // Temperature values changes are managed.
            virtualDevice?.setOnChange(callback: { (event: ChangeEvent) in
                let device = event.getVirtualDevice()
                var namedValue = event.getNamedValue()
                var hasMore = true
                
                repeat {
                    let attValue = namedValue.getValue()
                    
                    DispatchQueue.main.async() {
                        
                    switch (namedValue.getName()) {
                        case "temp", "unit":
                            temperature = (attValue?.doubleValue)!
                            do {
                                var getValue = try device.get(attributeName: "minTemp")
                                if getValue != nil {
                                    minTemperature = getValue as! Double
                                    self.minTempLabelOutlet.text =
                                        String(format: "\(kMinTemperature)%.2f \(unit)",
                                               minTemperature)
                                }
                                
                                getValue = try device.get(attributeName: "maxTemp")
                                if getValue != nil {
                                    maxTemperature = getValue as! Double
                                    self.maxTempLabelOutlet.text =
                                        String(format: "\(kMaxTemperature)%.2f \(unit)",
                                               maxTemperature)
                                }
                                
                                getValue = try device.get(attributeName: "startTime")
                                if getValue != nil {
                                    let startTime =  (getValue as! Double)
                                    let theDate = Date(timeIntervalSince1970: startTime)
                                    let dateFormatter = DateFormatter()
                                    dateFormatter.dateStyle = .short
                                    dateFormatter.timeStyle = .short
                                    lastOn = dateFormatter.string(from: theDate)
                                    self.lastOnLabelOutlet.text = "\(kLastOn) \(lastOn)"
                                }
                            } catch {
                                print(error)
                                showError(controller: self,
                                          alertTitle: "Temperature Alert",
                                          message: "\(error)",
                                          actionTitle: "OK", handler: nil)
                            }
                            
                            self.cvs[self.idx] = String(format: "%.2f", temperature)
                            self.tempLabelOutlet.text =
                                "\(kTemperature)\(self.cvs[self.idx]) \(unit)"
                        
                        case "maxThreshold":
                            temperatureMaxThreshold = (attValue?.intValue)!
                            self.oldMinLabelOutlet.text =
                                "\(temperatureMinThreshold.description) \(unit)"
                            self.oldMaxLabelOutlet.text =
                                "\(temperatureMaxThreshold.description) \(unit)"
                        
                        case "minThreshold":
                            temperatureMinThreshold = (attValue?.intValue)!
                            self.oldMinLabelOutlet.text =
                                "\(temperatureMinThreshold.description) \(unit)"
                            self.oldMaxLabelOutlet.text =
                                "\(temperatureMaxThreshold.description) \(unit)"
                        
                        case "minTemp":
                            minTemperature = (attValue?.doubleValue)!
                            self.minTempLabelOutlet.text =
                                String(format: "\(kMinTemperature)%.2f \(unit)",
                                       minTemperature)
                        
                        case "maxTemp":
                            maxTemperature = (attValue?.doubleValue)!
                            self.maxTempLabelOutlet.text =
                                String(format: "\(kMaxTemperature)%.2f \(unit)",
                                       maxTemperature)
                        
                        case "startTime":
                            let startTime = (attValue?.doubleValue)!
                            let theDate = Date(timeIntervalSince1970: startTime)
                            let dateFormatter = DateFormatter()
                            dateFormatter.dateStyle = .short
                            dateFormatter.timeStyle = .short
                            lastOn = dateFormatter.string(from: theDate)
                            self.lastOnLabelOutlet.text = "\(kLastOn) \(lastOn)"
                        
                        default: break
                    }
                        
                    } // end DispatchQueue.async
                    if (namedValue.next() != nil) {
                        namedValue = namedValue.next()!
                     }
                    else {
                        hasMore = false
                    }
                }
                while (hasMore)
                    
            })
            
            // Temperature alerts are managed.
            virtualDevice?.setOnAlert(callback: { (event: AlertEvent) in
                let longUrn = event.getURN()
                let index = longUrn.range(of: ":",
                                          options: .backwards)?.lowerBound
                let shortUrn = longUrn[index!..<longUrn.endIndex]
                
                // Loop for sample measurements
                // Timers must be started on the main thread.
                // This code is executed from the Alamofire response
                // queue. This queue has been changed to be NOT the main
                // queue.
                // TODO: Shouldn't this value be saved in order to invalidate
                // the timer ?
                DispatchQueue.main.async() {
                    
                if (":too_cold" == shortUrn) {
                    self.alertLabelOutlet.text = kTooCold
                    self.alertLabelOutlet.backgroundColor = .blue
                    self.alertLabelOutlet.textColor = .white
                    self.alertLabelOutlet.isHidden = false
                }

                if (":too_hot" == shortUrn) {
                    self.alertLabelOutlet.text = kTooHot
                    self.alertLabelOutlet.backgroundColor = .red
                    self.alertLabelOutlet.textColor = .black
                    self.alertLabelOutlet.isHidden = false
                }

                    let _ = Timer.scheduledTimer(timeInterval: 3, target: self,
                                             selector: #selector(self.hideAlert),
                                             userInfo: nil, repeats: false)
                } // end DispatchQueue.async
            })
            
            // Temperature min and max threshold update errors are managed.
            virtualDevice?.setOnError(callback: { (event: ErrorEvent) in
                let errMessage = event.getMessage()
                print("Error message: " + errMessage)
                
                if errMessage == "User Authentication failed!" {
                    appDeviceModelsList.removeAll()
                    dmArray.removeAll()
                    urnArray.removeAll()
                    
                    activeDevices.removeAll()
                    deviceManufacturers.removeAll()
                    deviceModelNumbers.removeAll()
                    deviceSerialNumbers.removeAll()
                    
                    do {
                        try ecl.close()
                    } catch {
                        print("Error while closing enterprise client")
                    }
                    
                    DispatchQueue.main.async() {
                        let firstViewController =
                            self.storyboard?.instantiateViewController(
                                withIdentifier: "ServerLoginView") as! ServerLoginView
                        self.navigationController?.pushViewController(firstViewController,
                                                                      animated: false)
                    }
                    return
                }
                
                let namedValue = event.getNamedValue()
                
                DispatchQueue.main.async() {
                    
                if namedValue.getName() == "maxThreshold" {
                    temperatureMaxThreshold = namedValue.getValue()!.intValue
                    self.oldMaxLabelOutlet.text =
                        "\(temperatureMaxThreshold.description) \(unit)"
                }
                
                if namedValue.getName() == "minThreshold" {
                    temperatureMinThreshold = namedValue.getValue()!.intValue
                    self.oldMinLabelOutlet.text =
                        "\(temperatureMinThreshold.description) \(unit)"
                }
                
                } // end DispatchQueue.main.async
            })
        }

    }

    override func viewWillAppear (_ animated: Bool) {
        self.navigationItem.setHidesBackButton(true, animated: false)
        
        deviceNameLabelOutlet.text = kTemperatureSensor
        deviceIdLabelOutlet.text = monitoredDevice
        appNameLabelOutlet.font = .boldSystemFont(ofSize: 18.0)
        appNameLabelOutlet.text = kEnterpriseHeadLine
        manufacturerLabelOutlet.text = kManufacturer + monitoredDeviceManufacturer
        modelNumberLabelOutlet.text = kModelNumber + monitoredDeviceModelNumber
        serialNumberLabelOutlet.text = kSerialNumber + monitoredDeviceSerialNumber
        tempLabelOutlet.font = .boldSystemFont(ofSize: 17.0)
        
        for dev in activeDevices {
            if dev.getEndpointId() == monitoredDevice {
                virtualDevice = dev
            }
        }
        
        let userDefaults = UserDefaults.standard
        self.cvs = userDefaults.array(forKey: "currentValues") as! [String]
        let ids = userDefaults.array(forKey: "idValues") as! [String]
        userDefaults.synchronize()
        
        do {
            switch try virtualDevice?.get(attributeName: "unit") as? String ?? "" {
            case "Cel", "Celsius", "\u{00B0}C":
                    unit = "\u{00B0}C"
            default:
                unit = "\u{00B0}F"
            }

            temperatureMinThreshold = Int32(truncating: (try virtualDevice?.get(
                            attributeName: "minThreshold")) as? NSNumber ?? 0)
            temperatureMaxThreshold = Int32(truncating: try virtualDevice?.get(
                            attributeName: "maxThreshold") as? NSNumber ?? 70)
            
            oldMinLabelOutlet.text = "\(temperatureMinThreshold.description) \(unit)"
            oldMaxLabelOutlet.text = "\(temperatureMaxThreshold.description) \(unit)"

        } catch { }
        
        let vdId = virtualDevice?.getEndpointId()
        for devId in ids {
            if devId == vdId {
                self.tempLabelOutlet.text = kTemperature + self.cvs[self.idx]
                break
            }
            self.idx += 1
        }
        
        minTempLabelOutlet.text = kMinTemperature
        maxTempLabelOutlet.text = kMaxTemperature
        lastOnLabelOutlet.text = kLastOn
        systemPowerLabelOutlet.text = kSystemPower
        setTempThresholdsLabelOutlet.font = .boldSystemFont(ofSize: 17.0)
        setTempThresholdsLabelOutlet.text = kSetTempThresholds
        alertLabelOutlet.isHidden = true
        powerSwitchOutlet.tintColor = .red
        powerSwitchOutlet.layer.cornerRadius = 16
        powerSwitchOutlet.backgroundColor = .red
        currentLabelOutlet.font = .boldSystemFont(ofSize: 17.0)
        currentLabelOutlet.text = kCurrent
        newLabelOutlet.font = .boldSystemFont(ofSize: 17.0)
        newLabelOutlet.text = kNewTemp
        minimumLabelOutlet.text = kMinimum
        maximumLabelOutlet.text = kMaximum
        
        applyButtonOutlet.setTitle(kApply, for: UIControlState())
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    
    // actions
    
    // Action to apply power off/on for temperature device.
    @IBAction func onPowerSwitchChanged(_ sender: UISwitch) {
        do {
            if sender.isOn {
                try self.virtualDevice?.call(actionName: "power",
                                             argument: true as AnyObject)
            }
            else {
                try self.virtualDevice?.call(actionName: "power",
                                             argument: false as AnyObject)
            }
        } catch {

            print(error)
            showError(controller: self,
                      alertTitle: "Power Action Error",
                      message: "\(error)",
                      actionTitle: "OK", handler: nil)
        }
    }
    
    @objc func hideAlert() {
        self.alertLabelOutlet.isHidden = true
    }
    
    // Action to reset temperature device.
    @IBAction func onResetButton(_ sender: AnyObject) {
        do {
            try self.virtualDevice?.call(actionName: "reset")
        } catch {
            print(error)
            showError(controller: self, alertTitle: "Reset Action Error",
                      message: "\(error)", actionTitle: "OK", handler: nil)
        }
    }
    
    // Action to apply changes on temperature thresholds.
    @IBAction func onApplyButton(_ sender: UIButton) {

        let newMinString = newMinTextFieldOutlet.text!
        let testMinInt = Int(newMinString)
        let newMaxString = newMaxTextFieldOutlet.text!
        let testMaxInt = Int(newMaxString)
        
        // If both min and max are nil, show an error and return
        if (testMinInt == nil) && (testMaxInt == nil) {
            showError(controller: self, alertTitle: "Threshold Setting Alert",
                      message: kThresholdSettingAlert, actionTitle: "OK",
                      handler: { (action:UIAlertAction?) -> () in
                self.newMinTextFieldOutlet.text = ""
                self.newMaxTextFieldOutlet.text = ""
            })
            return
        }
        
        if testMinInt != nil {
            let oldMinThreshold = temperatureMinThreshold
            temperatureMinThreshold = Int32(testMinInt!)
            newMinTextFieldOutlet.text = ""
            oldMinLabelOutlet.text = "\(temperatureMinThreshold.description) \(unit)"
            
            do {
                let newValue = NSNumber(value: temperatureMinThreshold as Int32)
                let _ = try self.virtualDevice?.set(attributeName: "minThreshold",
                                                    attributeValue: newValue as AnyObject)
            } catch ClientError.argument(let error){
                print(error)
                showError(controller: self,
                          alertTitle: "Threshold Setting Alert",
                          message: error, actionTitle: "OK",
                          handler: { (action:UIAlertAction?) -> () in
                            temperatureMinThreshold = oldMinThreshold
                            self.oldMinLabelOutlet.text =
                                "\(temperatureMinThreshold.description) \(unit)"
                })
            } catch {
                print(error)
                showError(controller: self, alertTitle: "Temperature Alert",
                          message: "\(error)", actionTitle: "OK",
                          handler: { (action:UIAlertAction?) -> () in
                            temperatureMinThreshold = oldMinThreshold
                            self.oldMinLabelOutlet.text =
                                "\(temperatureMinThreshold.description) \(unit)"
                })
            }
        }
        
        if testMaxInt != nil {
            let oldMaxThreshold = temperatureMaxThreshold
            temperatureMaxThreshold = Int32(testMaxInt!)
            newMaxTextFieldOutlet.text = ""
            oldMaxLabelOutlet.text = "\(temperatureMaxThreshold.description) \(unit)"
            
            do {
                let newValue = NSNumber(value: temperatureMaxThreshold as Int32)
                let _ = try self.virtualDevice?.set(attributeName: "maxThreshold",
                                                    attributeValue: newValue as AnyObject)
            } catch ClientError.argument(let error) {
                print(error)
                showError(controller: self,
                          alertTitle: "Threshold Setting Alert",
                          message: error, actionTitle: "OK",
                          handler: { (action:UIAlertAction?) -> () in
                            temperatureMaxThreshold = oldMaxThreshold
                            self.oldMaxLabelOutlet.text =
                                "\(temperatureMaxThreshold.description) \(unit)"
                })
            } catch {
                print(error)
                showError(controller: self, alertTitle: "Temperature Alert",
                          message: "\(error)", actionTitle: "OK",
                          handler: { (action:UIAlertAction?) -> () in
                            temperatureMaxThreshold = oldMaxThreshold
                            self.oldMaxLabelOutlet.text =
                                "\(temperatureMaxThreshold.description) \(unit)"
                })
            }
        }
    }
}
