import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import javax.crypto.*
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

class Aes {
    private lateinit var encryptCipher: Cipher
    private lateinit var decryptCipher: Cipher
    private val encryptLock = Any()
    private val decryptLock = Any()

    constructor() {
        try {
            val key = generateKey()
            val seed = SecureRandom().generateSeed(16)
            val iv = IvParameterSpec(seed)
            encryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding").apply {
                init(Cipher.ENCRYPT_MODE, key, iv)
            }
            decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding").apply {
                init(Cipher.DECRYPT_MODE, key, iv)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    constructor(keyArr: ByteArray, ivArr: ByteArray) {
        try {
            val key = SecretKeySpec(keyArr, "AES")
            val iv = IvParameterSpec(ivArr)
            encryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding").apply {
                init(Cipher.ENCRYPT_MODE, key, iv)
            }
            decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding").apply {
                init(Cipher.DECRYPT_MODE, key, iv)
            }
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: NoSuchPaddingException) {
            e.printStackTrace()
        } catch (e: InvalidKeyException) {
            e.printStackTrace()
        } catch (e: InvalidAlgorithmParameterException) {
            e.printStackTrace()
        }
    }

    @Throws(InvalidAlgorithmParameterException::class, InvalidKeyException::class)
    fun setKeyAndIV(keySpec: ByteArray, ivSpec: ByteArray) {
        val secretKeySpec = SecretKeySpec(keySpec, "AES")
        val ivParameterSpec = IvParameterSpec(ivSpec)
        encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
        decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
    }

    @Throws(BadPaddingException::class, ShortBufferException::class, IllegalBlockSizeException::class)
    fun decrypt(input: ByteArray, output: ByteArray, inputLen: Int): Int {
        var ret: Int
        synchronized(decryptLock) { ret = decryptCipher.doFinal(input, 0, inputLen, output) }
        return ret
    }

    @Throws(BadPaddingException::class, IllegalBlockSizeException::class)
    fun decrypt(input: ByteArray, inputLen: Int): ByteArray {
        var output: ByteArray
        synchronized(decryptLock) { output = decryptCipher.doFinal(input, 0, inputLen) }
        return output
    }

    @Throws(BadPaddingException::class, IllegalBlockSizeException::class)
    fun decrypt(input: ByteArray): ByteArray {
        var output: ByteArray
        synchronized(decryptLock) { output = decryptCipher.doFinal(input, 0, input.size) }
        return output
    }

    @Throws(BadPaddingException::class, IllegalBlockSizeException::class)
    fun encrypt(input: ByteArray): ByteArray {
        var output: ByteArray
        synchronized(encryptLock) { output = encryptCipher.doFinal(input, 0, input.size) }
        return output
    }

    @Throws(NoSuchAlgorithmException::class)
    private fun generateKey(): SecretKey {
        val instance = KeyGenerator.getInstance("AES")
        instance.init(128)
        return instance.generateKey()
    }
}