import { NativeEventEmitter, DeviceEventEmitter, EmitterSubscription, NativeModules, Platform } from 'react-native';
import { MPosControllerBase } from "./MPosControllerBase"
import { APIResponse } from "./constants/APIResponse";
import {
    POS_CodePage,
    POS_InternationalCharset,
    POS_CutType,
    POS_PageMode,
    POS_Direction,
    POS_FontType,
    POS_Bold,
    POS_Underline,
    POS_Reverse,
    POS_Alignment,
    POS_DitheringType,
    POS_CompressType,
    POS_BarcodeType,
    POS_Barcode_HRI,
    POS_QR_Model,
    POS_QR_EccLevel,
    POS_PDF417_Symbol,
    POS_PDF417_ECC_Level,
    POS_Status,
    POS_Drawer_Pin
} from "./constants/Constants_Pos";

const { MPosControllerPrinterProxy } = NativeModules

export class MPosControllerPrinter implements MPosControllerBase {
    private statusEventEmitter: NativeEventEmitter = new NativeEventEmitter(MPosControllerPrinterProxy);
    private outputCompletedEventEmitter: NativeEventEmitter = new NativeEventEmitter(MPosControllerPrinterProxy);
    private dataEventEmitter: NativeEventEmitter = new NativeEventEmitter(MPosControllerPrinterProxy);

    private statusEventSubscription?: EmitterSubscription;
    private outputCompletedEventSubscription?: EmitterSubscription;
    private dataEventSubscription?: EmitterSubscription;

    statusUpdatedEvent?: (deviceid: number, status: number) => void;
    outputCompletedEvent?: (deviceid: number) => void;
    dataEvent?: (deviceid: number, data: Array<number>) => void;

    private readonly isWindows: boolean;

    constructor() {
        this.isWindows = Platform.OS === 'windows';

        if (Platform.OS === 'ios') {
            this.statusEventSubscription = this.statusEventEmitter.addListener("StatusUpdateEvent", (event) => {
                // object format: "{status: 0, deviceid: -1 }"
                console.log('MPosControllerPrinter: StatusUpdateEvent fired');
                console.info(event);
                if (!this.statusUpdatedEvent) { return; }
                this.statusUpdatedEvent(event.deviceid, event.status);
            });
            this.outputCompletedEventSubscription = this.outputCompletedEventEmitter.addListener("OutputCompletedEvent", (event) => {
                console.log('MPosControllerPrinter: outputCompletedEvent fired');
                if (!this.outputCompletedEvent) { return; }
                this.outputCompletedEvent(event.deviceid);
            });
            this.dataEventSubscription = this.dataEventEmitter.addListener("PrinterDataEvent", (event) => {
                console.log('MPosControllerPrinter: DataEventPrinter fired');
                if (!this.dataEvent) { return; }
                this.dataEvent(event.deviceid, event.data);
            });
        } else if (Platform.OS === 'windows') {
            DeviceEventEmitter.addListener("StatusUpdateEvent", (event) => {
                // object format: "{status: 0, deviceid: -1 }"
                console.log('MPosControllerPrinter: StatusUpdateEvent fired');
                console.info(event);
                if (!this.statusUpdatedEvent) { return; }

                const [deviceid, status] = event;
                event.deviceid = deviceid;
                event.status = status;
                
                this.statusUpdatedEvent(event.deviceid, event.status);
            });
            DeviceEventEmitter.addListener("OutputCompletedEvent", (event) => {                
                console.log('MPosControllerPrinter: OutputCompletedEvent fired');
                if (!this.outputCompletedEvent) { return; }
                this.outputCompletedEvent(event.deviceid);
            });
            DeviceEventEmitter.addListener("PrinterDataEvent", (event) => { 
                console.log('MPosControllerPrinter: DataEventPrinter fired ' + this.dataEvent);
                console.info(event);

                if (!this.dataEvent) { return; }

                const [deviceid, data] = event;
                event.deviceid = deviceid;
                event.data = data;

                this.dataEvent(event.deviceid, event.data);
            })
        } else {
            DeviceEventEmitter.addListener("StatusUpdateEvent", (event) => {
                // object format: "{status: 0, deviceid: -1 }"
                console.log('MPosControllerPrinter: StatusUpdateEvent fired');
                console.info(event);
                if (!this.statusUpdatedEvent) { return; }
                this.statusUpdatedEvent(event.deviceid, event.status);
            });
            DeviceEventEmitter.addListener("OutputCompletedEvent", (event) => {
                console.log('MPosControllerPrinter: OutputCompletedEvent fired');
                if (!this.outputCompletedEvent) { return; }
                this.outputCompletedEvent(event.deviceid);
            });
            DeviceEventEmitter.addListener("PrinterDataEvent", (event) => {
                console.log('MPosControllerPrinter: DataEventPrinter fired');
                if (!this.dataEvent) { return; }
                this.dataEvent(event.deviceid, event.data);
            })
        }
    }

    async selectInterface(interfaceType: number, adddress: string): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.selectInterface([interfaceType, adddress]);
            } else {
                return await MPosControllerPrinterProxy.selectInterface(Number(interfaceType), adddress);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async selectCommandMode(commandMode: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.selectCommandMode(Number(commandMode));
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async openService(deviceID: number, timeout: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.openService([deviceID, timeout]);
            } else {
                return await MPosControllerPrinterProxy.openService(Number(deviceID), timeout);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async closeService(timeout: number): Promise<number> {
        try {
            this.statusEventSubscription?.remove();
            this.outputCompletedEventSubscription?.remove();
            this.dataEventSubscription?.remove();

            return await MPosControllerPrinterProxy.closeService(timeout);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async isOpen(): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.isOpen();
        } catch (e) {
            return 0;
        }
    }

    async getDeviceId(): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.getDeviceId();
        } catch (e) {
            return -1;
        }
    }

    async directIO(data: Array<number>): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.directIO(data);
        } catch (e) {
            return -1;
        }
    }

    async setTransaction(mode: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setTransaction(mode);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    /*
    async setTransaction(mode: number, outputCompletedCallback?: () => void): Promise<number> {
        try {
            if (outputCompletedCallback) {
                this.outputCompletedEvent = outputCompletedCallback;
            }
            return await MPosControllerPrinterProxy.setTransaction(mode);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }
    */

    async setReadMode(mode: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setReadMode(mode);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setKeepNetworkConnection(number: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setKeepNetworkConnection(Number(number));
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async getSDKVersion(): Promise<string> {
        try {
            return await MPosControllerPrinterProxy.getSDKVersion();
        } catch (e) {
            return "";
        }
    }

    async setCharacterset(characterset: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setCharacterSet(characterset);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setInternationalCharacterset(internationalCharset: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setInternationalCharacterSet(internationalCharset);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async cutPaper(cutType: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.cutPaper(cutType);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setPagemode(mode: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setPagemode(mode);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setPagemodePrintArea(x: number, y: number, width: number, height: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.setPagemodePrintArea([x, y, width, height]);
            } else {
                return await MPosControllerPrinterProxy.setPagemodePrintArea(x, y, width, height);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setPagemodeDirection(direction: number): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.setPagemodeDirection(direction);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async setPagemodePosition(x: number, y: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.setPagemodePosition([x, y]);
            } else {
                return await MPosControllerPrinterProxy.setPagemodePosition(x, y);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printText(
        data: string,
        fontType: number,
        fontWidth: number,
        fontHeight: number,
        bold: number,
        underline: number,
        reverse: number,
        alignment: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printText([data, fontType, fontWidth, fontHeight, bold, underline, reverse, alignment]);
            } else {
                return await MPosControllerPrinterProxy.printText(data, fontType, fontWidth, fontHeight, bold, underline, reverse, alignment);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printImageFile(
        filename: string,
        width: number,
        alignment: number,
        threshold: number,
        ditheringType: number,
        compressType: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printImageFile([filename, width, alignment, threshold, ditheringType, compressType]);
            } else {
                return await MPosControllerPrinterProxy.printImageFile(filename, width, alignment, threshold, ditheringType, compressType);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printBase64Image(
        base64String: string,
        width: number,
        alignment: number,
        threshold: number,
        ditheringType: number,
        compressType: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printBase64Image([base64String, width, alignment, threshold, ditheringType, compressType]);
            } else {
                return await MPosControllerPrinterProxy.printBase64Image(base64String, width, alignment, threshold, ditheringType, compressType);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printPDFFile(
        filename: string,
        width: number,
        alignment: number,
        startPage: number,
        endPage: number,
        threshold: number,
        ditheringType: number,
        compressType: number): Promise<number> {
        if (this.isWindows) {
            return APIResponse.RES_NOT_SUPPORT;
        }
        try {
            return await MPosControllerPrinterProxy.printPDFFile(filename, width, alignment, startPage, endPage, threshold, ditheringType, compressType);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async print1DBarcode(
        data: string,
        symbology: number,
        barWidth: number,
        height: number,
        alignment: number,
        textPosition: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.print1DBarcode([data, symbology, barWidth, height, alignment, textPosition]);
            } else {
                return await MPosControllerPrinterProxy.print1DBarcode(data, symbology, barWidth, height, alignment, textPosition);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printQRCode(
        data: string,
        model: number,
        alignment: number,
        moduleSize: number,
        eccLevel: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printQRCode([data, model, alignment, moduleSize, eccLevel]);
            } else {
                return await MPosControllerPrinterProxy.printQRCode(data, model, alignment, moduleSize, eccLevel);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printPDF417(
        data: string,
        symbol: number,
        alignment: number,
        columnNumber: number,
        rowNumber: number,
        moduleWidth: number,
        moduleHeight: number,
        eccLevel: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printPDF417(
                    [data, symbol, alignment, columnNumber, rowNumber, moduleWidth, moduleHeight, eccLevel]
                );
            } else {
                return await MPosControllerPrinterProxy.printPDF417(
                    data, symbol, alignment, columnNumber, rowNumber, moduleWidth, moduleHeight, eccLevel
                );
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printDataMatrix(
        data: string,
        alignment: number,
        moduleSize: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printDataMatrix([data, alignment, moduleSize]);
            } else {
                return await MPosControllerPrinterProxy.printDataMatrix(data, alignment, moduleSize);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printLine(
        x1: number,
        y1: number,
        x2: number,
        y2: number,
        thickness: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printLine([x1, y1, x2, y2, thickness]);
            } else {
                return await MPosControllerPrinterProxy.printLine(x1, y1, x2, y2, thickness);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async printBox(
        left: number,
        top: number,
        right: number,
        bottom: number,
        thickness: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.printBox([left, top, right, bottom, thickness]);
            } else {
                return await MPosControllerPrinterProxy.printBox(left, top, right, bottom, thickness);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async checkPrinterStatus(): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.checkPrinterStatus();
        } catch (e) {
            return POS_Status.POS_STATUS_FAIL;
        }
    }

    async checkBattStatus(): Promise<number> {
        try {
            return await MPosControllerPrinterProxy.checkBattStatus();
        } catch (e) {
            return POS_Status.POS_STATUS_FAIL;
        }
    }

    async asbEnable(enable: number, statusEventCallback: (deviceid: number, status: number) => void): Promise<number> {
        try {
            if (enable > 0) {
                this.statusUpdatedEvent = statusEventCallback;
            } else {
                this.statusUpdatedEvent = () => { };
            }
            return await MPosControllerPrinterProxy.asbEnable(enable);
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async openDrawer(
        pinNumber: number,
        onTimePulse: number,
        offTimePulse: number): Promise<number> {
        try {
            if (this.isWindows) {
                return await MPosControllerPrinterProxy.openDrawer([pinNumber, onTimePulse, offTimePulse]);
            } else {
                return await MPosControllerPrinterProxy.openDrawer(pinNumber, onTimePulse, offTimePulse);
            }
        } catch (e) {
            return APIResponse.RES_FAIL;
        }
    }

    async getModelName(): Promise<string> {
        try {
            return await MPosControllerPrinterProxy.getModelName();
        } catch {
            return "";
        }
    }

    async getFirmwareVersion(): Promise<string> {
        try {
            return await MPosControllerPrinterProxy.getFirmwareVersion();
        } catch {
            return "";
        }
    }

    async getOccuredData(): Promise<Array<number>|null> {
        try {
            return await MPosControllerPrinterProxy.getOccuredData();
        } catch (e) {
            return Promise.resolve(null);
        }
     }
}