package io.nexilis.service.core

import android.util.Log
import java.util.concurrent.ConcurrentHashMap

data class Data(
    var type: String = "",
    var version: String = "1.0.111",
    var code: String = "",
    var status: String = "",
    var f_pin: String = "",
    var l_pin: String = "",
    var bodies: MutableMap<String, String> = mutableMapOf(),
    var media: ByteArray = ByteArray(0)
) {

    private val tag: String = "Data"

    private val charHeader = 0x01.toChar()
    private val charEntry = 0x02.toChar()
    private val charKeyVal = 0x03.toChar()
    private val charArray = 0x04.toChar()

    val stringHeader = charHeader.toString()
    val stringEntry = charEntry.toString()
    val stringKeyVal = charKeyVal.toString()
    val stringArray = charArray.toString()

    fun parse(string: String): Boolean {
        var result = false
        try {
            string.split(stringHeader, ignoreCase = false, limit = 8).also {
                if (it.size == 8) {
                    type = it[0]
                    version = it[1]
                    code = it[2]
                    status = it[3]
                    f_pin = it[4]
                    l_pin = it[5]
                    bodies = parseBodies(it[6])
                    media = it[7].toByteArray(Charsets.ISO_8859_1)
                    result = true
                }
            }
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
        return result
    }

    fun parse(byteArray: ByteArray): Boolean {
        var result = false
        try {
            val data = parseHeader(byteArray)
            data.split(stringHeader, ignoreCase = false, limit = 7).also {
                if (it.size == 7) {
                    type = it[0]
                    version = it[1]
                    code = it[2]
                    status = it[3]
                    f_pin = it[4]
                    l_pin = it[5]
                    bodies = parseBodies(it[6])
                }
                media = byteArray.drop(data.length + 1).toByteArray()
                result = true
            }

        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
        return result
    }

    private fun parseHeader(byteArray: ByteArray): String {
        var string = ""
        try {
            var index = 0
            for (element in byteArray) {
                val c = element.toInt().toChar()
                if (c == charHeader) {
                    if (++index == 7) {
                        break
                    }
                }
                string += c
            }
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
        return string
    }

    private fun parseBodies(string: String): ConcurrentHashMap<String, String> {
        val concurrentHashMap: ConcurrentHashMap<String, String> = ConcurrentHashMap()
        try {
            string.split(stringEntry).also {
                for (i in it) {
                    val keyVal = i.split(stringKeyVal, ignoreCase = false, limit = 2)
                    if (keyVal.size == 2) {
                        concurrentHashMap[keyVal[0]] = keyVal[1]
                    }
                }
            }
        } catch (e: Exception) {
            Log.e(tag, e.message, e)
        }
        return concurrentHashMap
    }

    private fun toBodies(): String {
        var result = ""
        for (m in bodies.entries) {
            result += m.key + stringKeyVal + m.value + stringEntry
        }
        return result
    }

    override fun toString(): String {
        return type + stringHeader + version + stringHeader + code + stringHeader + status + stringHeader + f_pin + stringHeader + l_pin + stringHeader + toBodies() + stringHeader + String(
            media,
            Charsets.ISO_8859_1
        )
    }

    fun toByteArray(): ByteArray {
        return (type + stringHeader + version + stringHeader + code + stringHeader + status + stringHeader + f_pin + stringHeader + l_pin + stringHeader + toBodies() + stringHeader).toByteArray() + media
    }

    fun isOk(): Boolean {
        return (bodies["A97"] ?: "") == "00"
    }

    var packetId: String = ""

    fun responseOk(): Data {
        bodies.clear()
        bodies["A97"] = "00"
        media = ByteArray(0)
        return this
    }

    fun responseNok(): Data {
        bodies.clear()
        bodies["A97"] = "10"
        media = ByteArray(0)
        return this
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Data

        if (type != other.type) return false
        if (version != other.version) return false
        if (code != other.code) return false
        if (status != other.status) return false
        if (f_pin != other.f_pin) return false
        if (l_pin != other.l_pin) return false
        if (bodies != other.bodies) return false
        if (!media.contentEquals(other.media)) return false

        return true
    }

    override fun hashCode(): Int {
        var result = type.hashCode()
        result = 31 * result + version.hashCode()
        result = 31 * result + code.hashCode()
        result = 31 * result + status.hashCode()
        result = 31 * result + f_pin.hashCode()
        result = 31 * result + l_pin.hashCode()
        result = 31 * result + bodies.hashCode()
        result = 31 * result + media.contentHashCode()
        return result
    }
}