using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions;

namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK
{
    internal partial class MPosControllerTTYUSBImplementation : MPosControllerTTYUSB
    {
        public int DeviceID { get; private set; }
        public MPosDeviceType DeviceType { get; private set; }
        public MPosInterfaceType InterfaceType { get; private set; } = MPosInterfaceType.MPOS_INTERFACE_BLUETOOTH;
        public bool IsOpen { get; private set; } = false;
        public MPosCommandMode CommandMode { get; private set; } = MPosCommandMode.MPOS_COMMAND_MODE_BGATE;
        public IMPosConnection _MPosConnection { get; private set; } = null;
        public string _ConnectionString { get; private set; }
        private MPosDeviceDataProcessor _DeviceDataProcessor = new MPosDeviceDataProcessor();
    }

    internal partial class MPosControllerTTYUSBImplementation : MPosControllerTTYUSB
    {
        public MPosControllerTTYUSBImplementation(int deviceType, int deviceId)
        {
            MPosDebug.WriteLine("TTYUSBImplementation Constructor : START");
            DeviceID = deviceId;
            DeviceType = (MPosDeviceType)deviceType;
            MPosDebug.WriteLine("TTYUSBImplementation Constructor : DeviceID = " + DeviceID);
            MPosDebug.WriteLine("TTYUSBImplementation Constructor : DeviceType = " + DeviceType);
            MPosDebug.WriteLine("TTYUSBImplementation Constructor : END");
        }
        public async Task<int> openService(uint timeout = 5 /*second*/)
        {
            if (IsOpen)
            {
                MPosDebug.WriteLine("TTYUSBImplementation openService : MPOS_ALREADY_OPEN");
                MPosDebug.WriteLine("TTYUSBImplementation openService : END");
                return (int)MPosResult.MPOS_ALREADY_OPEN;
            }

            return await openService((uint)DeviceID, timeout);
        }
        public async Task<int> openService(uint deviceId = 0, uint timeout = 5 /*second*/)
        {
            int result = (int)MPosResult.MPOS_SUCCESS;
            MPosDebug.WriteLine("TTYUSBImplementation openService : START");
            MPosDebug.WriteLine("TTYUSBImplementation openService : ConnectionString = " + _ConnectionString);
            MPosDebug.WriteLine("TTYUSBImplementation openService : CommandMode = " + CommandMode.ToString());

            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_DIRECT){
                MPosDebug.WriteLine("TTYUSBImplementation openService : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            _MPosConnection = MPosConnectionBuilderImplementation.Build(InterfaceType, _ConnectionString, (int)CommandMode);

            if (_MPosConnection == null){
                MPosDebug.WriteLine("TTYUSBImplementation openService : MPosConnectionBuilder.Current.Build failed.");
                MPosDebug.WriteLine("TTYUSBImplementation openService : END");
                return (int)MPosResult.MPOS_FAIL_NOT_CONNECT;
            }

            try
            {
                _DeviceDataProcessor.initDataStream();
                _MPosConnection.addDataReadEvent(onDataReadEvent);
                _MPosConnection.addDisconnectedEvent(onDisconnectedEvent);
                _MPosConnection.addConnectedEvent(onConnectedEvent);
                _MPosConnection.addDeviceListUpdateEvent(onDeviceListUpdateEvent);

                if (await _MPosConnection.openAsync(DeviceType, timeout)){
                    IsOpen = true;

                    if (deviceId <= 0 || deviceId >= 255)
                        deviceId = (uint)await _MPosConnection.getLowestDeviceId(1500, this);

                    if (!await _MPosConnection.isAvailableDeviceId(this, deviceId)){
                        IsOpen = false;
                    }else{
                        DeviceID = (int)deviceId;
                        byte[] commandToWrite = null;
                        commandToWrite = MPosBgateCmdBuilder.getReadModeCmd((uint)DeviceID, BgateDeviceReadMode.AUTO_ENABLE);
                        //commandToWrite = MPosBgateCmdBuilder.addMPosHeaderData((uint)DeviceID, commandToWrite);
                        await _MPosConnection.writeAsync(DeviceID, commandToWrite, false, false);
                    }
                }
            }catch (Exception ex){
                MPosDebug.WriteLine("TTYUSBImplementation openService : ex.Message = " + ex.Message);
                IsOpen = false;
            }

            if (!IsOpen){
                await _MPosConnection.closeAsync();
                _MPosConnection = null;
                result = (int)MPosResult.MPOS_FAIL_NOT_CONNECT;
            }else{
                result = (int)MPosResult.MPOS_SUCCESS;
            }

            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE && result == (int)MPosResult.MPOS_SUCCESS){
                MPosConnectionBuilderImplementation.AddDevice(DeviceID);
            }

            MPosDebug.WriteLine("TTYUSBImplementation openService : END");
            return result;
        }
        public async Task<int> closeService(uint closeTimeout = 0 /*second*/)
        {
            MPosDebug.WriteLine("TTYUSBImplementation closeService : START");
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
                MPosConnectionBuilderImplementation.RemoveDevice(DeviceID);
            IsOpen = false;
            statusUpdateEvent = null;
            dataEvent = null;
            //_DeviceDataProcessor.clearDataStream();

            if (_MPosConnection != null){
                _MPosConnection.removeDataReadEvent(onDataReadEvent);
                _MPosConnection.removeDisconnectedEvent(onDisconnectedEvent);
                _MPosConnection.removeConnectedEvent(onConnectedEvent);
                _MPosConnection.removeDeviceListUpdateEvent(onDeviceListUpdateEvent);
                await _MPosConnection.closeAsync(closeTimeout);
                _MPosConnection = null;
            }

            MPosDebug.WriteLine("TTYUSBImplementation closeService : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public bool isOpen()
        {
            return IsOpen;
        }
        public int selectCommandMode(int commandMode)
        {
            MPosDebug.WriteLine("TTYUSBImplementation selectCommandMode : START");
            MPosDebug.WriteLine("TTYUSBImplementation selectCommandMode : commandMode = " + commandMode);

            if (commandMode == (int)MPosCommandMode.MPOS_COMMAND_MODE_DIRECT)
            {
                MPosDebug.WriteLine("ScannerImplementation selectCommandMode : MPOS_FAIL_INVALID_PARAMETER");
                MPosDebug.WriteLine("ScannerImplementation selectCommandMode : END");
                return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
            }

            MPosDebug.WriteLine("ScannerImplementation selectCommandMode : END");
            return (int)MPosResult.MPOS_SUCCESS;
        }
        public int selectInterface(int interfaceType, string address)
        {
            MPosDebug.WriteLine("TTYUSBImplementation selectInterface : START");
            MPosDebug.WriteLine("TTYUSBImplementation selectInterface : interfaceType = " + interfaceType + " address = " + address);

            bool isMember = Enum.GetValues(typeof(MPosInterfaceType)).Cast<int>().Any(x => x == (int)interfaceType);

            if (isMember)
            {
                InterfaceType = (MPosInterfaceType)interfaceType;
                _ConnectionString = address;
                MPosDebug.WriteLine("TTYUSBImplementation selectInterface : _ConnectionString = " + _ConnectionString);
                MPosDebug.WriteLine("TTYUSBImplementation selectInterface : END");
                return (int)MPosResult.MPOS_SUCCESS;
            }

            MPosDebug.WriteLine("TTYUSBImplementation selectInterface : MPOS_FAIL_INVALID_PARAMETER");
            MPosDebug.WriteLine("TTYUSBImplementation selectInterface : END");
            return (int)MPosResult.MPOS_FAIL_INVALID_PARAMETER;
        }
        public async Task<int> setTransaction(int transactionMode)
        {
            return (int)MPosResult.MPOS_FAIL_NOT_SUPPORT;
        }
        public async Task<int> directIO(byte[] data)
        {
            MPosDebug.WriteLine("PrinterImplementation directIO : START");

            if (IsOpen == false || _MPosConnection == null){
                MPosDebug.WriteLine("TTYUSBImplementation directIO : MPOS_FAIL_NO_OPEN");
                MPosDebug.WriteLine("TTYUSBImplementation directIO : END");
                return (int)MPosResult.MPOS_FAIL_NO_OPEN;
            }

            int writtenBytes = 0;
            if (data != null){
                MPosDebug.WriteLine("TTYUSBImplementation directIO : data.Length = " + data.Length);
                writtenBytes = await _MPosConnection.writeAsync(DeviceID, data, false, true);
                MPosDebug.WriteLine("TTYUSBImplementation directIO : written bytes = " + writtenBytes);
            }

            MPosDebug.WriteLine("TTYUSBImplementation directIO : END");
            if (writtenBytes <= 0)
                return (int)MPosResult.MPOS_FAIL;
            return (int)MPosResult.MPOS_SUCCESS;
        }
    }

    internal partial class MPosControllerTTYUSBImplementation : MPosControllerTTYUSB
    {
        public event Action<byte[]> dataEvent;
        public event Action<uint> statusUpdateEvent;
        private async void onConnectedEvent()
        {
            MPosDebug.WriteLine("TTYUSBImplementation onConnectedEvent");
            if (statusUpdateEvent == null)
                MPosDebug.WriteLine("TTYUSBImplementation onConnectedEvent : statusUpdateEvent is null");
            MPosDebug.WriteLine("TTYUSBImplementation onConnectedEvent : fireStatusUpdateEvent");
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_NORMAL);
        }
        private void onDisconnectedEvent()
        {
            MPosDebug.WriteLine("TTYUSBImplementation onDisconnectedEvent");
            if (statusUpdateEvent == null)
                MPosDebug.WriteLine("TTYUSBImplementation onDisconnectedEvent : statusUpdateEvent is null");
            MPosDebug.WriteLine("TTYUSBImplementation onDisconnectedEvent : fireStatusUpdateEvent");
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
        }
        private void onDataReadEvent(int deviceId, byte[] data)
        {
            MPosDebug.WriteLine("TTYUSBImplementation onDataReadEvent : deviceId = " + deviceId + ", DeviceID = " + DeviceID);
            if (data == null)
                return;
            if (CommandMode == MPosCommandMode.MPOS_COMMAND_MODE_BGATE)
            {
                if (deviceId != DeviceID)
                    return;
            }
            //DeviceDataProcessor.writeData(data);
            if (dataEvent == null)
                MPosDebug.WriteLine("TTYUSBImplementation onDataReadEvent : dataEvent is null");
            dataEvent?.Invoke(data);
        }
        private void onDeviceListUpdateEvent(int[] devList){
            if (devList.Any(x => x == DeviceID) == false) {
                statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_PRINTER_ERROR);
                return;
            }
            statusUpdateEvent?.Invoke((uint)MPosPrinterStatus.MPOS_STATUS_NORMAL);
        }
    }
}
