//
//  BluetoothConfig.swift
//  MVM5G
//
//  Created by Matt Bruce on 4/13/22.
//  Copyright © 2022 Kyle. All rights reserved.
//

import Foundation
import CoreBluetooth
import MVMCore

public enum BluetoothConfigError: Error, LocalizedError, CustomStringConvertible {
    case decode(key: String)
    case noJSONFound
    
    public var description: String {
        switch self {
        case .decode:
            return GMFGConstant.BLE.LogMsg.bleDecodeError
        case .noJSONFound:
            return GMFGConstant.BLE.LogMsg.bleModuleMissing
        }
    }
    
    public var errorDescription: String? {
       return description
    }
}

public struct BluetoothConfigModel: Codable, Equatable {
    var advertisedData: String
    var pin: String
    var bleAPIVersion: Double
    var bleSignalThreshold: Double
    var bleSignalLowerBound: Double
    var bleSignalUpperBound: Double
    var peripherals: [PeripheralModel]
    var services: [ServiceModel]
    var logger: ((String, String, [String: Any])-> Void)?
    
    enum CodingKeys: String, CodingKey {
        case bleAPIVersion
        case pin
        case bleSignalThreshold
        case bleSignalLowerBound
        case bleSignalUpperBound
        case advertisedData
        case bleAdv
        case service
    }

    public init(from decoder: Decoder) throws {
        let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
        let _bleAPIVersion = try typeContainer.decode(String.self, forKey: .bleAPIVersion, objectType: BluetoothConfigModel.self)
        let _bleSignalThreshold = try typeContainer.decode(String.self, forKey: .bleSignalThreshold, objectType: BluetoothConfigModel.self)
        let _bleSignalLowerBound = try typeContainer.decode(String.self, forKey: .bleSignalLowerBound, objectType: BluetoothConfigModel.self)
        let _bleSignalUpperBound = try typeContainer.decode(String.self, forKey: .bleSignalUpperBound, objectType: BluetoothConfigModel.self)

        guard let double = Double(_bleAPIVersion) else {
            throw BluetoothConfigError.decode(key: "bleAPIVersion: \(_bleAPIVersion)")
        }
        bleAPIVersion = double
        
        guard let double = Double(_bleSignalThreshold) else {
            throw BluetoothConfigError.decode(key: "bleSignalThreshold: \(_bleSignalThreshold)")
        }
        bleSignalThreshold = double
        
        guard let double = Double(_bleSignalLowerBound) else {
            throw BluetoothConfigError.decode(key: "bleSignalLowerBound: \(_bleSignalLowerBound)")
        }
        bleSignalLowerBound = double
        
        guard let double = Double(_bleSignalUpperBound) else {
            throw BluetoothConfigError.decode(key: "bleSignalUpperBound: \(_bleSignalUpperBound)")
        }
        bleSignalUpperBound = double        
        pin = try typeContainer.decode(String.self, forKey: .pin, objectType: BluetoothConfigModel.self)
        advertisedData = try typeContainer.decode(String.self, forKey: .advertisedData, objectType: BluetoothConfigModel.self)
        peripherals = try typeContainer.decode([PeripheralModel].self, forKey: .bleAdv, objectType: BluetoothConfigModel.self)
        services = try typeContainer.decode([ServiceModel].self, forKey: .service, objectType: BluetoothConfigModel.self)
    }
        
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(bleAPIVersion, forKey: .bleAPIVersion)
        try container.encode(pin, forKey: .pin)
        try container.encode(bleSignalThreshold, forKey: .bleSignalThreshold)
        try container.encode(bleSignalLowerBound, forKey: .bleSignalLowerBound)
        try container.encode(bleSignalUpperBound, forKey: .bleSignalUpperBound)
        try container.encode(advertisedData, forKey: .advertisedData)
        try container.encode(peripherals, forKey: .bleAdv)
        try container.encode(services, forKey: .service)
    }
    
    public static func == (lhs: BluetoothConfigModel, rhs: BluetoothConfigModel) -> Bool {
        return lhs.advertisedData == rhs.advertisedData &&
        lhs.pin == rhs.pin &&
        lhs.peripherals == rhs.peripherals &&
        lhs.services == rhs.services
    }
}
