//
//  GMFG5GSignalHandler.swift
//  MVM5G
//
//  Created by Jason Beck on 5/11/21.
//  Copyright © 2021 Kyle. All rights reserved.
//
import CoreBluetooth

protocol GMFG5GCBandSignalDelegate: AnyObject {
    func updated5GCBandSignal(_ update: GMFG5GCBandSignalUpdate)
    func signal5GCBandNotReady()
}

private enum ResultKeys: String {
    case signalNoiseRatio       = "SINR"
    case referencePower         = "RSRP"
    case pci                    = "PCI"
    case ssbIndex               = "SSBI"
    case referenceSignalQuality = "RSRQ"
    case modulationOrder        = "MO"
    case mcsIndex               = "MCS " //This is on purpose. The firmware has a trailing space in the key name.
    case arfcn5G                = "5GARFCN"
    case ledColor               = "LED"
}

struct GMFG5GCBandSignalUpdate {
    var signalToNoiseRatio: Int = 0
    var referencePower: Int = 0
    var pci: Int = 0
    var ssbIndex: Int = 0
    var referenceSignalQuality: Int = 0
    var modulationOrder: String = "NA"
    var mcsIndex: Int = 0
    var arfcn5G: Int = 0
    var ledColor: GMFGCPELEDColor?
    var rawResults: [String: Any]?
}

class GMFG5GCBandSignalHandler: GMFGBLEHandlerProtocol {   

    private let multicast = MulticastDelegate<GMFG5GCBandSignalDelegate>()
    public func setDelegate(delegates: [GMFG5GCBandSignalDelegate?]) {
        multicast.add(delegates: delegates)
    }
    
    public func reset() {
        multicast.removeAll()
    }
    
    //MARK:- Helper
    private func rawKey(_ type: ResultKeys) -> String { return type.rawValue }
    private func rawStatus(_ type: GMFGCPELEDColor) -> String { return type.rawValue }
    
    public func get5GSignal(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) {
       peripheral.readValue(for: characteristic)
    }
    
    //It is possible to try and call the device before it's done loading
    public func deviceNotReady() {
        multicast.invokeForEachDelegate{ $0.signal5GCBandNotReady() }
    }
    
    public func response(response: [String : Any]) {
        if let validResponse = responseFormat(response: response) {
            multicast.invokeForEachDelegate{ $0.updated5GCBandSignal(validResponse) }
        }
    }
    
    func responseFormat(response: [String: Any]) -> GMFG5GCBandSignalUpdate? {
        guard let fiveGData = response["5GSignal"] as? [String: Any] else {
            return nil
        }
        var returnSignal = GMFG5GCBandSignalUpdate()
        if let signalToNoise = (fiveGData[rawKey(.signalNoiseRatio)] as? String), let snrInt = Int(signalToNoise) {
            returnSignal.signalToNoiseRatio = snrInt
        }
        if let referenceSignalPower = (fiveGData[rawKey(.referencePower)] as? String), let refPowerInt = Int(referenceSignalPower) {
            returnSignal.referencePower = refPowerInt
        }
        if let fiveGARFCN = (fiveGData[rawKey(.arfcn5G)] as? String), let arfcnInt = Int(fiveGARFCN) {
            returnSignal.arfcn5G = arfcnInt
        }
        if let referenceQuality = (fiveGData[rawKey(.referenceSignalQuality)] as? String), let sqInt = Int(referenceQuality) {
            returnSignal.referenceSignalQuality = sqInt
        }
        if let ssbi = (fiveGData[rawKey(.ssbIndex)] as? String), let ssbInt = Int(ssbi) {
            returnSignal.ssbIndex = ssbInt
        }
        if let ledColorRaw = (fiveGData[rawKey(.ledColor)] as? String), let ledColor = GMFGCPELEDColor(rawValue: ledColorRaw) {
            returnSignal.ledColor = ledColor
        }
        if let mcs = (fiveGData[rawKey(.mcsIndex)] as? String), let mcsInt = Int(mcs) {
            returnSignal.mcsIndex = mcsInt
        }
        if let pci = (fiveGData[rawKey(.pci)] as? String), let pciInt = Int(pci) {
            returnSignal.pci = pciInt
        }
        if let mo = (fiveGData[rawKey(.modulationOrder)] as? String) {
            returnSignal.modulationOrder = mo
        }
        returnSignal.rawResults = fiveGData
        return returnSignal
    }
}
