/*
 *  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 Foundation

/**
 * A simulated humidity sensor for use in the samples. The sensor has
 * humidity and maximum threshold attributes.
 */
public class HumiditySensor {
    
    // The min range for values is 0% humidity
    private var hmin: Int = 0
    // The hmin range for values is 100% humidity
    private var hmax: Int = 100
    // Maximum humidity threshold, writable
    private var maxThreshold: Int = 80
    // A unique identifier for this sensor
    private var hardwareId:String = ""
    // The HumiditySensor simulates humidity data by generating values that
    // fluctuate around the 'set point', arbitrarily set to 75% humidity.
    private var setPoint:Double = 75.0
    // amplitude is the maximum deviation from the 'set point'
    private var amplitude:Double = 0.0
    // the simulated data
    private var humidities:[Int]? = nil
    // index into humidity data
    private var index:Int = 0

    /**
     * Create a HumiditySensor
     * - parameter id: a unique identifier for this sensor
     */
    public init(id: String) {
        self.hardwareId = id
        let persistentData = UserDefaults.standard
        if (persistentData.object(forKey: hardwareId + "maxThreshold") != nil) {
            self.maxThreshold = persistentData.integer(forKey: hardwareId + "maxThreshold")
        } else {
            self.maxThreshold = 80
            persistentData.set(maxThreshold, forKey: hardwareId + "maxThreshold")
        }
        // Because ios generates a different value when converting to radians
        // add a small increment to the 0.5 used in JCL This generates at least
        // one value over the threshold
        amplitude = Double(maxThreshold) - 75.0 + 0.525
        humidities = self.createHumidityData(setPoint:setPoint, amplitude:amplitude)
    }
    
    /**
     * Get the current humidity value. The value returned is between 0 and 100,
     * representing a percent humidity.
     * - returns: the current humidity value.
     */
    public func getHumidity() -> Int {
        defer {
            index = (index + 1) % humidities!.count
        }
        return humidities![index]
    }
    
    /**
     * Set the maximum percent humidity threshold for alerts. The value is
     * clamped to the range [60..100] and represents a whole percentage.
     * @param threshold a value between 60 and 100
     */
    public func setMaxThreshold(threshold: Int) {
        if threshold < 60 {
            maxThreshold = 60
        } else if threshold > 100 {
            maxThreshold = 100
        } else {
            maxThreshold = threshold
        }
    }
    
    /**
     * Get the maximum threshold value.
     * - returns: the maximum threshold
     */
    public func getMaxThreshold() -> Int {
        return self.maxThreshold
    }
    
    /**
     * Get the manufacturer name, which can be used as part of the
     * device meta-data.
     * - returns: the manufacturer name
     */
    public func getManufacturer() -> String {
        return "Sample"
    }
    
    /**
     * Get the model number, which can be used as part of the
     * device meta-data.
     * - returns: the model number
     */
    public func getModelNumber() -> String {
        return "MN-" + self.hardwareId
    }
    
    /**
     * Get the serial number, which can be used as part of the
     * device meta-data.
     * - returns: the serial number
     */
    public func getSerialNumber() -> String {
        return "SN-" + self.hardwareId
    }
    
    /**
     * Get the hardware id, which can be used as part of the
     * device meta-data.
     * - returns: the hardware id
     */
    public func getHardwareId() -> String {
        return self.hardwareId
    }
    
    private func createHumidityData(setPoint:Double, amplitude:Double) -> [Int] {
        
        let delta:Int = 10
        let dataPoints:Int = 360/delta
        var data:[Int] = [Int]()
        var angle:Int = 0
        
        //for (int angle=0, index=0; index < data.length; angle += delta, index++) {
        for _ in 0..<dataPoints {
            data.append(calculateHumidity(setPoint:setPoint,
                                          amplitude:amplitude, angle:angle))
            angle += delta
        }
        return data;
    }
    
    private func calculateHumidity(setPoint:Double, amplitude:Double, angle:Int) -> Int {
        let degrees = Measurement(value: Double(angle), unit: UnitAngle.degrees)
        let radians = degrees.converted(to: .radians)
        let delta:Double = amplitude * sin(radians.value)
        let humidity:Double = setPoint + delta
        return Int(humidity.rounded())
    }
}
