// This file has been generated by structbuf. Do not modify it!
import * as base from '~/common/network/structbuf/base.js';
import type * as types from '~/common/types.js';
import * as utils from '../utils.js';

/**
 * ## Transport
 *
 * These structs are helpful for network transports.
 */

/**
 * A fixed-length frame for stream-like transports.
 */
export interface FrameLike {
    /**
     * Length of the `data` field.
     */
    readonly length: types.u32;

    /**
     * The data.
     */
    readonly data: Uint8Array;
}

/**
 * Encodable of {@link FrameLike}.
 */
interface FrameEncodable_ {
    /**
     * 'data' field value or encoder. See {@link FrameLike#data} for
     * the field's description.
     */
    readonly data: Uint8Array | types.ByteLengthEncoder;
}

/**
 * New-type for FrameEncodable.
 */
export type FrameEncodable = types.WeakOpaque<
    FrameEncodable_,
    {readonly FrameEncodable: unique symbol}
>;

/** @inheritdoc */
export class Frame extends base.Struct implements FrameLike {
    private readonly _array: Uint8Array;
    private readonly _view: DataView;

    /**
     * Create a Frame from an array for accessing properties.
     *
     * Note: When accessing, attributes will be decoded on-the-fly which may be expensive.
     */
    private constructor(array: Uint8Array) {
        super();
        this._array = array;
        this._view = new DataView(array.buffer, array.byteOffset, array.byteLength);
    }

    /**
     * Decode a frame struct from an array.
     *
     * @param array Array to decode from.
     * @returns Frame instance.
     */
    public static decode(array: Uint8Array): Frame {
        return new Frame(array);
    }

    /**
     * Encode a frame struct into an array.
     *
     * @param struct FrameEncodable to encode.
     * @param array Array to encode into.
     * @returns A subarray of array containing the encoded struct.
     */
    public static encode(
        struct: types.EncoderPick<FrameEncodable, 'encode'>,
        array: Uint8Array,
    ): Uint8Array {
        const view = new DataView(array.buffer, array.byteOffset, array.byteLength);
        let offset = 4;

        // Encode `data`
        const lengthValue = utils.encodeBytes(struct.data, array, offset);
        offset += lengthValue;

        // Encode `length`
        view.setUint32(0, lengthValue, true);

        return array.subarray(0, offset);
    }

    /**
     * Get the amount of bytes that would be written when encoding a frame struct into an
     * array.
     *
     * @param struct FrameEncodable to encode.
     * @returns The amount of bytes that would be required to encode the struct.
     */
    public static byteLength(struct: types.EncoderPick<FrameEncodable, 'byteLength'>): types.u53 {
        let offset = 4;
        offset += utils.getByteLength(struct.data);
        return offset;
    }

    /**
     * 'length' field accessor. See {@link FrameLike#length} for the
     * field's description.
     */
    public get length(): types.u32 {
        return this._view.getUint32(0, true);
    }

    /**
     * 'data' field accessor. See {@link FrameLike#data} for the
     * field's description.
     */
    public get data(): Uint8Array {
        const offset = 4;
        return this._array.subarray(offset, offset + this.length);
    }

    /**
     * Create a snapshot of FrameLike.
     *
     * Note: This is **not** a deep-copy, so byte arrays will still be views of the underlying
     *       buffer.
     *
     * @returns FrameLike snapshot.
     */
    public snapshot(): FrameLike {
        return {
            length: this.length,
            data: this.data,
        };
    }

    /**
     * Create a clone of FrameLike.
     *
     * Note: This is a deep-copy that will copy the underlying buffer.
     *
     * @returns FrameLike clone.
     */
    public clone(): Frame {
        const array = new Uint8Array(this._array.byteLength);
        array.set(this._array);
        return new Frame(array);
    }
}
