//
//  ViewController.swift
//  btprint
//
//  Created by woosimsystems on 2021/05/12.
//  Copyright © 2021 Woosim Systems. All rights reserved.
//

import UIKit
import ExternalAccessory
import woosim302

class ViewController: UIViewController {

    @IBOutlet weak var rcvDataLabel: UILabel!
    
    var selectedAccessory: EAAccessory?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
 
        NotificationCenter.default.addObserver(self, selector: #selector(accessoryDidDisconnect), name: NSNotification.Name.EAAccessoryDidDisconnect, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(sessionDataReceived), name: NSNotification.Name(rawValue: WSSessionDataReceivedNotification), object: nil)
        EAAccessoryManager.shared().registerForLocalNotifications()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.EAAccessoryDidDisconnect, object: nil)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: WSSessionDataReceivedNotification), object: nil)
        EAAccessoryManager.shared().unregisterForLocalNotifications()
    }

    @objc private func accessoryDidDisconnect(notification: NSNotification) {
        guard selectedAccessory != nil else {
            return
        }
        let disconnectedAccessory = notification.userInfo?[EAAccessoryKey] as? EAAccessory
        if disconnectedAccessory?.connectionID == selectedAccessory?.connectionID {
            navigationItem.rightBarButtonItem?.title = "Printer"
            selectedAccessory = nil
        }
    }
    
    @objc private func sessionDataReceived(notification: NSNotification) {
        var rcvData = Data()
        let session = SessionController.shared
        while session.readBytesAvailable() > 0 {
            let data = session.readData(bytesToRead: session.readBytesAvailable())
            if data.count > 0 {
                rcvData.append(data)
            }
        }
        let packet = WSPacket(rcvData)
        if packet.packetType == .MSR {
            let tracks = packet.packetData as! [Data]
            var strData = "MSR Track 1\r\n"
            strData += String(data: tracks[0], encoding: .ascii)!
            strData += "\r\n\nMSR Track 2\r\n"
            strData += String(data: tracks[1], encoding: .ascii)!
            strData += "\r\n\nMSR Track 3\r\n"
            strData += String(data: tracks[2], encoding: .ascii)!
            rcvDataLabel.text = strData
        } else {
            rcvDataLabel.text = packet.packetData as? String
        }
    }

    @IBAction func selectPrinter(_ sender: Any) {
        if selectedAccessory != nil {
            // there is an opened accessory
            showOpenedAccessoryAlert()
        } else if EAAccessoryManager.shared().connectedAccessories.count == 0 {
            // no connected accessory
            showNoConnectedAccessoryAlert()
        } else {
            // list connected accessories up
            showConnectedAccessoriesAlert()
        }
    }
    
    private func showOpenedAccessoryAlert() {
        let alert = UIAlertController(title: "Close Session",
                                      message: "Do you want to close session for the accessory?",
                                      preferredStyle: UIAlertController.Style.alert)
        let okAction = UIAlertAction(title: "Yes", style: .default, handler: {action in
            SessionController.shared.closeSession()
            self.navigationItem.rightBarButtonItem?.title = "Printer"
            self.selectedAccessory = nil
        })
        let cancelAction = UIAlertAction(title: "No", style: .default, handler: nil)
        alert.addAction(okAction)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: nil)
    }
    
    private func showNoConnectedAccessoryAlert() {
        let alert = UIAlertController(title: "No Accessory Connected",
                                      message: "Do you want to move to Settings?",
                                      preferredStyle: UIAlertController.Style.alert)
        let okAction = UIAlertAction(title: "Yes", style: .default, handler: {action in
            let url = URL(string: "App-prefs:root=Bluetooth")
            UIApplication.shared.open(url!, options: [:], completionHandler: nil)
        })
        let cancelAction = UIAlertAction(title: "No", style: .default, handler: nil)
        alert.addAction(okAction)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: nil)
    }
    
    private func showConnectedAccessoriesAlert() {
        let supportedProtocolsStrings = Bundle.main.object(forInfoDictionaryKey: "UISupportedExternalAccessoryProtocols") as! [String]
        let alert = UIAlertController(title: "Select Printer",
                                      message: nil,
                                      preferredStyle: UIAlertController.Style.actionSheet)
        // add accessoryList item
        for accessory in EAAccessoryManager.shared().connectedAccessories {
            let selectAction = UIAlertAction(title: accessory.modelNumber, style: .default, handler: {action in
                for supportedProtocolsString in supportedProtocolsStrings {
                    for protocolString in accessory.protocolStrings {
                        // session open if there is the same protocol string
                        if supportedProtocolsString == protocolString {
                            self.selectedAccessory = accessory
                            if SessionController.shared.openSession(accessory, protocolString) {
                                self.navigationItem.rightBarButtonItem?.title = accessory.modelNumber
                            }
                            return
                        }
                    }
                }
                // alert that there is no same protocol string
                let protocolErrorAlert = UIAlertController(
                    title: "Protocol String Error",
                    message: "Selected accessory does not support assigned protocol string",
                    preferredStyle: UIAlertController.Style.alert)
                self.present(protocolErrorAlert, animated: true, completion: nil)
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                    protocolErrorAlert.dismiss(animated: true, completion: nil)
                }
            })
            alert.addAction(selectAction)
        }
        let moveSettingsAction = UIAlertAction(title: "Other printer", style: .default, handler: {action in
            let url = URL(string: "App-prefs:root=Bluetooth")
            UIApplication.shared.open(url!, options: [:], completionHandler: nil)
        })
        let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
        alert.addAction(moveSettingsAction)
        alert.addAction(cancelAction)
        alert.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
        present(alert, animated: true, completion: nil)
    }

    @IBAction func printText(_ sender: Any) {
        let data = NSMutableData()
        data.append(WSCmd.initPrinter())
        data.append(WSCmd.alignContent(.Right))
        let logo = UIImage(named: "logo.jpg")!
        data.append(WSImage.printImage(logo, dithering: false, compress: false))
        data.append(WSCmd.alignText(.Center))
        data.append(WSCmd.extendFont(width: 2, height: 2))
        data.append(Data("Receipt\n".utf8))
        data.append(WSCmd.alignText(.Left))
        data.append(WSCmd.extendFont(width: 1, height: 1))
        data.append(WSCmd.printData())
        data.append(Data("NAME".utf8))
        data.append(WSCmd.alignText(.Right))
        data.append(Data("Woosim Coffee\n".utf8))
        data.append(WSCmd.alignText(.Left))
        data.append(Data("MASTER".utf8))
        data.append(WSCmd.alignText(.Right))
        data.append(Data("Gil-dong Hong\n".utf8))
        data.append(WSCmd.alignText(.Left))
        data.append(Data("ADDRESS".utf8))
        data.append(WSCmd.alignText(.Right))
        data.append(Data("#60, Sandan-ro\n".utf8))
        data.append(Data("388beon-gil, Galsan-myeon\n".utf8))
        data.append(Data("Hongseong-gun, Chungnam-do\n".utf8))
        data.append(Data("Republic of Korea\n".utf8))
        data.append(WSCmd.alignText(.Left))
        data.append(Data("HELP DESK".utf8))
        data.append(WSCmd.alignText(.Right))
        data.append(WSCmd.setBold(true))
        data.append(WSCmd.setReverse(true))
        data.append(Data("+82-41-339-3700\n".utf8))
        data.append(WSCmd.alignText(.Left))
        data.append(WSCmd.setBold(false))
        data.append(WSCmd.setReverse(false))
        data.append(Data("--------------------------------\n".utf8))
        data.append(Data("Cafe Mocha          2       4.50\n".utf8))
        data.append(Data("Cafe Latte          1       4.00\n".utf8))
        data.append(Data("Cappucino           1       4.00\n".utf8))
        data.append(Data("--------------------------------\n".utf8))
        data.append(WSCmd.setBold(true))
        data.append(Data("Total                     $17.00\n\n".utf8))
        data.append(WSCmd.setBold(false))
        data.append(WSCmd.alignContent(.Center))
        data.append(WSCmd.setBarcodeWidth(2))
        data.append(WSCmd.setBarcodeHeight(60))
        data.append(WSCmd.setBarcodeHri(true))
        data.append(WSCmd.writeBarcode("051320211323+0900", type: .Code128))
        data.append(WSCmd.printFeed(line: 3))
        data.append(Data("Hello\n".utf8))
        var encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosPortuguese.rawValue)))
        data.append(WSCmd.setCodeTable(.CP860, mcu: .RX))
        data.append("Olá\n".data(using: encoding) ?? Data())
        encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosGreek.rawValue)))
        data.append(WSCmd.setCodeTable(.CP737, mcu: .RX))
        data.append("γεια\n".data(using: encoding) ?? Data())
        encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.dosRussian.rawValue)))
        data.append(WSCmd.setCodeTable(.CP866, mcu: .RX))
        data.append("Привет\n".data(using: encoding) ?? Data())
        // Korean is only working property with Korean version FW
        encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.EUC_KR.rawValue)))
        data.append(WSCmd.setCodeTable(.DBCS, mcu: .RX))
        data.append("안녕하세요\n".data(using: encoding) ?? Data())
        data.append(WSCmd.printFeed(line: 3))
        SessionController.shared.writeData(data as Data)
    }
    
    @IBAction func printImage(_ sender: Any) {
        let data = NSMutableData()
        data.append(WSCmd.initPrinter())
        data.append(WSCmd.setPageMode())
        data.append(WSCmd.pageModeSetArea(x: 0, y: 0, width: 384, height: 400))
        data.append(WSImage.drawRect(x: 0, y: 0, width: 384, height: 395, thick: 5))
        data.append(WSImage.drawLine(x1: 20, y1: 0, x2: 20, y2: 395, thick: 3))
        data.append(WSImage.drawLine(x1: 364, y1: 0, x2: 364, y2: 395, thick: 3))
        data.append(WSImage.drawLine(x1: 0, y1: 20, x2: 384, y2: 20, thick: 3))
        data.append(WSImage.drawLine(x1: 0, y1: 375, x2: 384, y2: 375, thick: 3))
        data.append(WSImage.drawEllipse(x: 192, y: 200, radiusW: 160, radiusH: 160, thick: 4))
        let image = UIImage(named: "logo.jpg")!
        data.append(WSImage.drawImage(image, x: 118, y: 62, dithering: true, compress: false))
        if let image2 = WSImage.adjustImage(image, brightness: -0.3, contrast: 2.0) {
            data.append(WSImage.drawImage(image2, x: 118, y: 162, dithering: true, compress: false))
        } else {
            data.append(WSImage.drawImage(image, x: 118, y: 162, dithering: true, compress: false))
        }
        data.append(WSImage.drawImage(image, x: 118, y: 262, dithering: false, compress: false))
        data.append(WSCmd.pageModePrintStdMode())
        data.append(WSCmd.printData())
        let logo = UIImage(named: "logo.bmp")!
        data.append(WSImage.printImage(logo, dithering: false, compress: true))
        data.append(WSCmd.printFeed(line: 3))
        SessionController.shared.writeData(data as Data)
    }
    
    @IBAction func print1DBarcode(_ sender: Any) {
        let data = NSMutableData()
        data.append(WSCmd.initPrinter())
        data.append(WSCmd.setBarcodeWidth(2))
        data.append(WSCmd.setBarcodeHeight(60))
        data.append(WSCmd.setBarcodeHri(true))
        data.append(Data("UPC-A\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .UPCA))
        data.append(WSCmd.printData())
        data.append(Data("UPC-E\n".utf8))
        data.append(WSCmd.writeBarcode("065100004327", type: .UPCE))
        data.append(WSCmd.printData())
        data.append(Data("EAN13\n".utf8))
        data.append(WSCmd.writeBarcode("0123456789012", type: .EAN13))
        data.append(WSCmd.printData())
        data.append(Data("EAN8\n".utf8))
        data.append(WSCmd.writeBarcode("01234567", type: .EAN8))
        data.append(WSCmd.printData())
        data.append(Data("Code39\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .Code39))
        data.append(WSCmd.printData())
        data.append(Data("ITF\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .ITF))
        data.append(WSCmd.printData())
        data.append(Data("Codabar\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .Codabar))
        data.append(WSCmd.printData())
        data.append(Data("Code93\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .Code93))
        data.append(WSCmd.printData())
        data.append(Data("Code128\n".utf8))
        data.append(WSCmd.writeBarcode("01234567890", type: .Code128))
        data.append(WSCmd.printFeed(line: 3))
        SessionController.shared.writeData(data as Data)
    }
    
    @IBAction func print2DBarcode(_ sender: Any) {
        let data = NSMutableData()
        data.append(WSCmd.initPrinter())
        data.append(WSCmd.setBarcodeWidth(2))
        data.append(WSCmd.setBarcodeHri(false))
        data.append(Data("PDF417\n".utf8))
        data.append(WSCmd.writePDF417(Data("http://www.woosim.com/".utf8), column: 3, secureLevel: 4, ratio: 2))
        data.append(WSCmd.printData())
        data.append(Data("DataMatrix\n".utf8))
        data.append(WSCmd.writeDataMatrix(Data("http://www.woosim.com/".utf8), height: 0, width: 0, size: 6))
        data.append(WSCmd.printData())
        data.append(Data("QR Code\n".utf8))
        data.append(WSCmd.writeQRcode(Data("http://www.woosim.com/".utf8), version: 0, ecLevel: "M", size: 5))
        data.append(WSCmd.printData())
        data.append(Data("Micro PDF417\n".utf8))
        data.append(WSCmd.writeMicroPDF417(Data("http://www.woosim.com/".utf8), column: 2, row: 0, ratio: 2))
        data.append(WSCmd.printData())
        data.append(Data("Truncated PDF417\n".utf8))
        data.append(WSCmd.writeTruncatedPDF417(Data("http://www.woosim.com/".utf8), column: 3, secureLevel: 4, ratio: 2))
        data.append(WSCmd.printData())
        data.append(Data("Maxicode\n".utf8))
        data.append(WSCmd.writeMaxicode(Data("ABCDE12345abcde".utf8), mode: 4))
        data.append(WSCmd.printFeed(line: 3))
        SessionController.shared.writeData(data as Data)
    }
    
    @IBAction func printGS1Databar(_ sender: Any) {
        let data = NSMutableData()
        data.append(WSCmd.initPrinter())
        data.append(Data("GS1 Databar Omnidirectional\n".utf8))
        data.append(WSCmd.writeGS1Databar("0001234567890", type: .Omnidirectional, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Truncated\n".utf8))
        data.append(WSCmd.writeGS1Databar("0001234567890", type: .Truncated, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Stacked\n".utf8))
        data.append(WSCmd.writeGS1Databar("0001234567890", type: .Stacked, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Stacked Omnidirectional\n".utf8))
        data.append(WSCmd.writeGS1Databar("0001234567890", type: .StackedOmnidirectional, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Limited\n".utf8))
        data.append(WSCmd.writeGS1Databar("0001234567890", type: .Limited, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Expanded\n".utf8))
        data.append(WSCmd.writeGS1Databar("[01]90012345678908[3103]012233", type: .Expanded, segment: 2))
        data.append(WSCmd.printData())
        data.append(Data("GS1 Databar Expanded Stacked\n".utf8))
        data.append(WSCmd.writeGS1Databar("[01]90012345678908[3103]012233[15]991231", type: .ExpandedStacked, segment: 4))
        data.append(WSCmd.printFeed(line: 3))
        SessionController.shared.writeData(data as Data)
    }
    
    @IBAction func setMsrMode(_ sender: Any) {
        rcvDataLabel.text = ""
        SessionController.shared.writeData(WSCmd.setMsrModeTripleTrack())
    }
    
    @IBAction func cancelMsrMode(_ sender: Any) {
        SessionController.shared.writeData(WSCmd.exitMsrMode())
    }
    
    @IBAction func getDeviceName(_ sender: Any) {
        rcvDataLabel.text = ""
        SessionController.shared.writeData(WSCmd.requestDeviceName())
    }
    
    @IBAction func getStatus(_ sender: Any) {
        rcvDataLabel.text = ""
        SessionController.shared.writeData(WSCmd.requestStatus())
    }
}

