/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights resvered.
 */

/**
 * @file
 * The file declars the Segment class.
 */
package io4cj

let NULL = unsafe{ zeroValue<Byte>() }
/**
 * The class is Segment inherited from Object & Equatable<Segment>
 * @author liyanqing14
 * @since 0.32.5
 */
public class Segment <: Object & Equatable<Segment> {
    static let SIZE = 8192
    static let SHARE_MINIMUM = 1024
    let data: Array<Byte>
    var pos: Int64
    var limit: Int64
    var shared: Bool
    var owner: Bool
    var next: ?Segment = Option.None
    var prev: ?Segment = Option.None

    /**
     * The Function is init constructor
     *
     * @since 0.32.5
     */
    public init() {
        this.data = Array<UInt8>(SIZE, repeat: NULL)
        this.pos = 0
        this.limit = 0
        this.shared = false
        this.owner = true
    }

    /**
     * The Function is init constructor
     *
     * @param data of Array<UInt8>
     * @param pos of Int64
     * @param limit of Int64
     * @param shared of Bool
     * @param owner of Bool
     * @since 0.32.5
     */
    public init(data: Array<UInt8>, pos: Int64, limit: Int64, shared: Bool, owner: Bool) {
        this.data = data
        this.pos = pos
        this.limit = limit
        this.shared = shared
        this.owner = owner
    }

    @Frozen
    func sharedCopy(): Segment {
        shared = true
        return Segment(this.data, this.pos, this.limit, true, false)
    }

    /**
     * The Function is push
     *
     * @param segment of Segment
     *
     * @return Type of Segment
     * @since 0.32.5
     */
    @Frozen
    public func push(segment: Segment): Segment {
        segment.prev = this
        segment.next = next
        next?.prev = segment
        next = segment
        return segment
    }

    /**
     * The Function is pop
     *
     * @return Type of ?Segment
     * @since 0.32.5
     */
    @Frozen
    public func pop(): ?Segment {
        let nextA = next.getOrThrow()
        let result: ?Segment = if (nextA != this) {
            next
        } else {
            None
        }
        prev?.next = next
        next?.prev = prev
        next = None
        prev = None
        return result
    }

    /*
     *  Split the head of the circular linked list into two sections.
     *  The first paragraph contains the data in [pos.. Pos+byteCount),
     *  The second paragraph contains the data in [pos+byteCount.. Limit],
     *  It is useful to move some segments from one buffer to another.
     */
    @Frozen
    public func split(byteCount: Int64): Segment {
        if (byteCount <= 0 || byteCount > limit - pos) {
            throw IllegalArgumentException()
        }
        var prefix: Segment
        if (byteCount >= SHARE_MINIMUM) {
            prefix = sharedCopy()
        } else {
            prefix = SegmentPool.take()
            data.copyTo(prefix.data, 0, 0, byteCount)
        }
        prefix.limit = prefix.pos + byteCount
        pos += byteCount
        prev?.push(prefix)
        return prefix
    }

    /**
     * The Function is compact
     *
     * @since 0.32.5
     */
    @Frozen
    public func compact() {
        match (prev) {
            case Some(seg) => doCompact(seg)
            case None =>
                1
                ()
        }
    }

    @Frozen
    func doCompact(prev: Segment) {
        if (!prev.owner) {
            return
        }
        let byteCount = limit - pos
        let availableByteCount = SIZE - prev.limit + (if (prev.shared) {
            0
        } else {
            prev.pos
        })
        if (byteCount > availableByteCount) {
            return
        }
        writeTo(prev, byteCount);
        pop();
        SegmentPool.recycle(this);
    }

    /**
     * The Function is writeTo
     *
     * @param sink of Segment
     * @param byteCount of Int64
     * @since 0.32.5
     */
    @Frozen
    public func writeTo(sink: Segment, byteCount: Int64) {
        if (!sink.owner) {
            throw IllegalArgumentException()
        }
        if (sink.limit + byteCount > SIZE) {
            if (sink.shared) {
                throw IllegalArgumentException()
            }
            if (sink.limit + byteCount - sink.pos > SIZE) {
                throw IllegalArgumentException()
            }
            data.copyTo(sink.data, sink.pos, 0, sink.limit - sink.pos);
            sink.limit -= sink.pos
            sink.pos = 0
        }
        data.copyTo(sink.data, pos, sink.limit, byteCount);
        sink.limit += byteCount
        pos += byteCount
    }
    /**
     * The Function is ==
     *
     * @param that of Segment
     *
     * @return Type of Bool
     * @since 0.32.5
     */
    public operator func ==(that: Segment): Bool {
        return refEq(this, that)
    }
    /**
     * The Function is !=
     *
     * @param that of Segment
     *
     * @return Type of Bool
     * @since 0.32.5
     */
    public operator func !=(that: Segment): Bool {
        return !refEq(this, that)
    }
}