package com.saki.encode;

import com.saki.QVMProtect;

@QVMProtect
public class TEA {

    private static long delta = 0x9E3779B9L;

    // 此方法解密一段为8的整数倍的数据
    public static byte[] decrypt(byte[] data, byte[] key,int loop) {
        int group_len = 8;
        if (data.length % group_len != 0) {
            return new byte[]{};
        }
        TEA t = new TEA();
        t.setLoops(loop);
        if (!t.setKey(key)) {
            return new byte[]{};
        }
        t.setLoops(16);
        return t.decrypt_group(data);
    }

    // 此方法加密一段长度为8的整数倍的数据
    public static byte[] encrypt(byte[] data, byte[] key,int loop) {
        if (data.length == 0) {
            return new byte[]{};
        }
        TEA t = new TEA();
        t.setLoops(loop);
        if (!t.setKey(key)) {
            return new byte[]{};
        }
        t.setLoops(16);
        return t.encrypt_group(data);
    }

    private long k0, k1, k2, k3;

    private int loops = 32;

    /**
     * 解密一组密文
     *
     * @param v 要解密的密文
     * @return 返回明文
     */
    private byte[] decrypt_group(byte[] v) {
        long v0 = Canvart.Bytes2UnsignedInt32(new byte[]{v[0], v[1], v[2],
                v[3]});
        long v1 = Canvart.Bytes2UnsignedInt32(new byte[]{v[4], v[5], v[6],
                v[7]});
        long tmp = 0L, sum;
        if (loops == 32)
            sum = 0xC6EF3720; /* delta << 5 */
        else if (loops == 16)
            sum = 0xE3779B90; /* delta << 4 */
        else
            sum = delta * loops;
        for (int i = 0; i < loops; i++) {
            tmp = Canvart
                    .toUnsignedInt32(Canvart.toUnsignedInt32(v0 << 4) + k2);
            v1 = Canvart.toUnsignedInt32(v1
                    - Canvart.toUnsignedInt32(tmp
                    ^ Canvart.toUnsignedInt32(v0 + sum)
                    ^ Canvart.toUnsignedInt32((v0 >> 5) + k3)));
            tmp = Canvart
                    .toUnsignedInt32(Canvart.toUnsignedInt32(v1 << 4) + k0);
            v0 = Canvart.toUnsignedInt32(v0
                    - Canvart.toUnsignedInt32(tmp
                    ^ Canvart.toUnsignedInt32(v1 + sum)
                    ^ Canvart.toUnsignedInt32((v1 >> 5) + k1)));
            sum = Canvart.toUnsignedInt32(sum - delta);
        }
        byte[] b0 = Canvart.Long2Bytes(v0, 4);
        byte[] b1 = Canvart.Long2Bytes(v1, 4);
        return new byte[]{b0[0], b0[1], b0[2], b0[3], b1[0], b1[1], b1[2],
                b1[3]};
    }

    /**
     * 加密一组明文
     *
     * @param v 需要加密的明文
     * @return 返回密文
     */
    private byte[] encrypt_group(byte[] v) {
        long v0 = Canvart.Bytes2UnsignedInt32(new byte[]{v[0], v[1], v[2],
                v[3]});
        long v1 = Canvart.Bytes2UnsignedInt32(new byte[]{v[4], v[5], v[6],
                v[7]});
        long sum = 0L;
        long v0_xor_1 = 0L, v0_xor_2 = 0L, v0_xor_3 = 0L;
        long v1_xor_1 = 0L, v1_xor_2 = 0L, v1_xor_3 = 0L;
        for (int i = 0; i < loops; i++) {
            sum = Canvart.toUnsignedInt32(sum + delta);
            v0_xor_1 = Canvart.toUnsignedInt32(Canvart.toUnsignedInt32(v1 << 4)
                    + k0);
            v0_xor_2 = Canvart.toUnsignedInt32(v1 + sum);
            v0_xor_3 = Canvart.toUnsignedInt32((v1 >> 5) + k1);
            v0 = Canvart.toUnsignedInt32(v0
                    + Canvart.toUnsignedInt32(v0_xor_1 ^ v0_xor_2 ^ v0_xor_3));
            v1_xor_1 = Canvart.toUnsignedInt32(Canvart.toUnsignedInt32(v0 << 4)
                    + k2);
            v1_xor_2 = Canvart.toUnsignedInt32(v0 + sum);
            v1_xor_3 = Canvart.toUnsignedInt32((v0 >> 5) + k3);
            v1 = Canvart.toUnsignedInt32(v1
                    + Canvart.toUnsignedInt32(v1_xor_1 ^ v1_xor_2 ^ v1_xor_3));
        }
        byte[] b0 = Canvart.Long2Bytes(v0, 4);
        byte[] b1 = Canvart.Long2Bytes(v1, 4);
        return new byte[]{b0[0], b0[1], b0[2], b0[3], b1[0], b1[1], b1[2],
                b1[3]};
    }

    /**
     * 设置密钥
     *
     * @param k 密钥
     * @return 密钥长度为16个byte时， 设置密钥并返回true，否则返回false
     */
    public boolean setKey(byte[] k) {
        if (k == null || k.length != 16) {
            return false;
        }
        k0 = Canvart.Bytes2UnsignedInt32(new byte[]{k[0], k[1], k[2], k[3]});
        k1 = Canvart.Bytes2UnsignedInt32(new byte[]{k[4], k[5], k[6], k[7]});
        k2 = Canvart
                .Bytes2UnsignedInt32(new byte[]{k[8], k[9], k[10], k[11]});
        k3 = Canvart.Bytes2UnsignedInt32(new byte[]{k[12], k[13], k[14],
                k[15]});
        return true;
    }

    /**
     * 设置加密的轮数，默认为32轮
     *
     * @param loops 加密轮数
     * @return 轮数为16、32、64时，返回true，否则返回false
     */
    public boolean setLoops(int loops) {
        switch (loops) {
            case 16:
            case 32:
            case 64:
                this.loops = loops;
                return true;
        }
        return false;
    }

}
