/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 */
package cbor4cj

import std.io.IOException
import std.io.InputStream
import std.math.numeric.BigInt

public abstract class AbstractDecoder<T> {
    protected static let INFINITY = -1

    protected let inputStream: InputStream

    protected let decoder: ?CborDecoder

    public init(decoder: ?CborDecoder, inputStream: InputStream) {
        this.decoder = decoder
        this.inputStream = inputStream
    }

    public func decode(initialByte: Int32): T

    protected func nextSymbol(): Int32 {
        try {
            var arr = Array<UInt8>(1, repeat: 0)
            var readsize = inputStream.read(arr)
            let symbol = Int32(arr[0])
            if (readsize == 0) {
                throw IOException("Unexpected end of stream")
            }
            return Int32(symbol)
        } catch (ioException: IOException) {
            throw CborException(ioException)
        }
    }

    protected func getLength(initialByte: Int32): Int64 {
        match (AdditionalInformation.ofByte(initialByte).getValue()) {
            case 0 => return Int64(initialByte & 31)
            case 24 => return Int64(nextSymbol())
            case 25 =>
                var twoByteValue = 0
                twoByteValue |= Int64((nextSymbol() << 8))
                twoByteValue |= Int64((nextSymbol() << 0))
                return twoByteValue
            case 26 =>
                var fourByteValue = 0
                fourByteValue |= (Int64(nextSymbol()) << 24)
                fourByteValue |= (Int64(nextSymbol()) << 16)
                fourByteValue |= (Int64(nextSymbol()) << 8)
                fourByteValue |= (Int64(nextSymbol()) << 0)
                return fourByteValue
            case 27 =>
                var eightByteValue = 0
                eightByteValue |= (Int64(nextSymbol()) << 56)
                eightByteValue |= (Int64(nextSymbol()) << 48)
                eightByteValue |= (Int64(nextSymbol()) << 40)
                eightByteValue |= (Int64(nextSymbol()) << 32)
                eightByteValue |= (Int64(nextSymbol()) << 24)
                eightByteValue |= (Int64(nextSymbol()) << 16)
                eightByteValue |= (Int64(nextSymbol()) << 8)
                eightByteValue |= (Int64(nextSymbol()) << 0)
                return eightByteValue
            case 31 => return Int64(INFINITY)
            case 28 => throw CborException("Reserved additional information")
            case _ => throw CborException("Reserved additional information")
        }
    }

    protected func getLengthAsBigInteger(initialByte: Int32): BigInt {
        match (AdditionalInformation.ofByte(initialByte).getValue()) {
            case 0 => return BigInt(Int64(initialByte & 31))
            case 24 => return BigInt(Int64(nextSymbol()))
            case 25 =>
                var twoByteValue = 0
                twoByteValue |= Int64((nextSymbol() << 8))
                twoByteValue |= Int64((nextSymbol() << 0))
                return BigInt(twoByteValue)
            case 26 =>
                var fourByteValue = 0
                fourByteValue |= (Int64(nextSymbol()) << 24)
                fourByteValue |= (Int64(nextSymbol()) << 16)
                fourByteValue |= (Int64(nextSymbol()) << 8)
                fourByteValue |= (Int64(nextSymbol()) << 0)
                return BigInt(fourByteValue)
            case 27 =>
                var eightByteValue = BigInt(0)
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (56))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (48))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (40))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (32))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (24))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (16))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (8))
                eightByteValue = eightByteValue | (BigInt(Int64(nextSymbol())) << (0))
                return eightByteValue
            case 31 => return BigInt(Int64(INFINITY))
            case 28 => throw CborException("Reserved additional information")
            case _ => throw CborException("Reserved additional information")
        }
    }
}