#define VIEW_EMV_TRACE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using YalkoEmvClassLibrary;
using System.Threading;

namespace EmvLibMultiInstanceExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        static int emv_ker_counter = 0;

#if VIEW_EMV_TRACE

        YalkoEmvClass emv_lib1 = new YalkoEmvClass();
        EMVLibraryDelegate emv_delegate1 = new EMVLibraryDelegate();

        YalkoEmvClass emv_lib2 = new YalkoEmvClass();
        EMVLibraryDelegate emv_delegate2 = new EMVLibraryDelegate();

        void ThreadMethod_RunEmv(Object o)
        {
            int i = (int)o;
            lock (this)
            {
                emv_ker_counter++;
                textBox2.Text = "emv_ker_counter = " + emv_ker_counter;
            }
            try
            {
                switch(i)
                {
                    case 0: 
                        emv_lib1.EmvRun(emv_delegate1);
                        break;
                    case 1: 
                        emv_lib2.EmvRun(emv_delegate2);
                        break;
                    default:
                        break;
                }
            }
            catch (OutOfMemoryException oom)
            {
                textBox1.Text += "\r\nUnexpected OutOfMemory exception thrown:\r\n" + oom;
            }

            lock (this)
            {
                emv_ker_counter--;
                textBox2.Text = "emv_ker_counter = " + emv_ker_counter;
                if (emv_ker_counter == 0)
                {
                    textBox1.Text = emv_delegate1.GetTraceString();
                    textBox3.Text = emv_delegate2.GetTraceString();
                    buttonEmvRun.Enabled = true;
                }
                else
                    buttonEmvRun.Enabled = false;
            }

        }

        void fEmvRun()
        {
            int i = 0;
            while (i < 2)
            {
                try
                {
                    Thread thread = new Thread(new ParameterizedThreadStart(ThreadMethod_RunEmv));
                    thread.Start(i);
                }
                catch (InsufficientMemoryException e)
                {
                    textBox1.Text += "\r\n\r\nExpected InsufficientMemoryException thrown.  Message:\r\n" + e.Message;
                    textBox1.Text += "\r\nWe could optionally sleep until a running worker thread";
                    textBox1.Text += "\r\nhas finished, like this:  threads[joinCount++].Join()";
                    Thread.Sleep(2000);
                }
                i++;
            }
        }


#else
        void ThreadMethod_RunEmv(Object o)
        {
            int i = (int)o;
            lock (this)
            {
                emv_ker_counter++;
                textBox2.Text = "emv_ker_counter = " + emv_ker_counter;
            }
            try
            {
                YalkoEmvClass emv_lib = new YalkoEmvClass();
                EMVLibraryDelegate emv_delegate = new EMVLibraryDelegate();
                emv_lib.EmvRun(emv_delegate);
            }
            catch (OutOfMemoryException oom)
            {
                textBox1.Text += "\r\nUnexpected OutOfMemory exception thrown:\r\n" + oom;
            }

            lock (this)
            {
                emv_ker_counter--;
                textBox2.Text = "emv_ker_counter = " + emv_ker_counter;
                if (emv_ker_counter == 0)
                {
                    buttonEmvRun.Enabled = true;
                }
                else
                    buttonEmvRun.Enabled = false;
            }

        }
        void fEmvRun()
        {
            int i = 0;

            while ( i < 100 )
            {
                try
                {
                    Thread thread = new Thread(new ParameterizedThreadStart(ThreadMethod_RunEmv));
                    thread.Start(i);
                }
                catch (InsufficientMemoryException e)
                {
                    textBox1.Text += "\r\n\r\nExpected InsufficientMemoryException thrown.  Message:\r\n" + e.Message;
                    textBox1.Text += "\r\nWe could optionally sleep until a running worker thread";
                    textBox1.Text += "\r\nhas finished, like this:  threads[joinCount++].Join()";
                    Thread.Sleep(2000);
                }
                i++;
             }
        }

#endif

        private void buttonEmvRun_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
            buttonEmvRun.Enabled = false;
            Thread RunEmvThread = new Thread(new ThreadStart(fEmvRun));
            RunEmvThread.Start();
        }

    }
    public class EMVLibraryDelegate : YalkoEmvClass, YalkoEmvClass.EMVLibraryDelegate
    {
        public string emv_trace_str = "";
        public string GetTraceString()
        {
            return emv_trace_str;
        }
        public void ClearTraceString()
        {
            emv_trace_str = "";
        }

        public void fEmvTrace(int lpHandle, String s)
        {
#if VIEW_EMV_TRACE
            emv_trace_str += s;
#endif
        }

        public unsafe byte fAtr_Delegate(int lpHandle, ref String atr, byte* protocol)
        {
            atr = "3B620000811F";
            *protocol = 0;//0 => T0, 1 => T1
            return 1;//ok
        }

        String CardSimulator(String sCmd, String sData)
        {
#if VIEW_EMV_TRACE
#else
            Thread.Sleep(100);
#endif
            if (sData.Length > 0)//&& apdu_data_len < sizeof(sData)/2)
            {
                if (sCmd.Equals("00A404000E") && sData.Equals("315041592E5359532E4444463031"))
                {
                    return "6A82";
                }
                else
                    if (sCmd.Equals("00A4040007") && sData.Equals("A0000000031010"))
                    {
                        return "6F318407A0000000031010A526500B56495341204352454449549F120F4352454449544F20444520564953418701019F1101019000";
                    }
                    else
                        if (sCmd.Equals("00A4040007") && sData.Equals("A0000000041010"))
                        {
                            return "6F318407A0000000041010A526500B4D415354455220434152449F120F4452454449544F20444520564953418701019F1101019000";
                        }
                        else

                            if (sCmd.Equals("80A8000002") && sData.Equals("8300"))
                            {
                                return "800E5C000801010010010300180102019000";
                            }
                            else
                                if (sCmd.Equals("80AE80001D"))// && sData.Equals("00000001000000000000000008404000048000064313083100E9C4B090"))
                                {
                                    return "80128000018B5911CDEBC190F506010A039000009000";
                                }
                                else
                                    if (sCmd.Equals("008200000A"))// && sData.Equals("993680ED37FC09533030"))
                                    {
                                        return "9000";
                                    }
                                    else
                                        if (sCmd.Equals("80AE40001F"))// && sData.Equals("3030000000010000000000000000084040000480000643130831006D4B0149"))
                                        {
                                            return "8012400001492136F956F8B83006010A036000009000";
                                        }
                                        else
                                            if (sCmd.Equals("80AE00001F"))// && sData.Equals("3030000000010000000000000000084040000480000643130831006D4B0149"))
                                            {
                                                return "8012000001492136F956F8B83006010A036000009000";
                                            }
                                            else
                                                if (sCmd.Substring(0, 8).Equals("00A404000"))
                                                {
                                                    return "6A82";
                                                }
                                                else
                                                {
                                                    //Log.i("Emv", "CardSimulator ERROR! No Equals for command = " + sCmd +" data = " + sData);
                                                }

            }
            else
            {
                if (sCmd.Equals("00B2010C00"))
                {
                    return "70435F201A564953412041435155495245522054455354204341524420303157114761739001010010D101220111438780899F1F10313134333830303738303030303030309000";
                }
                else
                    if (sCmd.Equals("00B2011400"))
                    {
                        return "7081939081908B3901F6253048A8B2CB08974A4245D90E1F0C4A2A69BCA469615A71DB21EE7B3AA94200CFAEDCD6F0A7D9AD0BF79213B6A418D7A49D234E5C9715C9140D87940F2E04D6971F4A204C927A455D4F8FC0D6402A79A1CE05AA3A526867329853F5AC2FEB3C6F59FF6C453A7245E39D73451461725795ED73097099963B82EBF7203C1F78A529140C182DBBE6B42AE00C029000";
                    }
                    else
                        if (sCmd.Equals("00B2021400"))
                        {
                            return "702D8F01959F320103922433F5E4447D4A32E5936E5A1339329BB4E8DD8BF0044CE4428E24D0866FAEFD2348809D719000";
                        }
                        else
                            if (sCmd.Equals("00B2031400"))
                            {
                                return "708193938190919D6C210B3981D1C99B3AD55EDF36A138FFAD54D838FA40622AB97046E05EA6E6230AB89D5BE871114EB5431B97403B8C3D2D4CA9BB625AC13FD8C6B825433656CB56557AAC396D945F6D4014FB6E71E8DBEA74B285E9CF3FCEABDFA61D5A4BE16DAAA433F7F2644B178A7DD93DA98BB9D10E84298BDB6B6AE02D04E6E5558C77E79F82C9E046DF821DD0277AB9D00C9000";
                            }
                            else
                                if (sCmd.Equals("00B2011C00"))
                                {
                                    return "700E5A0847617390010100105F3401019000";
                                }
                                else
                                    if (sCmd.Equals("00B2021C00"))
                                    {
                                        return "707D9F420208405F25039507015F24031012319F0802008C5F280208405F300202019F0702FF008E0E00000000000000001E0302031F008C159F02069F03069F1A0295055F2A029A039C019F37048D178A029F02069F03069F1A0295055F2A029A039C019F37049F0D05F0400088009F0E0500100000009F0F05F0400098009000";
                                    }
                                    else
                                    {
                                        //Log.i("Emv", "CardSimulator ERROR! No Equals for command = " + sCmd);
                                    }
            }
            return "";
        }

        public String fApdu_Delegate(int lpHandle, String sApdu)
        {
            if (sApdu.Length > 9)
            {
                if (sApdu.Length > 10)
                {
                    String sApdu_cmd = sApdu.Substring(0, 10);
                    String sApdu_data = sApdu.Substring(10, sApdu.Length - 10);
                    String str = CardSimulator(sApdu_cmd, sApdu_data);

                    //MessageBoxButtons buttons = MessageBoxButtons.YesNo;
                    //MessageBox.Show("cmd = " + sApdu_cmd + "\r\ndata = " + sApdu_data + "\r\nanswer =\r\n" + str, "", buttons);
                    return str;
                }
                else
                {
                    String sApdu_cmd = sApdu.Substring(0, 10);
                    String str = CardSimulator(sApdu_cmd, "");

                    //MessageBoxButtons buttons = MessageBoxButtons.YesNo;
                    //MessageBox.Show("cmd = " + sApdu_cmd + "\r\nanswer =\r\n" + str, "", buttons);
                    return str;
                }
            }
            else
            {
                MessageBoxButtons buttons = MessageBoxButtons.YesNo;
                MessageBox.Show("return null", "APDU ERROR!", buttons);
                return null;
            }
        }
        public unsafe byte fHostSwop_Delegate(int lpHandle,
                 String data_after_GAC1,
                 ref String host_answer,
                 ref String host_answer71,
                 ref String host_answer72
                )
        {
#if VIEW_FIELD55_BY_MESSAGE_BOX
            //Create field 55
            //ISO 8583 clearing a field P-55 containing chip data
            {
                String sField_55 =
                        "9F26" //    
                        + "9F27" //    
                        + "9F10" //    
                        + "9F37" //    
                        + "9F36" //    
                        + "95"   //    
                        + "9A"   //    
                        + "9C"   //    
                        + "9F02" //    
                        + "5F2A" //    
                        + "82"   //    
                        + "9F1A" //    
                        + "9F03" //    
                        + "9F34" //    
                        + "9F33" //    
                        + "9F35" //    
                        + "9F1E" //    
                        + "9F53" //    
                        + "84"   //    
                        + "9F09" //    
                        + "9F41"; //   
                String answer = GetTLVBufByDOL(sField_55);
                MessageBoxButtons buttons = MessageBoxButtons.YesNo;
                MessageBox.Show(answer, "Field 55:", buttons);
            }
#endif
            host_answer = "910A963680ED37FC095330308A023030";
            host_answer71 = "710F860D8424000008EDBB6BE453CA32C48A023030";
            host_answer72 = "720F860D8424000008EDBB6BE453CA32C48A023030";
            return 0;//ok
            //return 1;// - unable to go on line
        }
        public unsafe byte fGetTrData_Delegate(int lpHandle,
                ref String amount_max_6_bytes,
                ref String amount_other_max_6_bytes,
                ref String currency_max_2_bytes,
                ref String operation_type_1_byte,
                ref String pin_entry_statys_1_byte
                )
        {
            amount_max_6_bytes = "10000";//100,00
            amount_other_max_6_bytes = "1000";//10,00
            currency_max_2_bytes = "840";//USD
            operation_type_1_byte = "00";//PURCHES
            pin_entry_statys_1_byte = "00";//OK
            return 0;//ok
        }

        public unsafe byte fGetPin_Delegate(int lpHandle, ref String pin_data, byte min, byte max)
        {
            //#define GET_PIN_OK              0
            //#define GET_PIN_ABORT           1
            //#define GET_PIN_TIMEOUT         2
            //#define GET_PIN_LIMIT_EXCEEDED  3
            pin_data = "1234";
            return 0;//GET_PIN_OK 
        }
        public byte fGetCryptPin_Delegate(int lpHandle)
        {
            //#define GET_PIN_OK              0
            //#define GET_PIN_ABORT           1
            return 0;//GET_PIN_OK 
        }
        public String fSelLang_Delegate(int lpHandle)
        {
            return "EN";//English
        }
        public byte fSelectAccount_Delegate(int lpHandle)
        {
            byte RC = 0x10;
            switch (RC)
            {
                case 1: return 0x10;// 0     => Savings     
                case 2: return 0x20;// 2     => Cheque/debit
                case 3: return 0x30;// 3     => Credit      
                case 4: return 0x00;// 4     => Default – no account specified.
                default:
                    break;
            }
            return 0xFF;//err
        }
        public byte fSelApp_Delegate(int lpHandle, byte flagRetry, byte[] code_table, string[] AppList, string[] AidList)
        {
            MessageBoxButtons buttons = MessageBoxButtons.YesNo;
            DialogResult result;

            if (flagRetry != 0)
            {
                result = MessageBox.Show("Please Select Again", "Please Select Again", buttons);

                if (result != DialogResult.Yes)
                {
                    return 0;
                }
            }

#if SELECT_APP_BY_MESSAGE_BOX 
            for (int i = 0; i < AppList.Length; i++)
            {

                result = MessageBox.Show(

                    "Application(" + (i + 1) + ") = " + AppList[i]
                    + "\r\n(AID = " + AidList[i] + " Code Table = " + code_table[i] + ")"

                    , "Please select Application",
                    buttons);

                if (result == DialogResult.Yes)
                {
                    return (byte)(i + 1);//1 => first application selected
                }
            }
            return 0;// error
#else
            return 1;// => selected first application
#endif
        }
    }

}
