//
//  PrinterListView.swift
//  SiiLibSample
//

import SwiftUI

struct PrinterListView: View {
    @ObservedObject var siiPrinterModel: SiiPrinterModel
    
    @Binding var isShowPrinterListSheet: Bool
    
    @State var printerList : [SiiPrinter] = []
    @State var isDiscoveryComplete: Bool = true
    
    fileprivate static let printerDiscoverQueue = DispatchQueue(label: "com.sii-ps.printerDiscover")
    var body: some View {
        ZStack {
            NavigationView {
            VStack {
                List {
                    ForEach(printerList) { foundPrinter in
                        HStack { // Printer cells
                            VStack(alignment: .leading) {
                                Text(foundPrinter.address)
                                HStack {
                                    Text(foundPrinter.portType)
                                        .font(.footnote)
                                    Text(foundPrinter.seriesName)
                                        .font(.footnote)
                                }
                            }
                            Spacer()
                        }
                        .padding(1)
                        .contentShape(RoundedRectangle(cornerRadius: 5))
                        .onTapGesture {
                            // Set selected printer to viewModel and back to main view
                            siiPrinterModel.setAppStorage(printer: foundPrinter)
                            siiPrinterModel.getPrinter(printer: foundPrinter)
                            isShowPrinterListSheet = false
                        }
                    }
                }
                .listStyle(.plain)
            }
                .toolbar(content: {
                    ToolbarItem(placement: .topBarLeading, content: {
                        Button("close") {
                            isShowPrinterListSheet = false
                        }
                    })
                    // refresh button
                    ToolbarItem(placement: .topBarTrailing, content: {
                        Button(action: {
                            searchPrinter()
                        }, label: {
                            Image(systemName: "arrow.triangle.2.circlepath")
                                .resizable()
                                .scaledToFill()
                                .aspectRatio(contentMode: .fit)
                                .frame(width: 20, height: 20)
                                .padding(5)
                        })
                    })
                })
            }
            if siiPrinterModel.isShowPrinterListLoadingView == true {
                PrinterListLoadingView(siiPrinterModel: siiPrinterModel)
            }
        }
        .onAppear {
            // Start printer discovery when sheet appears
            searchPrinter()
        }
        .onDisappear() {
            // Cancel printer discovery immediately and clear printerList when sheet disappears
            cancelDiscoveryTcpPrinter()
            isShowPrinterListSheet = false
            printerList = []
        }
    }
    
    /// Printer discovery process
    func searchPrinter() {
        printerList = []
        PrinterListView.printerDiscoverQueue.async {
            siiPrinterModel.showPrinterListLoadingView(true)
            searchBluetoothPrinter()
            searchTcpPrinter()
            siiPrinterModel.showPrinterListLoadingView(false)
        }
    }
    
    /// Bluetooth printer discovery process
    func searchBluetoothPrinter() {
        var foundPrinterList = [SiiPrinter]()
        // Get accessories that connecting with current iOS device from EAAccessoryManager
        let accessories = EAAccessoryManager.shared().connectedAccessories
        if accessories.count > 0 {
            for obj: EAAccessory in accessories {
                // Get SII devices from all connected accessories
                if obj.protocolStrings.firstIndex(of: SII_EXTERNAL_ACCESSORY_PROTOCOL) != nil {
                    var printer = SiiPrinter()
                    // Determine connect type from device's macAddress:
                    // Mac address can be get: bluetooth device
                    // Mac address can not be get: USB device
                    let macAddressOptional = obj.value(forKey: "macAddress") as? String
                    var macAddress = ""
                    if let macAddressBinded = macAddressOptional {
                        macAddress = macAddressBinded
                    }
                    if macAddress.count == 0 {
                        printer.type = SII_PM_PRINTER_PORT_TYPE_USB
                    } else {
                        printer.type = SII_PM_PRINTER_PORT_TYPE_BLUETOOTH
                    }
                    // Set accessorie's name as printer address
                    printer.address = obj.name
                    // Get printer's port type name from port type
                    printer.portType = printer.getPrinterPortType(type: printer.type)
                    // Set printer's series name
                    printer.seriesName = obj.modelNumber
                    // Get printer's model name from series name
                    printer.modelName = printer.getPrinterModelName(seriesName: obj.modelNumber)
                    // Get printer's model number from model name
                    printer.model = printer.getPrinterModel(modelName: printer.modelName)
                    // Add current printer to printer list
                    foundPrinterList.append(printer)
                }
            }
            printerList = foundPrinterList
        }
    }
    
    /// Tcp printer discovery process
    func searchTcpPrinter() {
        var foundPrinterList = [SiiPrinter]()
        var lanPrinterList:[Any] = []
        startDiscoveryTcpPrinter()
        // Repeat process until discovery completed
        while SampleFunctions.getPrinterDiscoveryComletion() != true {
            // Copy from previous founded printer list
            if printerList.count > 0 {
                foundPrinterList = printerList
            }
            lanPrinterList = SampleFunctions.getFoundPrinterSample()
            
            if lanPrinterList.count > 0 {
                for obj in lanPrinterList {
                    var printer = SiiPrinter()
                    // Set ip as printer's address
                    printer.address = (obj as AnyObject).ip
                    // Set printer's port type number as TCP and get port type name from port type
                    printer.type = SII_PM_PRINTER_PORT_TYPE_TCP
                    printer.portType = printer.getPrinterPortType(type: printer.type)
                    
                    // Set printer's series name
                    printer.seriesName = (obj as AnyObject).name
                    // Get printer's model name from series name
                    printer.modelName = printer.getPrinterModelName(seriesName: printer.seriesName)
                    // Get printer's model number from model name
                    printer.model = printer.getPrinterModel(modelName: printer.modelName)
                    
                    // Determine whether current printer is existed in previous founded printer list
                    var isPrinterExist: Bool = false
                    for existPrinter in foundPrinterList {
                        if printer.address == existPrinter.address {
                            isPrinterExist = true;
                            break;
                        }
                    }
                    // Add current printer to printer list if it is not in precious founded list
                    if isPrinterExist == false {
                        printerList.append(printer)
                    }
                }
            }
            Thread.sleep(forTimeInterval: 0.01)
        }
    }
}

/// Start lan printer discovery process
func startDiscoveryTcpPrinter() {
    SampleFunctions.startLanDiscoveryPrinterSample(1, 10000)
}

/// Cancel current discovery process immediately
func cancelDiscoveryTcpPrinter() {
    SampleFunctions.cancelDiscoveryPrinterSample()
}
