/**
 * Copyright 2024 Beijing Baolande Software Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Runtime Library Exception to the Apache 2.0 License:
 *
 * As an exception, if you use this Software to compile your source code and
 * portions of this Software are embedded into the binary product as a result,
 * you may redistribute such product without providing attribution as would
 * otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
 */
package activemq4cj.client.openwire

public class OpenWireFormat <: WireFormat & Equatable<OpenWireFormat> & Hashable & ToString {
    public static let DEFAULT_LEGACY_VERSION: Int32 = Int32(CommandTypes.PROTOCOL_LEGACY_STORE_VERSION)
    //注册多个版本的MarshallerFactory
    private static let marshallerFactoryRegistry: HashMap<Int32, OpenWireMarshallerFactory> = HashMap<Int32, OpenWireMarshallerFactory>(
    )

    static init() {
        marshallerFactoryRegistry.add(12, MarshallerFactory())
    }

    public static let NULL_TYPE: Byte = CommandTypes.NULL
    //默认的缓存数量
    private static let MARSHAL_CACHE_SIZE: Int32 = Int32(Int16.Max / 2)

    private let context: OpenWireContext

    private var preferedWireFormatInfo: ?WireFormatInfo = None

    public init() {
        this(OpenWireContext.DEFAULT_WIRE_VERSION)
    }

    public init(version: Int32) {
        if (let Some(factory) <- marshallerFactoryRegistry.get(version)) {
            this.context = OpenWireContext(version, factory.createMarshallerMap())
        } else {
            throw ActiveMQException("This version is not supported: ${version}")
        }
    }

    /**
     * wireformatInfo协商
     */
    public func renegotiateWireFormat(info: WireFormatInfo) {
        if (let Some(preferedWireFormatInfo) <- preferedWireFormatInfo) {
            //协商版本号
            this.context.version = getMin(info.version, preferedWireFormatInfo.version)
            info.version = this.context.version
            if (let Some(factory) <- marshallerFactoryRegistry.get(this.context.version)) {
                this.context.dataMarshallers = factory.createMarshallerMap()
            } else {
                throw ActiveMQException("This version is not supported: ${this.context.version}")
            }

            this.context.maxFrameSize = getMin(preferedWireFormatInfo.getMaxFrameSize(), info.getMaxFrameSize())
            info.setMaxFrameSize(this.context.maxFrameSize)

            this.context.stackTraceEnabled = info.isStackTraceEnabled() && preferedWireFormatInfo.isStackTraceEnabled()
            info.setStackTraceEnabled(this.context.stackTraceEnabled)

            this.context.tcpNoDelayEnabled = info.isTcpNoDelayEnabled() && preferedWireFormatInfo.isTcpNoDelayEnabled()
            info.setTcpNoDelayEnabled(this.context.tcpNoDelayEnabled)

            this.context.cacheEnabled = info.isCacheEnabled() && preferedWireFormatInfo.isCacheEnabled()
            info.setCacheEnabled(this.context.cacheEnabled)

            this.context.tightEncodingEnabled = info.isTightEncodingEnabled() &&
                    preferedWireFormatInfo.isTightEncodingEnabled()
            info.setTightEncodingEnabled(this.context.tightEncodingEnabled)

            this.context.sizePrefixDisabled = info.isSizePrefixDisabled() &&
                    preferedWireFormatInfo.isSizePrefixDisabled()
            info.setSizePrefixDisabled(this.context.sizePrefixDisabled)

            if (this.context.cacheEnabled) {
                var size: Int32 = min(preferedWireFormatInfo.getCacheSize(), info.getCacheSize())
                info.setCacheSize(size)

                if (size == 0) {
                    size = MARSHAL_CACHE_SIZE
                }

                //初始化缓存
                this.context.enableCache(size)
            }
            return
        }
        throw CJMSIllegalStateException("Wireformat cannot be renegotiated.")
    }

    public func setPreferedWireFormatInfo(info: WireFormatInfo): Unit {
        this.preferedWireFormatInfo = info
    }

    public func getPreferedWireFormatInfo(): ?WireFormatInfo {
        return this.preferedWireFormatInfo
    }

    public operator override func ==(other: OpenWireFormat): Bool {
        if (this.context != other.context) {
            return false
        }
        return true
    }

    public operator override func !=(other: OpenWireFormat): Bool {
        if (this.context != other.context) {
            return true
        }
        return false
    }

    @OverflowWrapping
    public override func hashCode(): Int64 {
        return this.context.hashCode()
    }

    /**
     * Stream based marshaling
     */
    public func marshal(command: DataStructure, dataOut: ByteBuffer): Unit {
        if (context.cacheEnabled) {
            context.cacheManager.runMarshallCacheEvictionSweep()
        }

        let buffer = ByteBuffer.allocate(8192, expandSupport: true)
        let dsm = getDataStreamMarshaller(command.getDataStructureType())
        if (this.context.tightEncodingEnabled) {
            let commandByteBuffer = ByteBuffer.allocate(8192, expandSupport: true)
            let bs = BoolStream()
            //Tight Encoding
            dsm.tightMarshal(command, commandByteBuffer, bs)
            commandByteBuffer.flip()
            buffer.putByte(command.getDataStructureType())
            bs.marshal(buffer)
            MarshallingSupport.putByteArray(commandByteBuffer.toArray(), buffer)
        } else {
            dsm.looseMarshal(command, buffer)
        }
        buffer.flip()
        let content = buffer.toArray()
        let size = content.size
        if (this.context.maxFrameSizeEnabled && size > this.context.maxFrameSize) {
            throw TransportException("Frame size of ${size} larger than max allowed ${this.context.maxFrameSize}")
        }
        //sizePrefixDisabled被禁用,添加size前缀
        if (!this.context.sizePrefixDisabled) {
            dataOut.putInt32(Int32(size))
        }
        MarshallingSupport.putByteArray(content, dataOut)
        dataOut.flip()
    }

    /**
     * 数据包解码
     */
    public func unmarshal(dataIn: ByteBuffer): DataStructure {
        if (!this.context.sizePrefixDisabled) {
            //读取四字节长度
            let size = dataIn.getInt32()
            if (this.context.maxFrameSizeEnabled && Int64(size) > this.context.maxFrameSize) {
                throw TransportException("Frame size of ${size} larger than max allowed ${this.context.maxFrameSize}")
            }
        }
        //读取命令类型,调用对应的命令编解码器进行编解码
        let commandType = dataIn.getByte()
        let dsm = getDataStreamMarshaller(commandType)
        let data: DataStructure = dsm.createObject()

        if (this.context.tightEncodingEnabled) {
            let bs: BoolStream = BoolStream()
            bs.unmarshal(dataIn)
            dsm.tightUnmarshal(data, dataIn, bs)
        } else {
            dsm.looseUnmarshal(data, dataIn)
        }
        return data
    }

    public func setMaxFrameSize(maxFrameSize: Int64): Unit {
        this.context.maxFrameSize = maxFrameSize
    }

    public func setMaxFrameSizeEnabled(maxFrameSizeEnabled: Bool): Unit {
        this.context.maxFrameSizeEnabled = maxFrameSizeEnabled
    }

    public func getDataStreamMarshaller(commandType: UInt8): DataStreamMarshaller {
        return this.context.getDataStreamMarshaller(commandType)
    }

    /**
     * 是否包含完整的消息报文
     *
     * @param buffer
     * @param status
     */
    public func messageCompleted(buffer: ByteBuffer, status: MessageCompletedStatus): Unit {
        let orginalPos = buffer.position()
        let orginalLim = buffer.limit()
        try {
            buffer.flip()
            if (buffer.limit() < 4) {
                return
            }
            if (!this.context.sizePrefixDisabled) {
                let size = Int64(buffer.getInt32())
                if (buffer.position() + size <= buffer.limit()) {
                    status.completed = true
                }

                if (size != -1) {
                    status.messageSize = size + 4
                }
            }
        } finally {
            buffer.position(orginalPos)
            buffer.limit(orginalLim)
        }
    }

    private func getMin(value1: Int32, value2: Int32): Int32 {
        if (value1 < value2 && value1 > 0 || value2 <= 0) {
            return value1
        }
        return value2
    }

    private func getMin(value1: Int64, value2: Int64): Int64 {
        if (value1 < value2 && value1 > 0 || value2 <= 0) {
            return value1
        }
        return value2
    }

    public override func toString(): String {
        return "OpenWireFormat{context: ${this.context}}"
    }
}