import Foundation

struct DeviceInfo {

    /// The unique ID generated by the device to distinguish between power cycles
    let uniqueIdOfPowerCycle: Int

    /// The number of bytes recorded by the tracker
    let numberOfRecordedBytes: Int

    /// The sum of all recorded bytes
    let dataChecksum: UInt16

    /// The number of measurements already performed
    let numberOfStoredMeasurements: Int
    
    /// The interval between measurements (in seconds)
    let measurementInterval: Int
    
    let sensor0: TemperatureSensor?
    
    let sensor1: TemperatureSensor?
    
    // MARK: Device time
    
    let wakeupReason: DeviceWakeCause

    let time: DeviceTime
    
    // MARK: Storage
    
    let storageSize: Int
    
    /// The maximum number of bytes which can be requested
    let transferBlockSize: Int
    
    var storageFillRatio: Double {
        Double(numberOfRecordedBytes) / Double(storageSize)
    }
    
    var storageFillPercentage: Int {
        Int((storageFillRatio * 100).rounded())
    }

    var currentMeasurementStartTime: Date {
        time.nextMeasurement.addingTimeInterval(-Double(numberOfStoredMeasurements * measurementInterval))
    }

}

extension DeviceInfo {
    
    init(info: Data) throws {
        let date = Date()
        var data = info

        self.uniqueIdOfPowerCycle = try data.decodeFourByteInteger()
        self.numberOfRecordedBytes = try data.decodeTwoByteInteger()
        let secondsUntilNextMeasurement = try data.decodeTwoByteInteger()
        self.measurementInterval = try data.decodeTwoByteInteger()
        self.numberOfStoredMeasurements = try data.decodeTwoByteInteger()
        let totalNumberOfMeasurements = try data.decodeFourByteInteger()
        self.transferBlockSize = try data.decodeTwoByteInteger()
        self.storageSize = try data.decodeTwoByteInteger()
        let secondsSincePowerOn = try data.decodeFourByteInteger()
        let startSecondsOfCurrentRecording = try data.decodeFourByteInteger()
        self.dataChecksum = try data.decodeUInt16()
        self.wakeupReason = .init(rawValue: try data.getByte()) ?? .WAKEUP_UNDEFINED

        self.sensor0 = try data.decodeSensor()
        self.sensor1 = try data.decodeSensor()

        guard data.isEmpty else {
            log.error("\(data.count) bytes remaining in device info buffer")
            throw DeviceInfoError.missingData
        }

        self.time = .init(
            date: date,
            secondsSincePowerOn: secondsSincePowerOn,
            totalNumberOfMeasurements: totalNumberOfMeasurements,
            secondsUntilNextMeasurement: secondsUntilNextMeasurement,
            secondsOfFirstMeasurement: startSecondsOfCurrentRecording)
    }
}

extension DeviceInfo {
    
    static var mock: DeviceInfo {
        .init(
            uniqueIdOfPowerCycle: .random(in: 0...Int(UInt32.max)),
            numberOfRecordedBytes: 123,
            dataChecksum: .random(in: .min...UInt16.max),
            numberOfStoredMeasurements: 234,
            measurementInterval: 60,
            sensor0: .init(address: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], value: .value(21.0), date: .now.addingTimeInterval(-2)),
            sensor1: .init(address: [0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09], value: .value(19.0), date: .now.addingTimeInterval(-4)),
            wakeupReason: .WAKEUP_EXT0,
            time: .mock,
            storageSize: 10000,
            transferBlockSize: 180)
    }
}

extension DeviceInfo: Codable {

}
