using PCSC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;

namespace CardReaderService
{
    public partial class Service1 : ServiceBase
    {
        private Thread _mainWorkThread;
        private bool _stopMainWorkerThread;

        private string _readerName = "";
        //private SCardReader _reader;
        private SCardMonitor _cardMonitor;

        public Service1()
        {
            InitializeComponent();
        }

        public void Start()
        {
            OnStart(new string[] {});
        }
//
        protected override void OnStart(string[] args)
        {
            Console.WriteLine("Starting.. Getting available readers");
            var ctxFactory = ContextFactory.Instance;
            using(var context = ctxFactory.Establish(SCardScope.System))
            {
                var readerNames = context.GetReaders();
                if (NoReaderAvailable(readerNames))
                {
                    Console.WriteLine("No Card Reader is available, Exiting..");
                    return;
                }
                Console.WriteLine("Choosing first available reader: " + readerNames.First());
                _readerName = readerNames.First();
                _cardMonitor = new SCardMonitor(ctxFactory, SCardScope.System);
                _cardMonitor.CardInserted += _cardMonitor_CardInserted;
                _cardMonitor.Start(_readerName);

                StartWorkerThread();
            }
        }

        public void Stop()
        {
            OnStop();
        }

        protected override void OnStop()
        {
            _stopMainWorkerThread = true;
            if (_mainWorkThread!= null && _mainWorkThread.IsAlive)
            {
                _mainWorkThread.Join(3000);
                if (_mainWorkThread.IsAlive)
                {
                    _mainWorkThread.Interrupt();
                } 
            }
            if (_cardMonitor != null)
            {
                _cardMonitor.Cancel();
                _cardMonitor.Dispose();
                _cardMonitor = null; 
            }
        }

        private void StartWorkerThread()
        {
            _stopMainWorkerThread = false;
            _mainWorkThread = new Thread(MainWorkerThread)
            {
                Name = "CardServiceMainThread",
                IsBackground = false
            };
            _mainWorkThread.Start();
        }

        private void _cardMonitor_CardInserted(object sender, CardStatusEventArgs e)
        {
            var ctxFac = ContextFactory.Instance;
            using (var ctx = ctxFac.Establish(SCardScope.System))
            {
                var reader = new SCardReader(ctx);
                var pioSendPci = new IntPtr();
                byte[] rcvBuffer = new byte[256];

                if (_readerName == string.Empty)
                {
                    Console.WriteLine("Reader name is somehow empty... WTF!");
                    _stopMainWorkerThread = true;
                    return;
                }
                
                var err = reader.Connect(_readerName, SCardShareMode.Shared, SCardProtocol.T0 | SCardProtocol.T1);

                if (err == SCardError.Success)
                {
                    var uIdcmd = new byte[] { 0xFF, 0xCA, 0x00, 0x00, 0x00 };

                    err = reader.Transmit(pioSendPci, uIdcmd, ref rcvBuffer);
                    if (err == SCardError.Success)
                    {
                        var uid = ConvertByteUIDToString(rcvBuffer);
                        var atrString = ConvertByteUIDToString(e.Atr);
                        Console.WriteLine("Card Inserted, ATR: " + atrString + ", and UID is: " + uid);

                        CardDataPost postObj = new CardDataPost {CardUId = uid};
                        DataCenterHelper.PostAsync(postObj, "/api/swipedata");
                        Console.WriteLine("Posted to Server");
                    } 
                }
                else
                {
                    Console.WriteLine("Reader failed to connect, error: " + Enum.GetName(typeof(SCardError), err));
                }

            }
        }
        
        private void MainWorkerThread()
        {
            while (!_stopMainWorkerThread)
            {
                //dont actually need to do anything.. but cannot exit right away?
                Thread.Sleep(3000);
            }
        }

        private string ConvertByteUIDToString(byte[] uid)
        {
            StringBuilder sb = new StringBuilder();

            foreach(var b in uid)
            {
                sb.AppendFormat("{0:X2}", b);
            }

            return sb.ToString();
        }

        private bool NoReaderAvailable(ICollection<string> readerNames)
        {
            return readerNames == null || readerNames.Count < 1;
        }
    }
}
