package com.cscn;

import java.util.Arrays;

import static com.cscn.Zuc256Core.zuc256GenerateKeystream;
import static com.cscn.Zuc256Core.zuc256GenerateKeyword;
import static com.cscn.Zuc256Util.getU32;
import static com.cscn.Zuc256Util.putU32;


/**
 * 加密上下文类
 */
public final class Zuc256EncryptCtx {
    Zuc256State state;
    byte[] buf;
    int buflen;

    public Zuc256EncryptCtx(Zuc256State state, byte[] buf){
        this.state = state;
        this.buf = buf;
    }

    public Zuc256EncryptCtx(Zuc256State state){
        this.state = state;
        this.buf = new byte[4];
    }

    public Zuc256EncryptCtx(){
        this.state = new Zuc256State();
        this.buf = new byte[4];
    }

    // 初始化加密上下文
    public void init(byte[] key32, byte[] iv) {
        Arrays.fill(this.buf, (byte) 0);
        this.buflen = 0;
        Zuc256Core.initState(this.state, key32, iv);
    }

    // 分阶段处理加密数据
    public void update(byte[] in, int inlen, byte[] out) {
        if (in == null || out == null || inlen == 0) return;

        // 处理缓冲区中剩余的非4字节数据
        if (this.buflen > 0) {
            int need = 4 - this.buflen;
            int copy = Math.min(inlen, need);

            System.arraycopy(in, 0, this.buf, this.buflen, copy);
            this.buflen += copy;

            // 调整输入指针和长度
            byte[] newIn = new byte[inlen - copy];
            if (inlen - copy > 0) {
                System.arraycopy(in, copy, newIn, 0, inlen - copy);
            }
            in = newIn;
            inlen -= copy;

            // 缓冲区已满，处理一个完整的4字节块
            if (this.buflen == 4) {
                int keystream = zuc256GenerateKeyword(this.state);
                int plain = getU32(this.buf, 0);
                putU32(out, 0, plain ^ keystream);

                this.buflen = 0;
                Arrays.fill(this.buf, (byte) 0);

                // 调整输出指针
                byte[] newOut = new byte[out.length - 4];
                if (out.length - 4 > 0) {
                    System.arraycopy(out, 4, newOut, 0, out.length - 4);
                }
                out = newOut;
            }
        }

        // 处理完整的4字节块
        int fullBlocks = inlen / 4;
        if (fullBlocks > 0) {
            int[] keystream = new int[fullBlocks];
            zuc256GenerateKeystream(this.state, fullBlocks, keystream);

            // 逐块异或加密
            for (int i = 0; i < fullBlocks; i++) {
                int plain = getU32(in, i * 4);
                putU32(out, i * 4, plain ^ keystream[i]);
            }

            // 调整输入指针和长度
            int processed = fullBlocks * 4;
            byte[] newIn = new byte[inlen - processed];
            if (inlen - processed > 0) {
                System.arraycopy(in, processed, newIn, 0, inlen - processed);
            }
            in = newIn;
            inlen -= processed;
        }

        // 缓存剩余不足4字节的数据
        if (inlen > 0) {
            System.arraycopy(in, 0, this.buf, 0, inlen);
            this.buflen = inlen;
        }
    }

    // 完成加密处理
    public void finish(byte[] out) {
        if (this == null || out == null) return;

        // 处理缓冲区中剩余的不足4字节数据
        if (this.buflen > 0) {
            int keystream = zuc256GenerateKeyword(this.state);
            byte[] keystreamBytes = new byte[4];
            putU32(keystreamBytes, 0, keystream);

            // 逐字节异或
            for (int i = 0; i < this.buflen; i++) {
                out[i] = (byte) (this.buf[i] ^ keystreamBytes[i]);
            }
        }

        // 清理上下文
        Arrays.fill(this.buf, (byte) 0);
        this.buflen = 0;
        Arrays.fill(this.state.LFSR, 0);
        this.state.R1 = 0;
        this.state.R2 = 0;
    }
}
