
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using SmartCard.Runtime.Remoting.Channels.APDU;
using System.IO;
using System.Xml;
using System.Collections;
using MyCompany.Client;
using MyCompany.Server;
using MyCompany.Client.EncryptionSink;


namespace MyCompany.Client
{

    public class ClientApp
    {
         private const string URL = "apdu://selfdiscover:33/EncryptionSinkService.uri";
         private Service cardServer;                                     //definice nové služby
         private APDUClientChannel channel = new APDUClientChannel();    //definice nového komunikačního kanálu (channel)

         //metoda pro nastaveni proměnne service
         private void setService(Service cardServer)
         {
             this.cardServer = cardServer;
         }
         //metoda pro získáni hodnoty z proměnne service
         private Service getService()
         {
             return this.cardServer;
         }
         //metoda pro nastavení proměnne channel
         private void setChannel(APDUClientChannel channel)
         {
             this.channel = channel;
         }
         //metoda pro získáni hodnoty z proměnne channel
         private APDUClientChannel getChannel()
         {
             return this.channel;
         }

        //----------------------------------------------------------------------------
        //Metoda pro odesílání vybraných parametrů na kartu (token)
        //----------------------------------------------------------------------------
        public bool TransferNaKartu_token(string a, string sigPA, string r)
        {
            //Ověření vkládaného tokenu
            for (int i = 0; i <= a.Length - 1; i++)
            {
                if (!('0' <= a[i] && a[i] <= '9'))
                {
                    throw new InvalidDataException("Neplatný znak v čísle 'a'");
                }
            }
            for (int i = 0; i <= r.Length - 1; i++)
            {
                if (!('0' <= r[i] && r[i] <= '9'))
                {
                    throw new InvalidDataException("Neplatný znak v čísle 'r'");
                }
            }
            if (r.Length > 32)
            {
                throw new InvalidDataException("Příliš veliký parametr 'r'");
            }

            //Odeslání tokenu na kartu
            Inicializace();            
            getService().PutOnCard_token(a, sigPA, r);
            UkonciSpojeni();

            //Jakmile je transfer na kartu hotov, metoda vrátí true.
            //   Je zde kvůli ukládání tokenů na kartu, přesněji k nápomoci metodě přes kterou
            //   se tokeny budou hromadně vkládat.
            //   Uložení tokenu na kartu trvá přibližně 2-3sekundy.
            return true;
        }

        //----------------------------------------------------------------------------
        //Metoda pro odesílání vybraných parametrů na kartu (jen 1x)
        //----------------------------------------------------------------------------
        public void TransferNaKartu_param(string c, string w, string q, int mAP, int pocetTokenu)
        {
            //Převedení parametrů 'w', 'q' ze stringu na int[]
            int[] wInt = new int[w.Length];
            int[] qInt = new int[q.Length];

            //Ověření vkládaných proměnných
            for (int i = 0; i <= c.Length - 1; i++)
            {
                if (!('0' <= c[i] && c[i] <= '9'))
                {
                    throw new InvalidDataException("Neplatný znak v čísle 'c'.");
                }             
            }           
            for (int i = 0; i <= w.Length - 1; i++)
            {
                if (!('0' <= w[i] && w[i] <= '9'))
                {
                    throw new InvalidDataException("Neplatný znak v čísle 'w'.");
                }
                wInt[i] = w[i] - '0';
            }            
            for (int i = 0; i <= q.Length - 1; i++)
            {
                if (!('0' <= q[i] && q[i] <= '9'))
                {
                    throw new InvalidDataException("Neplatný znak v modulu 'q'.");
                }
                qInt[i] = q[i] - '0';               
            }
            if (!(0 <= mAP && mAP <= 9))
            {
                throw new InvalidDataException("Neplatná velikost parametru 'mAP'.");
            }

            if (q.Length > 32)
            {
                throw new InvalidDataException("Příliš veliký parametr 'q'.");
            }
            if (pocetTokenu > 3)
            {
                throw new InvalidDataException("Příliš mnoho tokenů. Maximální počet jsou 3 tokeny.");
            }
            
            //Odeslání parametrů na kartu 
            Inicializace();
            getService().PutOnCard_param(c, wInt, qInt, mAP);
            UkonciSpojeni();
        }

        //----------------------------------------------------------------------------
        //Metoda sloužící pro přepsání souboru s tokeny na kartě
        //----------------------------------------------------------------------------
        public void PrepisTokenList(bool prepsat)
        {
            Inicializace();
            getService().setEraseTokenList(prepsat);
            UkonciSpojeni();
        }

        //----------------------------------------------------------------------------
        // Metoda sloužící pro získávání tokenu z karty
        //----------------------------------------------------------------------------
        public string TransferZKarty_token()
        {
            Inicializace();
            string token = getService().GetFromCard_token();
            UkonciSpojeni();

            //token je v pořadí: 'c a sigPA' (oddělovačem je mezera)
            return token;
        }

        //----------------------------------------------------------------------------
        // Metoda pro získání odezvy na hodnotu 'e'
        //---------------------------------------------------------------------------- 
        public int[] TransferZKarty_eResponse(int[] e)
        {
            Inicializace();
            int[] z = getService().GetFromCard_eResponse(e);
            UkonciSpojeni();

            return z;
        }

        public static void Main()
        {
        }

        //----------------------------------------------------------------------------
        //Metoda sloužící k ustanovení komunikačního kanálu a registraci dané služby
        //---------------------------------------------------------------------------- 
        private void Inicializace()
        {
			string algorithm = string.Empty;        //Vybraný algoritmus
			string keyfile = string.Empty;          //Umístění souboru s klíčem
            bool isSecure = false;

			//Inicializace XML
			StreamReader reader = new StreamReader("..\\..\\AppConfig.xml");
			XmlTextReader xmlReader = new XmlTextReader(reader);

			//Vyčtení názvu zvoleného algoritmu a umístění souboru s klíčem
            while(xmlReader.Read())
			{
				switch(xmlReader.Name)
				{
					case "algorithm":
						algorithm = xmlReader.ReadString();
						break;

					case "keyfile":
						keyfile = xmlReader.ReadString();
						break;
				}
			}

			xmlReader.Close();
			reader.Close();

            FileStream fs = new FileStream(keyfile,FileMode.Open);
            byte[] encryptionKey = new Byte[fs.Length];
            fs.Read(encryptionKey,0,encryptionKey.Length);

            fs.Close();

			//Nastavení vlastností Sinku (vrstva určená pro šifrování)
			Hashtable properties = new Hashtable(); 
			properties["algorithm"] = algorithm;
			properties["key"] = encryptionKey;

			//Vytvožení tohoto Sinku s danými parametry
			IClientChannelSinkProvider provider = new APDUClientFormatterSinkProvider();
			provider.Next = new EncryptionClientSinkProvider(properties);

            //Nastavení připojení ke službě "EncryptionSinkClient" na kartě
            setChannel(new APDUClientChannel("EncryptionSinkClient", provider));
            
            //Registrace komunikačního kanálu
            ChannelServices.RegisterChannel(getChannel(), isSecure);
                
            //Nastavíme požadovanou službu (nacházející se na přislušné URL adrese)
            setService((Service)Activator.GetObject(typeof(Service), URL)); 
        }

        //----------------------------------------------------------------------------
        //Metoda sloužící ke zrušní komunikačního kanálu
        //---------------------------------------------------------------------------- 
        private void UkonciSpojeni()
        {              
            ChannelServices.UnregisterChannel(getChannel());
        }               
    }
}

