package com.jihe.punchnet.sdlan.utils

import com.jihe.punchnet.sdlan.config.AESConfig
import com.jihe.punchnet.sdlan.logs.TerminalLogger
import java.nio.ByteBuffer
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

class AES private constructor () {
    private var _secret: ByteArray = ByteArray(0)
    private var _iv: ByteArray = ByteArray(0)
    companion object {
        private var instance: AES? = null
            get() {
                if (field == null) {
                    field = AES()
                }
                return field
            }

        @Synchronized
        fun getAES(): AES {
            return instance!!
        }
    }

    fun isAuthorized(): Boolean {
        return _secret.size != 0
    }

    fun setSecret(secret: ByteArray?) {
        if (secret == null) {
            _secret = ByteArray(0)
            _iv = ByteArray(0)
        } else {
            _secret = secret
            _iv = _secret.sliceArray(0..<16)
        }
    }

    fun encrypt(content: ByteBuffer): ByteBuffer? {
        if (!isAuthorized()) {
            return null
        }
        val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM)
        val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC)
        try {
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, IvParameterSpec(_iv))
            val output = ByteBuffer.allocate(cipher.getOutputSize(content.remaining()))
            cipher.doFinal(content, output)
            output.flip()
            return output
        } catch (e: Exception) {
            println("failed to encrypt: ${e.toString()}")
            return null
        }
    }

    fun encrypt(content: ByteArray): ByteArray? {
        if (!isAuthorized()) {
            TerminalLogger.errorf { "not authed, so not encrypting" }
            return null
        }
        val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM)
        val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC)
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, IvParameterSpec(_iv))
        try {
            val encrypted = cipher.doFinal(content)
            return encrypted
        } catch (e: Exception) {
            TerminalLogger.errorf {"encrypt failed: $e"}
            return null
        }
    }

    fun decrypt(ciphered: ByteBuffer): ByteBuffer? {
        if (!isAuthorized()) {
            return null
        }
        val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM)
        val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC)
        cipher.init(Cipher.DECRYPT_MODE, keyspec, IvParameterSpec(_iv))
        try {
            val output = ByteBuffer.allocate(cipher.getOutputSize(ciphered.remaining()))
            cipher.doFinal(ciphered, output)
            output.flip()
            return output
        } catch (e: Exception) {
            return null
        }
    }

    fun decrypt(ciphered: ByteArray): ByteArray? {
        if (!isAuthorized()) {
            return null
        }
        val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM)
        val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC)
        cipher.init(Cipher.DECRYPT_MODE, keyspec, IvParameterSpec(_iv))
        try {
            val decrypted = cipher.doFinal(ciphered)
            return decrypted
        } catch (e: Exception) {
            return null
        }
    }
}

fun byteArray2Hex(array: ByteArray): String {
    var result = mutableListOf<String>()
    for (item in array) {
        result.add(String.format("0x%02x", item.toInt() and 0xff))
    }
    return result.joinToString(" ")
}