using Prism.Commands;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Windows;
using unitechRFID;
using unitechRFID.Device.Params;
using unitechRFID.Diagnositics;
using unitechRFID.Params;
using unitechRFID.Reader;
using unitechRFID.Transport;
using unitechRFID.UHF.Params;
using unitechRFID.Util.Diagnotics;
using unitechRFIDSample.Model;

namespace unitechRFIDSample.ViewModels
{
    public class MainViewModel : BaseViewModel, IDisposable
    {
        bool _isOnInventory = false;

        #region UI Parameters
        #region Command
        private DelegateCommand<string> _operationCmd;
        public DelegateCommand<string> OperationCommand => _operationCmd ??= new DelegateCommand<string>(OnOperation);
        #endregion

        #region Connection Type
        public List<ItemCollection> ConnectionTypes { get; set; }

        public ItemCollection SelectedConnectionType { get; set; }

        private int _connectionTypeIndex;
        public int SelectedConnectionTypeIndex
        {
            get => _connectionTypeIndex;

            set
            {
                _connectionTypeIndex = value;
                NotifyPropertyChanged(nameof(PathInfo));
                NotifyPropertyChanged(nameof(HintText));
            }
        }
        #endregion

        #region Bank Type
        public List<ItemCollection> BankTypes { get; set; }

        public ItemCollection SelectedBankType { get; set; }

        public int SelectedBankTypeIndex { get; set; }
        #endregion

        #region Lock Bank Type
        public List<ItemCollection> LockBankTypes { get; set; }

        public ItemCollection SelectedLockBankType { get; set; }

        public int SelectedLockBankTypeIndex { get; set; }
        #endregion

        #region Lock Type
        public List<ItemCollection> LockTypes { get; set; }

        public ItemCollection SelectedLockType { get; set; }

        public int SelectedLockTypeIndex { get; set; }
        #endregion

        #region UI Text
        private string _connectText;
        public string ConnectText
        {
            get => _connectText;
            set
            {
                _connectText = value;
                NotifyPropertyChanged(nameof(ConnectText));
            }
        }

        private string _inventoryText;
        public string InventoryText
        {
            get => _inventoryText;
            set
            {
                _inventoryText = value;
                NotifyPropertyChanged(nameof(InventoryText));
            }
        }

        public string PathInfo
        {
            get
            {
                return SelectedConnectionTypeIndex == 0 ? IConstValue.Path : IConstValue.MACAddress;
            }
        }

        public string HintText
        {
            get
            {
                if (SelectedConnectionTypeIndex == 0)
                {
                    return "Ex: COM3";
                }
                else
                {
                    return "Ex: AABBCCDDEEFF";
                }
            }
        }
        #endregion

        #region Device Info
        public string Name { get; set; }
        public string Version { get; set; }
        public string Bluetooth { get; set; }
        public string Battery { get; set; }
        public string Temperature { get; set; }
        public string KeyStatus { get; set; }
        public string ConnectStatus { get; set; }
        public string ActionStateText { get; set; }
        #endregion

        #region Tag Info
        public string EPC { get; set; }
        public string RSSI { get; set; }
        #endregion

        #region Operation Info
        public string Offset { get; set; }
        public string Length { get; set; }

        public string Data { get; set; }
        public string Result { get; set; }

        public string AccessPassword { get; set; }

        public string Text { get; set; }

        public string LockPassword { get; set; }
        public string KillPassword { get; set; }
        #endregion

        public string ConnectPath { get; set; }

        private bool _isConnected;
        public bool IsConnected
        {
            get => _isConnected;
            set
            {
                _isConnected = value;
                ConnectText = _isConnected ? IConstValue.Disconnect : IConstValue.Connect;
                NotifyPropertyChanged(nameof(IsConnected));
                NotifyPropertyChanged(nameof(IsDisconnected));
                NotifyPropertyChanged(nameof(IsFindable));
            }
        }

        public bool IsInventoried => IsConnected && !StringUtil.IsNullOrEmpty(EPC) && ActionStateText.Equals(ActionState.Stop.ToString());

        public bool IsDisconnected => !IsConnected;

        public bool IsFindable
        {
            get
            {
                if (IsConnected)
                {
                    if (StringUtil.IsNullOrEmpty(ActionStateText))
                    {
                        return true;
                    }
                    
                    if (ActionStateText.Equals(ActionState.Stop.ToString()))
                    {
                        return true;
                    }
                }

                return false;
            }
        }
        #endregion

        UHFReader _reader;
        BaseTransport _baseTransport;

        DeviceType _detectDeviceType;

        public MainViewModel()
        {
            InitUI();

            UHFReader.LogLevel = LogLevel.Info;
            UHFReader.LogEvent += OnLogEvent;
        }

        private void OnLogEvent(object sender, LogEventArg e)
        {
            Debug.WriteLine($"[{e.Tag}] {e.Log}");
        }

        private void InitUI()
        {
            ConnectText = IConstValue.Connect;
            InventoryText = IConstValue.Inventory;

            ConnectionTypes = new List<ItemCollection>();
            ConnectionTypes.Add(new ItemCollection(IConstValue.Serial));
            ConnectionTypes.Add(new ItemCollection(IConstValue.Bluetooth));

            BankTypes = new List<ItemCollection>();
            BankTypes.Add(new ItemCollection(IConstValue.Reserved));
            BankTypes.Add(new ItemCollection(IConstValue.EPC));
            BankTypes.Add(new ItemCollection(IConstValue.TID));
            BankTypes.Add(new ItemCollection(IConstValue.User));

            LockBankTypes = new List<ItemCollection>();
            LockBankTypes.Add(new ItemCollection(IConstValue.AccessPassword));
            LockBankTypes.Add(new ItemCollection(IConstValue.KillPassword));
            LockBankTypes.Add(new ItemCollection(IConstValue.EPC));
            LockBankTypes.Add(new ItemCollection(IConstValue.TID));
            LockBankTypes.Add(new ItemCollection(IConstValue.User));

            LockTypes = new List<ItemCollection>();
            LockTypes.Add(new ItemCollection(IConstValue.Unlock));
            LockTypes.Add(new ItemCollection(IConstValue.Lock));

            SelectedBankTypeIndex = 1;
            SelectedLockBankTypeIndex = 1;
            SelectedLockTypeIndex = 1;
            Offset = IConstValue.DefaultOffset;
            Length = IConstValue.DefaultLength;

            AccessPassword = IConstValue.DefaultPassword;
            LockPassword = IConstValue.DefaultPassword;
            KillPassword = IConstValue.DefaultPassword;

            NotifyPropertyChanged(nameof(Offset));
            NotifyPropertyChanged(nameof(Length));
            NotifyPropertyChanged(nameof(AccessPassword));
            NotifyPropertyChanged(nameof(LockPassword));
            NotifyPropertyChanged(nameof(KillPassword));
        }

        private void InitReader()
        {
            if (_reader != null)
            {
                _reader.ActionStateChangedEvent += OnActionStateChanged;
                _reader.ConnectStateChangedEvent += OnConnectStateChangedEvent;
                _reader.BatteryStateEvent += OnBatteryEvent;
                _reader.TemperatureEvent += OnTemperatureEvent;
                _reader.KeyStateEvent += OnKeyEvent;
            }
        }

        private void InitUHF()
        {
            if (_reader != null && _reader.BaseUHF != null)
            {
                RFIDConfig rfidConfig = new RFIDConfig()
                {
                    ContinuousMode = true,
                    Power = 24,
                    Algorithm = AlgorithmType.DynamicQ,
                    StartQ = 4,
                    MinQ = 0,
                    MaxQ = 15,
                    Session = Session.S0,
                    Encoding = unitechRFID.Encoding.FM0,
                    Target = Target.A,
                    InventoryTime = 200,
                    IdleTime = 0
                };

                _reader.BaseUHF.RFIDConfig = rfidConfig;
            }
        }

        public void OnOperation(string param)
        {
            Show(param);
            if (param.Equals(IConstValue.Connect)) { OnConnect(); }
            else if (param.Equals(IConstValue.Disconnect)) { OnDisconnect(); }
            else if (param.Equals(IConstValue.Inventory)) { OnInventory(); }
            else if (param.Equals(IConstValue.Stop)) { OnStop(); }
            else if (param.Equals(IConstValue.Read)) { OnRead(); }
            else if (param.Equals(IConstValue.Write)) { OnWrite(); }
            else if (param.Equals(IConstValue.Display)) { OnDisplay(); }
            else if (param.Equals(IConstValue.Lock)) { OnLock(); }
            else if (param.Equals(IConstValue.Kill)) { OnKill(); }
            else if (param.Equals(IConstValue.Find)) { OnFind(); }
        }

        private void OnConnect()
        {
            if (_reader == null)
            {
                if (SelectedConnectionType.Name.Equals(IConstValue.Serial))
                {
                    //Detect device
                    _baseTransport = new TransportSerial(DeviceType.Unknown, ConnectPath, 115200);
                    _reader = new UHFReader(_baseTransport);

                    _reader.DetectDeviceEvent += OnDetectDevice;
                    _reader.DetectFinishedEvent += OnDetectFinished;

                    _detectDeviceType = DeviceType.Unknown;
                    _reader.StartDetect();
                }
                else
                {
                    _baseTransport = new TransportBluetooth(DeviceType.RP902, ConnectPath);
                    _reader = new UHFReader(_baseTransport);

                    InitReader();

                    _reader.Connect();
                }

                _reader.DisplayTags = new DisplayTags(ReadOnceState.Off, BeepAndVibrateState.On);
            }
        }

        #region Detect Device
        private void OnDetectFinished(object sender, DetectDeviceEventArgs e)
        {
            _reader.DetectDeviceEvent -= OnDetectDevice;
            _reader.DetectFinishedEvent -= OnDetectFinished;
            _reader.Dispose();
            _reader = null;

            if (_detectDeviceType != DeviceType.Unknown)
            {
                _baseTransport = new TransportSerial(_detectDeviceType, ConnectPath, 115200);
                _reader = new UHFReader(_baseTransport);

                InitReader();

                _reader.Connect();
            }
            else
            {
                MessageBox.Show("No device detected!");
            }
        }

        private void OnDetectDevice(object sender, DetectDeviceEventArgs e)
        {
            _detectDeviceType = e.DeviceType;
        }
        #endregion

        private void OnDisconnect()
        {
            _reader.Disconnect();
        }

        private void OnInventory()
        {
            if (!_isOnInventory)
            {
                _reader.DisplayTags = new DisplayTags(ReadOnceState.Off, BeepAndVibrateState.On);

                _reader.BaseUHF.SetSelectMask6cEnabled(0, false);
                _reader.BaseUHF.Inventory6c();

                _isOnInventory = true;
            }
        }

        private void OnStop() 
        {
            if (_isOnInventory)
            {
                _reader.BaseUHF.Stop();

                _isOnInventory = false;
            }
        }

        private void OnRead()
        {
            _reader.DisplayTags = new DisplayTags(ReadOnceState.On, BeepAndVibrateState.On);

            SelectTag(EPC);
            _reader.BaseUHF.ReadMemory6c((BankType)SelectedBankTypeIndex, int.Parse(Offset), int.Parse(Length), AccessPassword);
        }

        private void OnWrite()
        {
            _reader.DisplayTags = new DisplayTags(ReadOnceState.On, BeepAndVibrateState.On);

            SelectTag(EPC);
            _reader.BaseUHF.WriteMemory6c((BankType)SelectedBankTypeIndex, int.Parse(Offset), Data, AccessPassword);
        }

        private void OnDisplay()
        {
            SetDisplayOutput(2, true, Text);
        }

        private void OnLock()
        {
            _reader.DisplayTags = new DisplayTags(ReadOnceState.On, BeepAndVibrateState.On);

            SelectTag(EPC);
            Lock6cParam lockParam = new Lock6cParam();

            LockState lockState = SelectedLockType.Name.Equals(IConstValue.Lock) ? LockState.Lock : LockState.Unlock;
            switch (SelectedLockBankType.Name)
            {
                case IConstValue.AccessPassword:
                    lockParam.AccessPassword = lockState;
                    break;
                case IConstValue.KillPassword:
                    lockParam.KillPassword = lockState;
                    break;
                case IConstValue.EPC:
                    lockParam.EPC = lockState;
                    break;
                case IConstValue.TID:
                    lockParam.TID = lockState;
                    break;
                case IConstValue.User:
                    lockParam.User = lockState;
                    break;
            }

            _reader.BaseUHF.Lock6c(lockParam, LockPassword);
        }

        private void OnKill()
        {
            _reader.DisplayTags = new DisplayTags(ReadOnceState.On, BeepAndVibrateState.On);

            SelectTag(EPC);
            if (MessageBox.Show("Are you ready to kill tag?", "Kill Tag", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                _reader.BaseUHF.Kill6c(KillPassword);
            }
        }

        private void OnFind()
        {
            FindDevice findDevice = new FindDevice(FindDeviceMode.VibrateBeep, 10);

            _reader.FindDevice = findDevice;
        }

        private void SelectTag(string tagEPC)
        {
            SelectMask6cParam param = new SelectMask6cParam(
                    true,
                    Mask6cTarget.SL,
                    Mask6cAction.AB,
                    BankType.EPC,
                    0,
                    tagEPC,
                    tagEPC.Length * 4);
            try
            {
                for (int i = 0; i < 2; i++)
                {
                    _reader.BaseUHF.SetSelectMask6cEnabled(i, false);
                }
                _reader.BaseUHF.SetSelectMask6c(0, param);
            }
            catch (ReaderException e)
            {
            }
        }

        #region Event Functions
        private void OnActionStateChanged(object sender, ActionStateEventArgs e)
        {
            InventoryText = e.ActionState == ActionState.Stop ? IConstValue.Inventory : IConstValue.Stop;

            ActionStateText = e.ActionState.ToString();

            NotifyPropertyChanged(nameof(InventoryText));
            NotifyPropertyChanged(nameof(IsInventoried));
            NotifyPropertyChanged(nameof(IsFindable));
            NotifyPropertyChanged(nameof(ActionStateText));
        }

        private void OnConnectStateChangedEvent(object sender, ConnectStateEventArgs e)
        {
            ConnectStatus = e.ConnectState.ToString();
            NotifyPropertyChanged(nameof(ConnectStatus));
            switch (e.ConnectState)
            {
                case ConnectState.Disconnected:
                    IsConnected = false;
                    ReleaseReader();
                    break;
                case ConnectState.Listen:
                case ConnectState.Connecting:
                    IsConnected = false;
                    break;
                case ConnectState.Connected:
                    IsConnected = true;

                    Name = _reader.DeviceName;
                    Version = _reader.Version;
                    Bluetooth = _reader.BaseTransport.MacAddress;

                    NotifyPropertyChanged(nameof(Name));
                    NotifyPropertyChanged(nameof(Version));
                    NotifyPropertyChanged(nameof(Bluetooth));

                    _reader.BaseUHF.TagReadEvent += OnTagRead;
                    _reader.BaseUHF.AccessResultEvent += OnAccessResult;

                    InitUHF();
                    break;
            }
        }

        private void OnKeyEvent(object sender, KeyEventArgs e)
        {
            KeyStatus = e.KeyState.ToString();
            if (KeyStatus == "KeyDown")
            {
                OnInventory();
            }
            else
            {
                OnStop();
            }

            NotifyPropertyChanged(nameof(KeyStatus));
        }

        private void OnTemperatureEvent(object sender, TemperatureEventArgs e)
        {
            Temperature = e.Temperature.ToString();
            NotifyPropertyChanged(nameof(Temperature));
        }

        private void OnBatteryEvent(object sender, BatteryStateEventArgs e)
        {
            Battery = e.Battery.ToString();
            NotifyPropertyChanged(nameof(Battery));
        }

        private void OnAccessResult(object sender, AccessResultEventArgs e)
        {
            Data = e.Data;
            Result = $"{e.ActionState} {e.ResultCode}";

            NotifyPropertyChanged(nameof(Data));
            NotifyPropertyChanged(nameof(Result));
        }

        private void OnTagRead(object sender, TagReadEventArgs e)
        {
            EPC = e.EPC;
            RSSI = e.RSSI.ToString();

            NotifyPropertyChanged(nameof(EPC));
            NotifyPropertyChanged(nameof(RSSI));
        }
        #endregion

        public static void Show(string msg)
        {
            Debug.WriteLine(msg);
        }

        private void ReleaseReader()
        {
            if (_reader != null)
            {
                _reader.ActionStateChangedEvent -= OnActionStateChanged;
                _reader.ConnectStateChangedEvent -= OnConnectStateChangedEvent;
                _reader.BatteryStateEvent -= OnBatteryEvent;
                _reader.TemperatureEvent -= OnTemperatureEvent;
                _reader.KeyStateEvent -= OnKeyEvent;

                if (_reader.BaseUHF != null)
                {
                    _reader.BaseUHF.TagReadEvent -= OnTagRead;
                    _reader.BaseUHF.AccessResultEvent -= OnAccessResult;
                }

                _reader.Dispose();
                _reader = null;
            }
        }

        public void Dispose()
        {
            ReleaseReader();
        }

        private void SetDisplayOutput(int linePosition, bool clearScreen, string displayOutput)
        {
            try
            {
                int command = 0;

                switch (linePosition)
                {
                    case 1:
                        command += 64;
                        break;
                    case 2:
                        command += 128;
                        break;
                    case 3:
                        command += 192;
                        break;
                    default:
                        command += 128;
                        break;
                }

                if (clearScreen)
                {
                    command += 32;
                }

                int outputLength = displayOutput.ToString().Length;
                if (outputLength < 16)
                {
                    command += (16 - outputLength) / 2;
                }

                _reader.DisplayOutput = new DisplayOutput((byte)command, displayOutput.ToCharArray());
            }
            catch
            { }
        }
    }
}
