using System;
using System.Text;

// Оставляем пространство имен LabRab2, т.к. хеш-алгоритм из Лаб. 2
namespace LabRab2
{
    // ПЕРЕИМЕНОВАЛИ КЛАСС! Теперь он отражает суть алгоритма (G/T хеш)
    public class GTHashAlgorithm
    {
        // Константы G/T алгоритма (остаются без изменений)
        private const byte T_PLUS = 165;
        private const byte G_PLUS = 23;
        private const byte T_SHIFT = 3;
        private const byte G_SHIFT = 3;
        private const int TARGET_LENGTH = 128; // Длина для расширения данных

        // Метод для вычисления хеша из строки и получения СУММЫ первых 24 байт
        // Именно этот метод вызывается для генерации ключа из пароля
        public int ComputeHash(string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                throw new ArgumentException("Input string cannot be null or empty.", nameof(data));
            }

            Console.WriteLine($"Hashing string: \"{data}\""); // Логгирование

            // Шаг 1а: Добавляем длину
            string processedString = data + data.Length;
            Console.WriteLine($"String with length appended: \"{processedString}\"");

            // Шаг 1б: Расширяем до 128 символов
            processedString = MultiplyString(processedString);
            Console.WriteLine($"String multiplied/truncated to {TARGET_LENGTH} chars: \"{processedString.Substring(0, Math.Min(30, processedString.Length))}...\""); // Показываем начало

            // Кодировка должна быть зарегистрирована в Main
            byte[] strBytes;
            try
            {
                // Используем Windows-1251, как и раньше
                strBytes = Encoding.GetEncoding("Windows-1251",
                                                  new EncoderExceptionFallback(),
                                                  new DecoderExceptionFallback())
                                     .GetBytes(processedString);
            }
            catch (EncoderFallbackException e)
            {
                Console.WriteLine($"FATAL: Error encoding the processed string to Windows-1251: {e.Message}");
                throw; // Без байтов хешировать нечего
            }
            Console.WriteLine($"String as Windows-1251 bytes ({strBytes.Length}) ready for hashing.");

            // Шаг 1в: Вычисляем 128-байтный хеш
            var hashBytes = Hash(strBytes);
            Console.WriteLine($"Generated 128-byte hash. First few bytes (hex): {BitConverter.ToString(hashBytes, 0, Math.Min(16, hashBytes.Length)).Replace("-", "")}");

            // Суммируем первые 24 байта для получения итогового значения (ключа)
            int resultSum = 0;
            int bytesToSum = Math.Min(24, hashBytes.Length); // Берем 24 или меньше, если хеш короче
            for (int i = 0; i < bytesToSum; i++)
            {
                resultSum += hashBytes[i];
            }
            Console.WriteLine($"Sum of first {bytesToSum} hash bytes: {resultSum}");

            // Возвращаем именно СУММУ (как int). Приведение к uint будет в Main.
            return resultSum;
        }

        // Функция расширения строки (без изменений)
        private string MultiplyString(string s)
        {
            StringBuilder sb = new StringBuilder(s);
            while (sb.Length < TARGET_LENGTH)
            {
                // Добавляем исходную строку (s), а не то, что уже накоплено в sb,
                // чтобы точно соответствовать описанию циклического повтора.
                sb.Append(s);
            }
            return sb.ToString(0, TARGET_LENGTH); // Обрезаем до 128
        }

        // Основная функция G/T хеширования (128 байт) (без изменений)
        private byte[] Hash(byte[] msgBytes)
        {
            if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes));
            // Добавим проверку на соответствие длины ожиданиям
            if (msgBytes.Length != TARGET_LENGTH)
            {
                // Это не должно происходить, если MultiplyString работает верно
                Console.WriteLine($"Warning: Hash function received {msgBytes.Length} bytes, expected {TARGET_LENGTH}. Algorithm might behave unexpectedly.");
            }
            if (msgBytes.Length == 0) throw new ArgumentException("Input byte array cannot be empty.", nameof(msgBytes));

            int hashLength = msgBytes.Length; // Длина будет 128
            var hash = new byte[hashLength];
            // TArray будет 128 x 128 x 2
            var TArray = new byte[hashLength, hashLength, 2];
            var GArray = new byte[hashLength];

            // Вычисляем GArray
            for (int i = 0; i < hashLength; i++)
            {
                G(i, GArray);
            }

            // Вычисляем TArray и собираем хеш
            for (int i = 0; i < hashLength; i++) // Цикл по строкам
            {
                for (int j = 0; j < hashLength; j++) // Цикл по столбцам
                {
                    T(i, j, msgBytes, TArray, GArray);
                }
                // Берем результат из самого правого столбца
                hash[i] = TArray[i, hashLength - 1, 1];
            }

            return hash; // Возвращаем 128 байт
        }

        // Функция G (без изменений)
        private void G(int index, byte[] GArray)
        {
            if (index == 0)
                GArray[index] = CircularShiftLeft((byte)(G_PLUS + 1), G_SHIFT);
            else
                GArray[index] = CircularShiftLeft((byte)(G_PLUS + GArray[index - 1]), G_SHIFT);
        }

        // Функция T (без изменений)
        private void T(int row, int col, byte[] msgBytes, byte[,,] TArray, byte[] GArray)
        {
            byte res1;
            byte res2;

            // Логика T остается прежней
            if (row == 0)
            {
                if (col == 0)
                {
                    res1 = (byte)(GArray[row] ^ CircularShiftLeft(msgBytes[0], T_SHIFT));
                    res2 = (byte)(T_PLUS ^ 1);
                }
                else
                {
                    res1 = (byte)(TArray[row, col - 1, 1] ^ CircularShiftLeft(msgBytes[col], T_SHIFT));
                    res2 = (byte)(T_PLUS ^ msgBytes[col - 1]);
                }
            }
            else
            {
                if (col == 0)
                {
                    res1 = (byte)(GArray[row] ^ CircularShiftLeft(TArray[row - 1, col, 0], T_SHIFT));
                    res2 = (byte)(T_PLUS ^ GArray[row - 1]);
                }
                else
                {
                    res1 = (byte)(TArray[row, col - 1, 1] ^ CircularShiftLeft(TArray[row - 1, col, 0], T_SHIFT));
                    res2 = (byte)(T_PLUS ^ TArray[row - 1, col - 1, 0]);
                }
            }

            TArray[row, col, 0] = res1;
            TArray[row, col, 1] = (byte)(res1 + res2); // Сложение по модулю 256
        }

        // Циклический сдвиг влево (без изменений)
        private byte CircularShiftLeft(byte value, byte count)
        {
            count %= 8;
            if (count == 0) return value;
            return (byte)((value << count) | (value >> (8 - count)));
        }
    }
}
