package crypto

import (
	"agent/defines/derrs"
	"crypto/rc4"
)

func NewRc4(key any) (ICipher, error) {
	cipher := &rc4impl{}
	if err := cipher.SetKey(key); err != nil {
		return nil, err
	}
	return cipher, nil
}

type rc4impl struct {
	encryptIndex int64
	decryptIndex int64
	key          []byte
	encrypt      *rc4.Cipher
	decrypt      *rc4.Cipher
}

func (n *rc4impl) New(key any) ICipher {
	cipher := &rc4impl{}
	if err := cipher.SetKey(key); err != nil {
		panic(err)
	}
	return cipher
}

func (n *rc4impl) SetKey(key any) (err error) {
	if n.key, err = any2key(key); err != nil {
		return err
	}
	if !(len(n.key) > 0 && len(n.key) <= 256) {
		return derrs.NewIncorrectParamsError("incorrect key size")
	}
	if n.encrypt, err = rc4.NewCipher(n.key); err != nil {
		return err
	}
	if n.decrypt, err = rc4.NewCipher(n.key); err != nil {
		return err
	}
	n.encryptIndex = 0
	n.decryptIndex = 0
	return nil
}

func (n *rc4impl) Encrypt(input []byte, output []byte) (int, error) {
	outputSize := n.EncryptOutputSize(len(input))
	if len(output) < outputSize {
		return outputSize, derrs.NewIncorrectParamsError("len(output) < outputSize")
	}

	n.encrypt.XORKeyStream(output, input)
	n.encryptIndex += int64(outputSize)

	return outputSize, nil
}

func (n *rc4impl) Encrypt2(input []byte) ([]byte, error) {
	outputSize := n.EncryptOutputSize(len(input))
	output := make([]byte, outputSize)

	n.encrypt.XORKeyStream(output, input)
	n.encryptIndex += int64(outputSize)

	return output, nil
}

func (n *rc4impl) Decrypt(input []byte, output []byte) (int, error) {
	outputSize := n.DecryptOutputSize(len(input))
	if len(output) < outputSize {
		return outputSize, derrs.NewIncorrectParamsError("len(output) < outputSize")
	}

	n.decrypt.XORKeyStream(output, input)
	n.decryptIndex += int64(outputSize)

	return outputSize, nil
}

func (n *rc4impl) Decrypt2(input []byte) ([]byte, error) {
	outputSize := n.DecryptOutputSize(len(input))
	output := make([]byte, outputSize)

	n.decrypt.XORKeyStream(output, input)
	n.decryptIndex += int64(outputSize)

	return output, nil
}

func (n *rc4impl) DecryptBlock(input []byte, output []byte, blocks int) (int, error) {
	outputSize := n.EncryptOutputSize(n.BlockSize() * blocks)
	if len(output) < outputSize {
		return outputSize, derrs.NewIncorrectParamsError("len(output) < outputSize")
	}

	n.decrypt.XORKeyStream(output, input)
	n.decryptIndex += int64(outputSize)

	return outputSize, nil
}

func (n *rc4impl) EncryptOutputSize(inputSize int) int {
	return inputSize
}

func (n *rc4impl) DecryptOutputSize(inputSize int) int {
	return inputSize
}

func (n *rc4impl) BlockSize() int {
	return 1
}

func (n *rc4impl) PlainTextBlockSize() int {
	return 1
}

func (n *rc4impl) Encrypted() int {
	return int(n.encryptIndex)
}

func (n *rc4impl) Decrypted() int {
	return int(n.decryptIndex)
}
