#include "FeistelNetwork.h"


using namespace std;
using namespace FeistelNetwork;

string FeistelNetwork::MultiplyString(const string& s) {
    string result = s;
    while (result.length() < TARGET_LENGTH) {
        result += result;
    }
    return result.substr(0, TARGET_LENGTH);
}

byte FeistelNetwork::CircularShiftLeft(byte value, byte count) {
    return (value << count) | (value >> (8 - count));
}

void FeistelNetwork::G(int index, vector<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);
}

void FeistelNetwork::T(int row, int col, const vector<byte>& msgBytes,
    vector<vector<vector<byte>>>& TArray, const vector<byte>& GArray) {
    byte res1;
    byte res2;

    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);
}

vector<byte> FeistelNetwork::Hash(const vector<byte>& msgBytes) {
    int hashLength = msgBytes.size();

    if (msgBytes.empty()) {
        throw invalid_argument("msgBytes cannot be empty");
    }

    vector<byte> hash(hashLength);
    vector<vector<vector<byte>>> TArray(hashLength,
        vector<vector<byte>>(msgBytes.size(), vector<byte>(2)));
    vector<byte> GArray(hashLength);

    for (int i = 0; i < hash.size(); i++) {
        G(i, GArray);
    }

    for (int i = 0; i < TArray.size(); i++) {
        for (int j = 0; j < TArray[0].size(); j++) {
            T(i, j, msgBytes, TArray, GArray);
        }
        hash[i] = TArray[i][TArray[0].size() - 1][1];
    }

    return hash;
}

vector<uint8_t> FeistelNetwork::ComputeHash(const string& input) {
    if (input.empty()) {
        throw invalid_argument("Input string cannot be empty");
    }

    string data = input + to_string(input.length());
    data = MultiplyString(data);

    vector<byte> strBytes(data.begin(), data.end());
    auto hash = Hash(strBytes);

    std::vector<uint8_t> result(25);
    for (int i = 0; i < 24 && i < hash.size(); i++) {
        result[i] += hash[i];
    }

    return result;
}

void FeistelNetwork::PrintHash(const std::vector<uint8_t>& hash) {
    for (size_t i = 1; i < hash.size(); ++i) {
        printf("%02X ", hash[i]);
    }
    std::cout << std::endl;
}

int FeistelNetwork::CalculateSum(const std::vector<uint8_t>& hash) {
    int sum = 0;
    for (int i = 0; i < hash.size(); i++)
        sum += hash.at(i);
    return sum;
}
