using NUnit.Framework;
using NFC;
using NFC.Readers.PCSC;
using NFC.Mifare_DESFire;
using NFC.Mifare_DESFire.Enums;
using System;
using System.Text;
using NFC.Crypto;

namespace NFC_Real_Test
{
    [TestFixture, Explicit]
    public class REAL_FabAccess_OTA
    {
        private string _ReaderID = "ACS ACR122U PICC Interface 0";
        private UInt32 _FabAccess_AID = 0x2A472D;
        private byte _FabAccess_FID = 0x01;
        private UInt32 _FabAccess_FSize = 0xF0;

        // Change of PICC Key is not implementet yet
        // private CipherKey _FabAccess_Card_MasterKey = new CipherKey("294A404E635266556A576E5A72347537", CipherType.AES, 0x10);

        private CipherKey _FabAccess_Application_MasterKey = new CipherKey("50645367566B59703273357638792F42", CipherType.AES, 0x10);
        private CipherKey _FabAccess_Application_AuthKey = new CipherKey("6D5A7134743677397A24432646294A40", CipherType.AES, 0x10);

        private string _FabAccess_UserDomain = "verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrylooooooooooooooooooongusssssssssssssssssssssssernaaaaaaaaaaaaaaaaaaaaaaaame@fvm.fab-access.org";
        private string _FabAccess_Domain = "fvm.fab-access.org";

        private CipherKey _Default_DESKey = new CipherKey(CipherType.TDES);
        private CipherKey _Default_AESKey = new CipherKey(CipherType.AES);

        /// <summary>
        /// Create FabAccess Application and UserData File
        /// </summary>
        [Test, Order(1)]
        public void Init_EmptyCard()
        {
            IHardware hardware = new PCSC_Hardware();
            IReader reader = hardware.OpenReader(_ReaderID);

            bool transmit_successfully = false;

            ReaderEventHandler handler = (sender, card) =>
            {
                card.Connect();

                MIFARE_DESFire desfire = new MIFARE_DESFire(card);

                desfire.SelectApplication(0x000000);
                desfire.AuthenticateISO_DES(0x00, _Default_DESKey._Key);
                desfire.Format();

                desfire.AuthenticateISO_DES(0x00, _Default_DESKey._Key);

                byte keySetting1 = desfire.GenerateKeySetting1(ChangeApplicationKey.MASTERKEY, ChangeMasterKeySettings.WITHMASTERKEY, CreateDeleteFile.ONLYMASTERKEY, FileDirectoryAccess.NOKEY, ChangeMasterKey.CHANGEABLE);
                byte keySetting2 = desfire.GenerateKeySetting2(CryptoOperationsType.AES, FileIdentifies.NOTUSED, 0x02);
                desfire.CreateApplication(_FabAccess_AID, keySetting1, keySetting2);

                desfire.SelectApplication(_FabAccess_AID);
                desfire.AuthenticateISO_AES(0x00, _Default_AESKey._Key);

                desfire.ChangeKey_AES(0x00, _FabAccess_Application_MasterKey._Key, _FabAccess_Application_MasterKey._KeyVersion);

                desfire.AuthenticateISO_AES(0x00, _FabAccess_Application_MasterKey._Key);
                desfire.ChangeOtherKey_AES(0x01, _FabAccess_Application_AuthKey._Key, _Default_AESKey._Key, _FabAccess_Application_AuthKey._KeyVersion);

                UInt16 accesRights = desfire.GenerateFileAccessRights((byte)FileAccessRights.FREE, 0x00, 0x00, 0x00);
                desfire.CreateFile_Standard(_FabAccess_FID, FileCommunication.PLAIN, accesRights, _FabAccess_FSize);

                desfire.WriteData(_FabAccess_FID, 0, Encoding.ASCII.GetBytes(_FabAccess_UserDomain));

                transmit_successfully = true;

                card.Disconnect();
            };

            reader.CardDiscovered += handler;
            reader.Start();

            Assert.AreEqual(true, transmit_successfully);

            reader.Stop();
            reader.CardDiscovered -= handler;
        }

        /// <summary>
        /// Authenticate with UserData File and AuthKey
        /// </summary>
        [Test, Order(2)]
        public void Authenticate()
        {
            IHardware hardware = new PCSC_Hardware();
            IReader reader = hardware.OpenReader(_ReaderID);

            bool transmit_successfully = false;

            ReaderEventHandler handler = (sender, card) =>
            {
                card.Connect();

                MIFARE_DESFire desfire = new MIFARE_DESFire(card);

                desfire.SelectApplication(_FabAccess_AID);
                byte[] card_data = desfire.ReadData(_FabAccess_FID, 0x00, _FabAccess_FSize);
                string userdomain = Encoding.ASCII.GetString(card_data).Replace("\u0000", "");

                string domain = userdomain.Split('@')[1];
                if(domain != _FabAccess_Domain)
                {
                    throw new Exception("Incorrect Domain");
                }

                desfire.SelectApplication(_FabAccess_AID);
                desfire.AuthenticateISO_AES(0x01, _FabAccess_Application_AuthKey._Key);

                transmit_successfully = true;

                card.Disconnect();
            };

            reader.CardDiscovered += handler;
            reader.Start();

            Assert.AreEqual(true, transmit_successfully);

            reader.Stop();
            reader.CardDiscovered -= handler;
        }
    }
}