package com.dlc.eboard.serial

import java.security.InvalidParameterException
import kotlin.experimental.and

/**
 * info
 *
 * @author ReiChin_
 */
object HexDump {

    private val HEX_DIGITS = charArrayOf(
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    )

    fun dumpHexString(array: ByteArray, offset: Int = 0, length: Int = array.size): String {
        val result = StringBuilder()
        val line = ByteArray(8)
        var lineIndex = 0
        for (i in offset until offset + length) {
            if (lineIndex == line.size) {
                for (j in line.indices) {
                    val txt = if (line[j].toChar() in '!'..'}') String(line, j, 1) else "."
                    result.append(txt)
                }
                result.append("\n")
                lineIndex = 0
            }
            val b = array[i]
            result.append(HEX_DIGITS[b.toInt() ushr 4 and 0x0F])
            result.append(HEX_DIGITS[b.toInt() and 0x0F])
            result.append(" ")
            line[lineIndex++] = b
        }
        for (i in 0 until line.size - lineIndex) {
            result.append("   ")
        }
        for (i in 0 until lineIndex) {
            val txt = if (line[i].toChar() in '!'..'}') String(line, i, 1) else "."
            result.append(txt)
        }
        return result.toString()
    }

    fun toHexString(array: ByteArray, offset: Int = 0, length: Int = array.size): String {
        val buf = CharArray(length * 2)
        var bufIndex = 0
        for (i in offset until offset + length) {
            val b = array[i]
            buf[bufIndex++] = HEX_DIGITS[b.toInt() ushr 4 and 0x0F]
            buf[bufIndex++] = HEX_DIGITS[b.toInt() and 0x0F]
        }
        return String(buf)
    }

    fun toHexString(b: Byte) = toHexString(byteArrayOf(b))

    fun toHexString(i: Int): String {
        return toHexString(toByteArray(i))
    }

    fun toByteArray(i: Int): ByteArray {
        val array = ByteArray(4)
        array[3] = (i and 0xFF).toByte()
        array[2] = (i shr 8 and 0xFF).toByte()
        array[1] = (i shr 16 and 0xFF).toByte()
        array[0] = (i shr 24 and 0xFF).toByte()
        return array
    }

    fun toHexString(i: Short): String {
        return toHexString(toByteArray(i))
    }

    fun toByteArray(i: Short): ByteArray {
        val array = ByteArray(2)
        array[1] = (i and 0xFF).toByte()
        array[0] = (i.toInt() shr 8 and 0xFF).toByte()
        return array
    }

    private fun toByte(c: Char): Int {
        if (c in '0'..'9') return c - '0'
        if (c in 'A'..'F') return c - 'A' + 10
        if (c in 'a'..'f') return c - 'a' + 10
        throw InvalidParameterException("Invalid hex char '$c'")
    }

    fun hexStringToByteArray(hexString: String): ByteArray {
        val length = hexString.length
        val buffer = ByteArray(length / 2)
        var i = 0
        while (i < length) {
            buffer[i / 2] = (toByte(hexString[i]) shl 4 or toByte(hexString[i + 1])).toByte()
            i += 2
        }
        return buffer
    }

    fun hexToAscii(hexStr: String): String {
        val output = StringBuilder()
        var i = 0
        while (i < hexStr.length) {
            val str = hexStr.substring(i, i + 2)
            output.append(str.toInt(16).toChar())
            i += 2
        }
        return output.toString()
    }

    fun hexToDecimal(hexStr: String): Long {
        return hexStr.fold(0L) { acc, c ->
            acc * 16 + charToDecimal(c)
        }
    }

    fun charToDecimal(c: Char): Int {
        return when (c) {
            in 'A'..'F' -> {
                10 + c.toInt() - 'A'.toInt()
            }
            in 'a'..'f' -> {
                10 + c.toInt() - 'a'.toInt()
            }
            else -> c - '0'
        }
    }

    /**
     * 计算CRC16校验码
     */
    fun getCRC_MODBUS(bytes: ByteArray): String {
        // CRC寄存器全为1
        var CRC = 0x0000ffff
        // 多项式校验值
        val POLYNOMIAL = 0x0000a001

        bytes.forEach {
            CRC = CRC xor (it.toInt() and 0x000000ff)
            for (i in 0..7) {
                if (CRC and 0x00000001 != 0) {
                    CRC = CRC shr 1
                    CRC = CRC xor POLYNOMIAL
                } else {
                    CRC = CRC shr 1
                }
            }
        }

        // 交换高低位，低位在前高位在后
        CRC = CRC and 0x0000FF00 shr 8 or (CRC and 0x000000FF shl 8)

        return with(toHexString(CRC)) {
            when {
                length > 4 -> subSequence(length - 4, length).toString()
                length < 4 -> "0000".subSequence(0, 4 - length).toString().plus(this)
                else -> this
            }
        }
    }

}