/**
 * 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 hyperion.buffer

/**
 * 字节类型相关操作的接口
 *
 * @author yangfuping
 */
public interface ByteOperation {
    func getByte(): Byte

    func getByte(index: Int64): Byte

    func getUInt16(): UInt16

    func getUInt32(): UInt32

    func getUInt64(): UInt64

    func getInt8(): Int8

    func getInt16(): Int16

    func getInt32(): Int32

    func getInt64(): Int64

    func getUInt16(index: Int64): UInt16

    func getUInt32(index: Int64): UInt32

    func getUInt64(index: Int64): UInt64

    func getInt8(index: Int64): Int8

    func getInt16(index: Int64): Int16

    func getInt32(index: Int64): Int32

    func getInt64(index: Int64): Int64

    func putByte(value: Byte): Unit

    func putUInt16(val: UInt16): Unit

    func putUInt32(val: UInt32): Unit

    func putUInt64(val: UInt64): Unit

    func putInt8(val: Int8): Unit

    func putInt16(val: Int16): Unit

    func putInt32(val: Int32): Unit

    func putInt64(val: Int64): Unit

    func putByte(index: Int64, value: Byte): Unit

    func putUInt16(index: Int64, val: UInt16): Unit

    func putUInt32(index: Int64, val: UInt32): Unit

    func putUInt64(index: Int64, val: UInt64): Unit

    func putInt8(index: Int64, val: Int8): Unit

    func putInt16(index: Int64, val: Int16): Unit

    func putInt32(index: Int64, val: Int32): Unit

    func putInt64(index: Int64, val: Int64): Unit

    func expandAndPutByte(value: Byte): Unit

    func expandAndPutUInt16(val: UInt16): Unit

    func expandAndPutUInt32(val: UInt32): Unit

    func expandAndPutUInt64(val: UInt64): Unit

    func expandAndPutInt8(val: Int8): Unit

    func expandAndPutInt16(val: Int16): Unit

    func expandAndPutInt32(val: Int32): Unit

    func expandAndPutInt64(val: Int64): Unit

    func get(dst: Array<Byte>, startPos: Int64, length: Int64): Unit

    func get(dst: Array<Byte>): Unit

    func get(index: Int64, dst: Array<Byte>, startPos: Int64, length: Int64): Unit

    func get(index: Int64, dst: Array<Byte>): Unit

    func put(src: Array<Byte>, startPos: Int64, length: Int64): Unit

    func put(src: Array<Byte>): Unit

    func put(index: Int64, src: Array<Byte>, startPos: Int64, length: Int64): Unit

    func put(index: Int64, src: Array<Byte>): Unit

    func expandAndPut(src: Array<Byte>, startPos: Int64, length: Int64): Unit

    func expandAndPut(src: Array<Byte>): Unit

    /**
     * 将数据收集到目标字节数组中,收集到的字节数小于需要收集的字节数不会报错
     *
     * @param dst 目标字节数组
     * @param startPos 数组起始位置
     * @param length 需要收集的字节数
     * @return 收集到的字节数
     */
    func partialGather(index: Int64, dst: Array<Byte>, startPos: Int64, length: Int64): Int64

    /**
     * 将数据收集到目标字节数组中,收集到的字节数小于目标数组的长度不会报错
     *
     * @param index 从ByteBuffer的指定位置开始收集
     * @param dst 目标字节数组
     * @return 收集到的字节数
     */
    func partialGather(index: Int64, dst: Array<Byte>): Int64

    /**
     * 将源字节数组的数据存储到ByteBuffer中,ByteBuffe存储容量不够不会报错
     *
     * @param index 从ByteBuffer的指定位置开始存储
     * @param src 源字节数组
     * @param startPos 数组起始位置
     * @param length 需要村粗的字节数
     * @return 完成存储的字节数
     */
    func partialOffer(index: Int64, src: Array<Byte>, startPos: Int64, length: Int64): Int64

    /**
     * 将源字节数组的数据存储到ByteBuffer中,ByteBuffe存储容量不够不会报错
     *
     * @param index 从ByteBuffer的指定位置开始存储
     * @param src 源字节数组
     * @return 完成存储的字节数
     */
    func partialOffer(index: Int64, src: Array<Byte>): Int64
}

/**
 * Buffer接口
 *
 * position 游标位置
 * limit 限制游标访问的位置
 * capacity 容量
 * maxCapactiy 允许的最大容量
 *
 * 如下等式成立:
 *  0 <= position < limit <= capacity
 */
public interface Buffer <: ByteOperation {
    prop expandSupport: Bool

    func position(): Int64

    func position(position: Int64): Buffer

    func limit(): Int64

    func limit(limit: Int64): Buffer

    func capacity(): Int64

    func remainingCapacity(): Int64

    func maxCapacity(): Int64

    func remaining(): Int64

    func hasRemaining(): Bool

    /**
     * 返回position..=limit区间包含的数组
     */
    func toArray(): Array<Byte>

    /**
     * 返回index..=index+length包含的数组
     */
    func toArray(index!: Int64, length!: Int64): Array<Byte>

    func toArray(range: Range<Int64>): Array<Byte>

    /**
     * 将limit的位置设置为capacity
     */
    func limitToCapacity(): Buffer

    /**
     * 扩容容量并限制Limit位置
     */
    func expandAndLimit(limit: Int64): Buffer

    func clear(): Buffer

    func flip(): Buffer

    func rewind(): Buffer

    func slice(): Buffer

    /** 
     * 从当前的起始位置开始(positon = 0的位置),跳过skipSize
     *
     * 需要当前的position大于skipSize时,才能调整
     */
    func skip(skipSize: Int64): Buffer

    func slice(index!: Int64, length!: Int64): Buffer

    func slice(range: Range<Int64>): Buffer

    func compact(): Buffer

    /**
     * 用于池化的ByteBuffer, 保留引用
     */
    func retain(): Buffer

    /**
     * 用于池化的ByteBuffer, 释放对ByteBuffer的引用
     */
    func release(): Unit

    /**
     * 用于池化的ByteBuffer, 清理ByteBuffer的信息,归还到缓存池之前调用
     */
    func free(): Unit
}

/**
 * 允许扩容ByteBuffer的仓颉实现,参考java.nio.ByteBuffer。
 *
 *
 */
public abstract class ByteBuffer <: Buffer & ToString {
    protected static let logger = LoggerFactory.getLogger("transport")

    static let DEFAULT_INITIAL_CAPACITY = 256
    static let DEFAULT_MAX_CAPACITY = Int64(Int32.Max)

    public static func allocate() {
        let byteBuffer = ArrayByteBuffer()
        if (logger.isLoggable(LogLevel.TRACE)) {
            logger.log(LogLevel.TRACE, "Allocate ByteBuffer: ${byteBuffer}")
        }
        return byteBuffer
    }

    public static func allocate(capacity: Int64) {
        let byteBuffer = ArrayByteBuffer(capacity)
        if (logger.isLoggable(LogLevel.TRACE)) {
            logger.log(LogLevel.TRACE, "Allocate ByteBuffer: ${byteBuffer}, required capacity ${capacity}")
        }
        return byteBuffer
    }

    public static func allocate(capacity: Int64, expandSupport!: Bool) {
        let byteBuffer = ArrayByteBuffer(capacity, expandSupport: expandSupport)
        if (logger.isLoggable(LogLevel.TRACE)) {
            logger.log(LogLevel.TRACE, "Allocate ByteBuffer: ${byteBuffer}, required capacity ${capacity}")
        }
        return byteBuffer
    }

    public static func wrap(buffer: Array<Byte>) {
        if (logger.isLoggable(LogLevel.TRACE)) {
            logger.log(LogLevel.TRACE, "ByteBuffer wrap: buffer[0..${buffer.size -1}]")
        }

        let byteBuffer = ArrayByteBuffer(
            buffer,
            skipBytes: 0,
            position: 0,
            limit: buffer.size,
            capacity: buffer.size,
            maxCapacity: buffer.size,
            expandSupport: false
        )
        return byteBuffer
    }

    public static func wrap(buffer: Array<Byte>, startPos: Int64, length: Int64) {
        if (logger.isLoggable(LogLevel.TRACE)) {
            logger.log(LogLevel.TRACE, "ByteBuffer wrap: buffer[${startPos}..${startPos + length}]")
        }

        if (startPos < 0) {
            throw IllegalArgumentException(
                "Negative start position is not allowed, provided start position is ${startPos}")
        }

        if (startPos + length > buffer.size) {
            throw IndexOutOfBoundsException(
                "The sum of start position ${startPos} and length ${length} is out of bounds, array size is ${buffer.size}"
            )
        }

        let byteBuffer = ArrayByteBuffer(
            buffer,
            skipBytes: startPos,
            position: 0,
            limit: length,
            capacity: length,
            maxCapacity: buffer.size,
            expandSupport: false
        )
        return byteBuffer
    }

    /**
     * ByteBuffer的分配器,池化和回收ByteBuffer使用
     */
    private let allocator: ?ByteBufferAllocator

    /**
     * 是否支持扩展容量
     */
    private let expandSupportVal: Bool

    /**
     * 游标位置
     */
    protected var positionVal: Int64

    /**
     * 读取限制位置
     */
    protected var limitVal: Int64

    /**
     * 当前最大容量
     */
    protected var capacityVal: Int64

    /**
     * 允许扩容的最大容量
     */
    protected let maxCapacityVal: Int64

    public init(
        position!: Int64,
        limit!: Int64,
        capacity!: Int64,
        expandSupport!: Bool
    ) {
        if (expandSupport == false) {
            this.maxCapacityVal = capacity
        } else {
            this.maxCapacityVal = DEFAULT_MAX_CAPACITY
        }
        checkBufferInitParams(position: position, limit: limit, capacity: capacity, maxCapacity: maxCapacityVal)

        this.positionVal = position
        this.limitVal = limit
        this.capacityVal = capacity
        this.expandSupportVal = expandSupport
        this.allocator = None<ByteBufferAllocator>
    }

    public init(
        position!: Int64,
        limit!: Int64,
        capacity!: Int64,
        maxCapacity!: Int64,
        expandSupport!: Bool
    ) {
        checkBufferInitParams(position: position, limit: limit, capacity: capacity, maxCapacity: maxCapacity)
        this.positionVal = position
        this.limitVal = limit
        this.capacityVal = capacity
        this.expandSupportVal = expandSupport
        if (expandSupport == false) {
            this.maxCapacityVal = capacity
        } else {
            this.maxCapacityVal = maxCapacity
        }
        this.allocator = None<ByteBufferAllocator>
    }

    public init(
        position!: Int64,
        limit!: Int64,
        capacity!: Int64,
        maxCapacity!: Int64,
        expandSupport!: Bool,
        allocator!: ByteBufferAllocator
    ) {
        checkBufferInitParams(position: position, limit: limit, capacity: capacity, maxCapacity: maxCapacity)

        this.positionVal = position
        this.limitVal = limit
        this.capacityVal = capacity
        this.expandSupportVal = expandSupport
        if (expandSupport == false) {
            this.maxCapacityVal = capacity
        } else {
            this.maxCapacityVal = maxCapacity
        }
        this.allocator = allocator
    }

    public prop expandSupport: Bool {
        get() {
            return expandSupportVal
        }
    }

    public func position(): Int64 {
        return positionVal
    }

    public func position(position: Int64): Buffer {
        if (position > limitVal) {
            throw IllegalArgumentException("Out of bound position: ${position}, limit is ${limitVal}")
        }

        this.positionVal = position
        return this
    }

    public func limit(): Int64 {
        return limitVal
    }

    public func limit(limit: Int64): Buffer {
        if (limit > capacityVal) {
            throw IllegalArgumentException(
                "Out of bound new limit: ${limit}, position is ${positionVal}, current limit is ${limitVal}, capacity is ${capacityVal}"
            )
        }

        this.limitVal = limit
        return this
    }

    public func capacity(): Int64 {
        return capacityVal
    }

    public func maxCapacity(): Int64 {
        return maxCapacityVal
    }

    public func remaining(): Int64 {
        return limitVal - positionVal
    }

    public func hasRemaining(): Bool {
        return positionVal < limitVal
    }

    public func limitToCapacity(): ByteBuffer {
        this.limitVal = capacityVal
        return this
    }

    public open func expand(): ByteBuffer {
        expandAndLimit(capacityVal * 2)
        return this
    }

    public func expandAndLimit(limit: Int64): ByteBuffer {
        if (limit > capacityVal) {
            if (!this.expandSupportVal) {
                throw IllegalArgumentException(
                    "Out of bound new limit: ${limit}, position is ${positionVal}, current limit is ${limitVal}, capacity is ${capacityVal}"
                )
            }

            expandCapacity(limit)
        }

        this.limitVal = limit
        return this
    }

    public func clear(): ByteBuffer {
        positionVal = 0
        limitVal = capacityVal
        return this;
    }

    public func flip(): ByteBuffer {
        limitVal = positionVal
        positionVal = 0
        return this;
    }

    public func rewind(): ByteBuffer {
        positionVal = 0
        return this;
    }

    public func slice(): ByteBuffer

    public func slice(index!: Int64, length!: Int64): ByteBuffer

    public func slice(range: Range<Int64>): ByteBuffer

    public func getAllocator(): ?ByteBufferAllocator {
        return allocator
    }

    /**
     * 用于池化的ByteBuffer, 保留引用
     */
    public open func retain(): ByteBuffer {
        return this
    }

    public open func release(): Unit {
    }

    /**
     * 搜索指定的Byte数组的起始位置
     */
    public open func indexOf(needle: Array<Byte>): Int64 {
        if (remaining() >= needle.size) {
            for (i in positionVal..limitVal) {
                if (matches(needle, i)) {
                    return i;
                }
            }
        }

        return -1;
    }

    private func matches(needle: Array<Byte>, pos: Int64): Bool {
        for (i in 0..needle.size) {
            let compareIndex = pos + i
            if (compareIndex >= limitVal) {
                // 游标等于或超过Limit,返回false
                return false
            }

            if (getByte(compareIndex) != needle[i]) {
                return false;
            }
        }

        return true;
    }

    protected func nextGetIndex(): Int64 {
        let p = positionVal;
        if (p >= limitVal) {
            throw IndexOutOfBoundsException("Position ${p} is out of bounds, limit is ${limitVal}")
        }
        positionVal = p + 1
        return p;
    }

    protected func nextGetIndex(needBytes: Int64): Int64 {
        let p = positionVal;
        if (limitVal - p < needBytes) {
            throw IndexOutOfBoundsException(
                "The sum of position ${p} and ${needBytes} is out of bounds, limit is ${limitVal}");
        }
        positionVal = p + needBytes
        return p;
    }

    protected func nextPutIndex(): Int64 {
        let p = positionVal;
        if (p >= limitVal) {
            throw IndexOutOfBoundsException("Position ${p} is out of bounds, limit is ${limitVal}")
        }

        positionVal = p + 1
        return p;
    }

    protected func nextPutIndex(needBytes: Int64): Int64 {
        let p = positionVal;
        if (limitVal - p < needBytes) {
            throw IndexOutOfBoundsException(
                "The sum of position ${p} and ${needBytes} is out of bounds, limit is ${limitVal}");
        }
        positionVal = p + needBytes
        return p;
    }

    protected func checkIndex(i: Int64) {
        if (i < 0 || i > limitVal) {
            throw IndexOutOfBoundsException("Index ${i} is out of bounds, limit is ${limitVal}")
        }

        return i
    }

    protected func expandAndCheckIndex(i: Int64) {
        if (i < 0) {
            throw IndexOutOfBoundsException("Negative index ${i}")
        }

        if (i > capacityVal) {
            expandCapacity(i)
        }

        return i
    }

    protected func expandCapacity(needCapicity: Int64): Buffer
}