package org.bdware.sc.util;

import org.bouncycastle.jcajce.provider.digest.SHA3;
import org.bouncycastle.util.encoders.Hex;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class HashUtil {
    private static final SHA3.DigestSHA3 DIGEST_SHA3 = new SHA3.Digest224();

    public static long bytes2Long(byte[] data) {
        long ret = 0L;
        for (int i = 0; i < 8; ++i) {
            ret <<= 8;
            ret &= ~0xFFL;
            ret |= data[i] & 0xFFL;
        }
        return ret;
    }

    public static byte[] str16ToBytes(String byteStr) {
        byte[] bytes = new byte[byteStr.length() / 2];
        for (int i = 0; i < byteStr.length() / 2; i++) {
            byte b1 = char16ToByte(byteStr.charAt(i * 2));
            byte b2 = char16ToByte(byteStr.charAt(i * 2 + 1));
            bytes[i] = (byte) ((b1 << 4) + b2);
        }
        return bytes;
    }

    public static byte char16ToByte(char c) {
        if (c >= '0' && c <= '9') {
            return (byte) (c - '0');
        }
        if (c >= 'a' && c <= 'f') {
            return (byte) (c - 'a' + 10);
        }
        return 0;
    }

    public static String hashByteArray(byte[] hash) {
        return String.valueOf(Arrays.hashCode(hash));
    }

    public static String byteArray2Str(byte[] data) {
        return byteArray2Str(data, 0);
    }

    public static String byteArray2Str(byte[] byteArray, int offset) {
        return byteArray2StrByLen(byteArray, offset, byteArray.length);
    }

    public static String byteArray2StrByLen(byte[] byteArray, int offset, int len) {
        StringBuilder sb = new StringBuilder();
        for (int j = offset; j < byteArray.length && j < len; j++) {
            byte b = byteArray[j];
            int i = ((int) b) & 0xff;
            sb.append(String.format("%x%x", i >> 4, i & 0xf));
        }
        return sb.toString();
    }

    public static String formatByteArray(byte[] byteArray, int len) {
        StringBuilder sb = new StringBuilder("0x");
        sb.append(byteArray2StrByLen(byteArray, 0, len));
        for (int i = 0; i < len - byteArray.length; ++i) {
            sb.append("00");
        }
        return sb.toString();
    }

    public static byte[] truncation(byte[] source, int len) {
        byte[] ret = new byte[len];
        System.arraycopy(source, 0, ret, 0, len);
        return ret;
    }

    public static int toLittleEndian(int a) {
        return (((a & 0xFF) << 24) | (((a >> 8) & 0xFF) << 16) | (((a >> 16) & 0xFF) << 8) | ((a >> 24) & 0xFF));
    }

    public static long toLittleEndian(long a) {
        return (((a & 0xFFL) << 56) | (((a >> 8) & 0xFFL) << 48) | (((a >> 16) & 0xFFL) << 40)
                | (((a >> 24) & 0xFFL) << 32) | (((a >> 32) & 0xFFL) << 24) | (((a >> 40) & 0xFFL) << 16)
                | (((a >> 48) & 0xFFL) << 8) | (((a >> 56) & 0xFFL)));
    }

    public static long hashStr2Long(String s) {
        long h = 1125899906842597L; // prime
        int len = s.length();

        for (int i = 0; i < len; i++) {
            h = 31 * h + s.charAt(i);
        }
        return h;
    }

    public static String sha3(String... value) {
        return Hex.toHexString(DIGEST_SHA3.digest(String.join("", value).getBytes(StandardCharsets.UTF_8)));
    }

    public static String sha3ToFixedLen(String value, int len) {
        String hash = sha3(value);
        while (hash.length() < len) {
            hash = HashUtil.sha3(hash) + hash;
        }
        return hash.substring(hash.length() - len);
    }
}
