/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/**
 * @file
 *
 * This file implements the DataProvider class.
 */

package stdx.fuzz


/**
 * Convert the byte stream to basic cangjie type.
 *
 */
public open class FuzzDataProvider {
    public let data: Array<UInt8>
    public var remainingBytes: Int64
    public var offset: Int64

    init(data: Array<UInt8>) {
        this.data = data
        this.remainingBytes = data.size
        this.offset = 0
    }

    /**
     * Construct DataProvider with UInt8 Array.
     *
     * @param data Initial data of DataProvider.
     */
    public static func withCangjieData(data: Array<UInt8>): FuzzDataProvider {
        return FuzzDataProvider(data)
    }

    /**
     * Construct FuzzDataProvider with CPointer and length.
     * This is an unsafe function, caller should make sure the data is legal.
     *
     * @param data Pointer of initial data.
     * @param length Length of initial data.
     */
    public static unsafe func withNativeData(data: CPointer<UInt8>, length: Int64): FuzzDataProvider {
        let cangjieData: Array<UInt8> = Array(length, repeat: 0)
        let ptr: CPointerHandle<UInt8> = acquireArrayRawData(cangjieData)
        memcpy_s(ptr.pointer, UIntNative(length), data, UIntNative(length))
        releaseArrayRawData(ptr)
        return FuzzDataProvider(cangjieData)
    }

    /**
     * Consume data as bool.
     */
    public open func consumeBool(): Bool {
        return (this.consumeByteInner() & 1) == 1
    }

    /**
     * Return a Bool Array with `count` items.
     */
    public open func consumeBools(count: Int64): Array<Bool> {
        let tmp = this.consumeBytesInner(count)
        return Array(count, {i => (tmp[i] & 1) == 1})
    }

    /**
     * Consume data as byte.
     */
    public open func consumeByte(): Byte {
        return this.consumeByteInner()
    }

    /**
     * Return a Byte Array with `count` items.
     */
    public open func consumeBytes(count: Int64): Array<Byte> {
        return this.consumeBytesInner(count)
    }

    @OverflowWrapping
    func consumeByteInner(): Byte {
        if (this.remainingBytes < 1) {
            throw ExhaustedException("Remaining data is not enough, please increase -max_len=N.")
        }

        let ret = this.data[offset]
        this.offset += 1
        this.remainingBytes -= 1
        return ret
    }

    @OverflowWrapping
    func consumeBytesInner(count: Int64): Array<Byte> {
        if (count < 0) {
            throw IllegalArgumentException("Negative count.")
        }
        if (remainingBytes < count) {
            throw ExhaustedException("Remaining data is not enough, please increase -max_len=N.")
        }
        let ret = this.data[offset..offset + count]
        this.offset += count
        this.remainingBytes -= count
        return ret
    }

    @OverflowWrapping
    func consumeInteger(byteCount: Int64): UInt64 {
        let buf = this.consumeBytesInner(byteCount)
        var ret: UInt64 = 0
        var i = byteCount - 1
        while (i >= 0) {
            ret <<= 8
            ret |= UInt64(buf[i])
            i--
        }
        return ret
    }

    func consumeIntegerArray(dstPointer: CPointer<UInt8>, byteCount: Int64) {
        let buf = this.consumeBytesInner(byteCount)
        unsafe {
            let srcHandler = acquireArrayRawData(buf)
            memcpy_s(dstPointer, UIntNative(byteCount), srcHandler.pointer, UIntNative(byteCount))
            releaseArrayRawData(srcHandler)
        }
    }

    /**
     * Consume data as UInt8.
     */
    public open func consumeUInt8(): UInt8 {
        return this.consumeByteInner()
    }

    /**
     * Consume data as UInt16.
     */
    @OverflowWrapping
    public open func consumeUInt16(): UInt16 {
        return UInt16(consumeInteger(Int64(sizeOf<Int16>())))
    }

    /**
     * Consume data as UInt32.
     */
    @OverflowWrapping
    public open func consumeUInt32(): UInt32 {
        return UInt32(consumeInteger(Int64(sizeOf<Int32>())))
    }

    /**
     * Consume data as UInt64.
     */
    @OverflowWrapping
    public open func consumeUInt64(): UInt64 {
        return UInt64(consumeInteger(Int64(sizeOf<Int64>())))
    }

    /**
     * Consume data as Int8.
     */
    @OverflowWrapping
    public open func consumeInt8(): Int8 {
        return Int8(this.consumeByteInner())
    }

    /**
     * Consume data as Int16.
     */
    @OverflowWrapping
    public open func consumeInt16(): Int16 {
        return Int16(consumeInteger(Int64(sizeOf<Int16>())))
    }

    /**
     * Consume data as Int32.
     */
    @OverflowWrapping
    public open func consumeInt32(): Int32 {
        return Int32(consumeInteger(Int64(sizeOf<Int32>())))
    }

    /**
     * Consume data as Int64.
     */
    @OverflowWrapping
    public open func consumeInt64(): Int64 {
        return Int64(consumeInteger(Int64(sizeOf<Int64>())))
    }

    /**
     * Return an UInt8 Array with `count` items.
     */
    public open func consumeUInt8s(count: Int64): Array<UInt8> {
        return this.consumeBytesInner(count)
    }

    /**
     * Return an UInt16 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeUInt16s(count: Int64): Array<UInt16> {
        let ret = Array<UInt16>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int16>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an UInt32 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeUInt32s(count: Int64): Array<UInt32> {
        let ret = Array<UInt32>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int32>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an UInt64 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeUInt64s(count: Int64): Array<UInt64> {
        let ret = Array<UInt64>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int64>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an Int8 Array with `count` items.
     */
    public open func consumeInt8s(count: Int64): Array<Int8> {
        let ret = Array<Int8>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an Int16 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeInt16s(count: Int64): Array<Int16> {
        let ret = Array<Int16>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int16>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an Int32 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeInt32s(count: Int64): Array<Int32> {
        let ret = Array<Int32>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int32>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Return an Int64 Array with `count` items.
     */
    @OverflowWrapping
    public open func consumeInt64s(count: Int64): Array<Int64> {
        let ret = Array<Int64>(count, repeat: 0)
        unsafe {
            let dstHandler = acquireArrayRawData(ret)
            try {
                consumeIntegerArray(CPointer<UInt8>(dstHandler.pointer), Int64(sizeOf<Int64>()) * count)
            } finally {
                releaseArrayRawData(dstHandler)
            }
        }
        return ret
    }

    /**
     * Consume data as Float32.
     */
    public open func consumeFloat32(): Float32 {
        Float32.fromBits(UInt32(consumeInteger(Int64(sizeOf<Int32>()))))
    }

    /**
     * Consume data as Float64.
     */
    public open func consumeFloat64(): Float64 {
        Float64.fromBits(UInt64(consumeInteger(Int64(sizeOf<Int64>()))))
    }

    /**
     * Consume data as char.
     * Cangjie Rune range: [0x0000, 0xD7FF], [0xE000, 0x10FFFF].
     */
    @OverflowWrapping
    public open func consumeRune(): Rune {
        // The data in range [0x110000, 0xFFFFFFFF] will be chunked, this will
        // cause waste of random information.
        let r = UInt32(this.consumeInteger(Int64(sizeOf<Int32>()))) % (0x10FFFF + 1)

        if (0xD7FF < r && r < 0xE000) {
            // If the Rune is invalid, subtract it to [0,2048).
            return Rune(r - (0xD7FF + 1))
        } else {
            return Rune(r)
        }
    }

    /**
     * Consume data as ascii string. The length of string (in codepoint) is at most `maxLength`.
     * If remaining byte is not enough, a shorter string will be return.
     *
     * @param maxLength max count (in codepoint) of string
     */
    public open func consumeAsciiString(maxLength: Int64): String {
        if (maxLength < 0) {
            throw IllegalArgumentException("Negative count.")
        }
        let consumeCount = if (remainingBytes < maxLength) {
            remainingBytes
        } else {
            maxLength
        }
        let ret = this.consumeBytesInner(consumeCount)
        for (i in 0..consumeCount) {
            ret[i] &= 0x7F
        }
        return unsafe { String.fromUtf8Unchecked(ret) }
    }

    /**
     * Consume data as utf8 string. The length of string (in codepoint) is at most `maxLength`.
     * If remaining byte is not enough, a shorter string will be return.
     *
     * @param maxLength max count in codepoint of string
     */
    public open func consumeString(maxLength: Int64): String {
        // Cangjie Rune range: [0x0000, 0xD7FF], [0xE000, 0x10FFFF], occupying 1 to 4 UTF8.
        if (maxLength < 0) {
            throw IllegalArgumentException("Negative count.")
        }
        // We assume every codepoint cost 3 bytes on average.
        let peekCount = if (this.remainingBytes < maxLength * 3) {
            remainingBytes
        } else {
            maxLength * 3
        }

        // make a copy, because the fixup will change the original data
        let fixedUtf8: Array<UInt8> = this.data[offset..offset + peekCount].clone()
        var consumeCount = 0
        unsafe {
            let handler = acquireArrayRawData(fixedUtf8)
            consumeCount = CJ_fix_to_utf8(handler.pointer, fixedUtf8.size)
            releaseArrayRawData(handler)
        }

        this.offset += consumeCount
        this.remainingBytes -= consumeCount

        return unsafe { String.fromUtf8Unchecked(fixedUtf8[0..consumeCount]) }
    }

    /**
     * Consume all remaining data as Array<UInt8>
     */
    public open func consumeAll(): Array<UInt8> {
        return this.consumeBytesInner(this.remainingBytes)
    }

    /**
     * Consume all remaining data as ascii string.
     */
    @OverflowWrapping
    public open func consumeAllAsAscii(): String {
        let ret = this.consumeBytesInner(this.remainingBytes)
        for (i in 0..remainingBytes) {
            ret[i] &= 0x7F
        }
        return unsafe { String.fromUtf8Unchecked(ret) }
    }

    /**
     * Consume all remaining data as utf string. Some trailing bytes will not be consumed.
     */
    @OverflowWrapping
    public open func consumeAllAsString(): String {
        let ret = this.consumeBytesInner(this.remainingBytes)
        var consumeCount = 0
        unsafe {
            let handler = acquireArrayRawData(ret)
            consumeCount = CJ_fix_to_utf8(handler.pointer, ret.size)
            releaseArrayRawData(handler)
        }

        this.offset += consumeCount
        this.remainingBytes -= consumeCount

        return unsafe { String.fromUtf8Unchecked(ret[0..consumeCount]) }
    }
}

public class DebugDataProvider <: FuzzDataProvider {
    init(data: Array<UInt8>) {
        super(data)
    }

    public static func wrap(dp: FuzzDataProvider): DebugDataProvider {
        return DebugDataProvider(dp.data)
    }

    public override func consumeBool(): Bool {
        let ret = super.consumeBool()
        println("[DEBUG] consumeBool return ${ret}")
        return ret
    }

    public override func consumeBools(count: Int64): Array<Bool> {
        let ret = super.consumeBools(count)
        println("[DEBUG] consumeBools return ${ret}")
        return ret
    }

    public override func consumeByte(): Byte {
        let ret = super.consumeByte()
        println("[DEBUG] consumeByte return ${ret}")
        return ret
    }

    public override func consumeBytes(count: Int64): Array<Byte> {
        let ret = super.consumeBytes(count)
        println("[DEBUG] consumeBytes return ${ret}")
        return ret
    }

    public override func consumeUInt8(): UInt8 {
        let ret = super.consumeUInt8()
        println("[DEBUG] consumeUInt8 return ${ret}")
        return ret
    }

    public override func consumeUInt16(): UInt16 {
        let ret = super.consumeUInt16()
        println("[DEBUG] consumeUInt16 return ${ret}")
        return ret
    }

    public override func consumeUInt32(): UInt32 {
        let ret = super.consumeUInt32()
        println("[DEBUG] consumeUInt32 return ${ret}")
        return ret
    }

    public override func consumeUInt64(): UInt64 {
        let ret = super.consumeUInt64()
        println("[DEBUG] consumeUInt64 return ${ret}")
        return ret
    }

    public override func consumeInt8(): Int8 {
        let ret = super.consumeInt8()
        println("[DEBUG] consumeInt8 return ${ret}")
        return ret
    }

    public override func consumeInt16(): Int16 {
        let ret = super.consumeInt16()
        println("[DEBUG] consumeInt16 return ${ret}")
        return ret
    }

    public override func consumeInt32(): Int32 {
        let ret = super.consumeInt32()
        println("[DEBUG] consumeInt32 return ${ret}")
        return ret
    }

    public override func consumeInt64(): Int64 {
        let ret = super.consumeInt64()
        println("[DEBUG] consumeInt64 return ${ret}")
        return ret
    }

    public override func consumeUInt8s(count: Int64): Array<UInt8> {
        let ret = super.consumeUInt8s(count)
        println("[DEBUG] consumeUInt8s return ${ret}")
        return ret
    }

    public override func consumeUInt16s(count: Int64): Array<UInt16> {
        let ret = super.consumeUInt16s(count)
        println("[DEBUG] consumeUInt16s return ${ret}")
        return ret
    }

    public override func consumeUInt32s(count: Int64): Array<UInt32> {
        let ret = super.consumeUInt32s(count)
        println("[DEBUG] consumeUInt32s return ${ret}")
        return ret
    }

    public override func consumeUInt64s(count: Int64): Array<UInt64> {
        let ret = super.consumeUInt64s(count)
        println("[DEBUG] consumeUInt64s return ${ret}")
        return ret
    }

    public override func consumeInt8s(count: Int64): Array<Int8> {
        let ret = super.consumeInt8s(count)
        println("[DEBUG] consumeInt8s return ${ret}")
        return ret
    }

    public override func consumeInt16s(count: Int64): Array<Int16> {
        let ret = super.consumeInt16s(count)
        println("[DEBUG] consumeInt16s return ${ret}")
        return ret
    }

    public override func consumeInt32s(count: Int64): Array<Int32> {
        let ret = super.consumeInt32s(count)
        println("[DEBUG] consumeInt32s return ${ret}")
        return ret
    }

    public override func consumeInt64s(count: Int64): Array<Int64> {
        let ret = super.consumeInt64s(count)
        println("[DEBUG] consumeInt64s return ${ret}")
        return ret
    }

    public override func consumeFloat32(): Float32 {
        let ret = super.consumeFloat32()
        println("[DEBUG] consumeFloat32 return ${ret}")
        return ret
    }

    public override func consumeFloat64(): Float64 {
        let ret = super.consumeFloat64()
        println("[DEBUG] consumeFloat64 return ${ret}")
        return ret
    }

    public override func consumeRune(): Rune {
        let ret = super.consumeRune()
        println("[DEBUG] consumeRune return ${ret}")
        return ret
    }

    public override func consumeAsciiString(maxLength: Int64): String {
        let ret = super.consumeAsciiString(maxLength)
        println("[DEBUG] consumeAsciiString return ${ret}")
        return ret
    }

    public override func consumeString(maxLength: Int64): String {
        let ret = super.consumeString(maxLength)
        println("[DEBUG] consumeString return ${ret}")
        return ret
    }

    public override func consumeAll(): Array<UInt8> {
        let ret = super.consumeAll()
        println("[DEBUG] consumeAll return ${ret}")
        return ret
    }

    public override func consumeAllAsAscii(): String {
        let ret = super.consumeAllAsAscii()
        println("[DEBUG] consumeAllAsAscii return ${ret}")
        return ret
    }

    public override func consumeAllAsString(): String {
        let ret = super.consumeAllAsString()
        println("[DEBUG] consumeAllAsString return ${ret}")
        return ret
    }
}