package org.openrs2.util.charset

import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.Charset
import java.nio.charset.CharsetDecoder
import java.nio.charset.CharsetEncoder
import java.nio.charset.CoderResult

public object Cp1252Charset : Charset("Cp1252", null) {
    private val CODE_PAGE = charArrayOf(
        '\u20AC', '\u0000', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021',
        '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u0000', '\u017D', '\u0000',
        '\u0000', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014',
        '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u0000', '\u017E', '\u0178'
    )
    private val ENCODE_TABLE = ByteArray(65536)
    private val DECODE_TABLE = CharArray(256)
    private const val REPLACEMENT_CHAR = '\uFFFD'
    private const val REPLACEMENT_BYTE = '?'.code.toByte()

    init {
        for (b in 0 until 256) {
            val c = if (b in 0x80 until 0xA0) {
                CODE_PAGE[b and 0x7F]
            } else {
                b.toChar()
            }

            if (c != '\u0000') {
                ENCODE_TABLE[c.code] = b.toByte()
                DECODE_TABLE[b] = c
            }
        }
    }

    public fun decode(byte: Byte): Char {
        val char = DECODE_TABLE[byte.toInt() and 0xFF]
        return if (char == '\u0000') {
            REPLACEMENT_CHAR
        } else {
            char
        }
    }

    public fun encode(char: Char): Byte {
        val byte = ENCODE_TABLE[char.code]
        return if (byte.toInt() == 0) {
            REPLACEMENT_BYTE
        } else {
            byte
        }
    }

    override fun contains(cs: Charset): Boolean {
        return Charsets.US_ASCII.contains(cs) || cs is Cp1252Charset
    }

    override fun newEncoder(): CharsetEncoder {
        return object : CharsetEncoder(this, 1F, 1F, byteArrayOf(REPLACEMENT_BYTE)) {
            override fun encodeLoop(input: CharBuffer, output: ByteBuffer): CoderResult {
                while (input.hasRemaining()) {
                    if (!output.hasRemaining()) {
                        return CoderResult.OVERFLOW
                    }

                    val char = input.get()
                    val byte = ENCODE_TABLE[char.code]

                    if (byte.toInt() == 0) {
                        input.position(input.position() - 1)
                        return CoderResult.unmappableForLength(1)
                    }

                    output.put(byte)
                }

                return CoderResult.UNDERFLOW
            }
        }
    }

    override fun newDecoder(): CharsetDecoder {
        return object : CharsetDecoder(this, 1F, 1F) {
            init {
                replaceWith(REPLACEMENT_CHAR.toString())
            }

            override fun decodeLoop(input: ByteBuffer, output: CharBuffer): CoderResult {
                while (input.hasRemaining()) {
                    if (!output.hasRemaining()) {
                        return CoderResult.OVERFLOW
                    }

                    val byte = input.get()
                    val char = DECODE_TABLE[byte.toInt() and 0xFF]

                    if (char == '\u0000') {
                        input.position(input.position() - 1)
                        return CoderResult.unmappableForLength(1)
                    }

                    output.put(char)
                }

                return CoderResult.UNDERFLOW
            }
        }
    }
}
