using LabRab1; // ИЗМЕНЕНО: Используем пространство имен для блочного шифра из Лаб. 1

using LabRab2; // Используем пространство имен для хеш-алгоритма из Лаб. 2

using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace SecureCryptoApp // Основное пространство имен приложения
{
    internal static class CryptoProcessor
    {
        static void Main(string[] args)
        {
            // Регистрация кодировки (без изменений)
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            // ИЗМЕНЕНО: Создаем объект хеш-алгоритма с новым именем класса
            var gtHashAlgorithm = new GTHashAlgorithm();
            // ИЗМЕНЕНО: Создаем объект блочного шифра из пространства имен LabRab1
            var blockCipher = new LabRab1.BlockCipher(); // Явно указали LabRab1 для ясности

            const string inputData = "тексттексттекстектстетктст";
            const string secret = "password"; // Пароль

            Console.WriteLine("--- Key Generation (using LabRab2 G/T Hash) ---");
            // ИЗМЕНЕНО: Вызываем ComputeHash у gtHashAlgorithm
            // Получаем СУММУ байт хеша как int, затем приводим к uint для ключа
            uint secretKey = (uint)gtHashAlgorithm.ComputeHash(secret);
            Console.WriteLine($"Derived Key (from hash sum): {secretKey}");
            Console.WriteLine("---------------------------------------------\n");

            // Подготовка данных (24 байта, разделенные на 3 блока по 8)
            // (Логика остается без изменений)
            var processedData = new byte[24];
            byte[] tempBuffer;
            try
            {
                // Используем Windows-1251
                tempBuffer = Encoding.GetEncoding("Windows-1251",
                                                  new EncoderExceptionFallback(),
                                                  new DecoderExceptionFallback())
                                     .GetBytes(inputData);
            }
            catch (EncoderFallbackException e)
            {
                Console.WriteLine($"Error encoding input data: {e.Message}");
                Console.ReadKey();
                return;
            }
            int bytesToCopy = Math.Min(tempBuffer.Length, 24);
            Array.Copy(tempBuffer, processedData, bytesToCopy);

            var dataBlocks = new byte[3][]; // Массив для 3 блоков по 8 байт (x0, x1, x2)
            for (int i = 0; i < 3; i++)
            {
                dataBlocks[i] = new byte[8];
                Array.Copy(processedData, i * 8, dataBlocks[i], 0, 8);
            }

            /////////////////////////////////// ECB Mode (схема 1)
            Console.WriteLine("--- ECB Mode Processing (using LabRab1 BlockCipher) ---");
            // Превращаем блоки в ulong
            var ECBResultsULong = dataBlocks.Select(b => BitConverter.ToUInt64(b)).ToArray();
            // Шифруем каждый блок независимо
            for (int i = 0; i < 3; i++)
            {
                // Используем наш blockCipher из LabRab1
                ECBResultsULong[i] = blockCipher.Encrypt(ECBResultsULong[i], secretKey);
            }
            // Собираем результат
            byte[] ECBFinalResult = BitConverter.GetBytes(ECBResultsULong[0])
                .Concat(BitConverter.GetBytes(ECBResultsULong[1]))
                .Concat(BitConverter.GetBytes(ECBResultsULong[2]))
                .ToArray();
            DisplayResults("ECB", inputData, secret, secretKey, ECBFinalResult);

            /////////////////////////////////// CBC Mode (схема 2)
            Console.WriteLine("--- CBC Mode Processing (using LabRab1 BlockCipher) ---");
            var random = new Random();
            ulong seed = (ulong)random.NextInt64(); // Генерируем IV (seed)
            Console.WriteLine($"Generated Seed (IV): {seed}");
            // Вызываем обработку CBC
            var CBCResultsBytes = ProcessCBCMode(blockCipher, dataBlocks, secretKey, seed);
            // Собираем результат (IV + шифротекст)
            byte[] CBCFinalResult = BitConverter.GetBytes(seed)
                .Concat(CBCResultsBytes[0])
                .Concat(CBCResultsBytes[1])
                .Concat(CBCResultsBytes[2])
                .ToArray();
            DisplayResults("CBC", inputData, secret, secretKey, CBCFinalResult);

            /////////////////////////////////// CFB Mode (схема 3)
            Console.WriteLine("--- CFB Mode Processing (using LabRab1 BlockCipher) ---");
            // Используем тот же seed
            Console.WriteLine($"Using Seed (IV): {seed}");
            // Вызываем обработку CFB
            var CFBResultsBytes = ProcessCFBMode(blockCipher, dataBlocks, secretKey, seed);
            // Собираем результат (IV + шифротекст)
            byte[] CFBFinalResult = BitConverter.GetBytes(seed)
                .Concat(CFBResultsBytes[0])
                .Concat(CFBResultsBytes[1])
                .Concat(CFBResultsBytes[2])
                .ToArray();
            DisplayResults("CFB", inputData, secret, secretKey, CFBFinalResult);

            /////////////////////////////////// IGE Mode (схема 4)
            Console.WriteLine("--- IGE Mode Processing (using LabRab1 BlockCipher) ---");
            // Используем один seed для обоих начальных значений (как было)
            ulong seed1_ige = seed;
            ulong seed2_ige = seed;
            Console.WriteLine($"Using Seed1={seed1_ige}, Seed2={seed2_ige} for IGE");
            // Вызываем обработку IGE
            var IGEResultsBytes = ProcessIGEMode(blockCipher, dataBlocks, secretKey, seed1_ige, seed2_ige);
            // Собираем результат (IV1 + IV2 + шифротекст)
            byte[] IGEFinalResult = BitConverter.GetBytes(seed1_ige)
                 .Concat(BitConverter.GetBytes(seed2_ige))
                 .Concat(IGEResultsBytes[0])
                 .Concat(IGEResultsBytes[1])
                 .Concat(IGEResultsBytes[2])
                 .ToArray();
            DisplayResults("IGE", inputData, secret, secretKey, IGEFinalResult);

            Console.WriteLine("\nPress any key to exit...");
            Console.ReadKey();
        }

        // --- Функции для режимов шифрования ---
        // Используют BlockCipher из LabRab1

        // CBC: z_i = crypt(z_{i-1} ^ x_i) (без изменений)
        private static byte[][] ProcessCBCMode(BlockCipher cipher, byte[][] blocks, uint key, ulong iv)
        {
            ulong previousCiphertext = iv;
            var results = new byte[3][];
            for (int i = 0; i < 3; i++)
            {
                var currentPlaintext = BitConverter.ToUInt64(blocks[i]);
                var inputToEncrypt = previousCiphertext ^ currentPlaintext;
                var currentCiphertext = cipher.Encrypt(inputToEncrypt, key); // Шифруем с помощью LabRab1.BlockCipher
                results[i] = BitConverter.GetBytes(currentCiphertext);
                previousCiphertext = currentCiphertext;
            }
            return results;
        }

        // CFB: z_i = crypt(z_{i-1}) ^ x_i (без изменений)
        private static byte[][] ProcessCFBMode(BlockCipher cipher, byte[][] blocks, uint key, ulong iv)
        {
            ulong previousCiphertext = iv;
            var results = new byte[3][];
            for (int i = 0; i < 3; i++)
            {
                var encryptedFeedback = cipher.Encrypt(previousCiphertext, key); // Шифруем с помощью LabRab1.BlockCipher
                var currentPlaintext = BitConverter.ToUInt64(blocks[i]);
                var currentCiphertext = encryptedFeedback ^ currentPlaintext;
                results[i] = BitConverter.GetBytes(currentCiphertext);
                previousCiphertext = currentCiphertext;
            }
            return results;
        }

        // IGE: temp_i = crypt(z_{i-1} ^ x_i); z_i = temp_i ^ x_{i-1} (без изменений)
        private static byte[][] ProcessIGEMode(BlockCipher cipher, byte[][] blocks, uint key, ulong iv1, ulong iv2)
        {
            ulong previousCiphertext = iv1;
            ulong previousPlaintext = iv2;
            var results = new byte[3][];
            for (int i = 0; i < 3; i++)
            {
                var currentPlaintext = BitConverter.ToUInt64(blocks[i]);
                var inputToEncrypt = previousCiphertext ^ currentPlaintext;
                var tempResult = cipher.Encrypt(inputToEncrypt, key); // Шифруем с помощью LabRab1.BlockCipher
                var currentCiphertext = tempResult ^ previousPlaintext;
                results[i] = BitConverter.GetBytes(currentCiphertext);
                previousCiphertext = currentCiphertext;
                previousPlaintext = currentPlaintext;
            }
            return results;
        }

        // Функция вывода результатов (без изменений)
        private static void DisplayResults(string mode, string input, string secret, uint key, byte[] result)
        {
            Console.WriteLine($"--- {mode} Mode Results ---");
            Console.WriteLine($"Input String: \"{input}\"");
            Console.WriteLine($"Secret Phrase: \"{secret}\"");
            // Ключ уже выведен глобально
            Console.WriteLine($"Generated Seed/IV (if applicable): Included in hex output below");
            Console.WriteLine($"Final Result (hex): {BitConverter.ToString(result).Replace("-", "")}");
            Console.WriteLine("------------------------\n");
        }
    }
}
