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

package stdx.syntax

public import std.ast.{Tokens, Token, TokenKind}
import std.collection.ArrayList

/*
 * The api used comes from lsp.so, which is a dynamic package.
 */
foreign func CJ_ParseAnnotationArguments(tokensBytes: CPointer<UInt8>): CPointer<ParseRes>

foreign func CJ_CheckAddSpaces(tokBytes: CPointer<UInt8>, spaceFlag: CPointer<Bool>): Unit

foreign func CJ_CheckIdentifier(str: CString, useContextualKeyWords: Bool): Bool

// ======================== PART ONE: Parse/Lex API from CJC static library ========================

/**
 * NOTE: four bytes in the front of buffer represents the size of the buffer.
 */
let BUFFER_HEADER_SIZE: Int64 = 4

/**
 * Returns Int32 value from pointer.
 */
func getInt32FromUnsafePointer(ptr: CPointer<UInt8>): Int32 {
    if (ptr.isNull()) {
        throw IllegalArgumentException("the input pointer is null.\n")
    }
    var buf = Array<UInt8>(BUFFER_HEADER_SIZE, {x: Int64 => unsafe { ptr.read(x) }})
    return getInt32(buf[0..4])
}

@C
struct ParseRes {
    let node: CPointer<UInt8> = CPointer<UInt8>()
    let errMsg: CPointer<UInt8> = CPointer<UInt8>()
}

unsafe func parse(input: Tokens, nativeParse: (CPointer<UInt8>) -> CPointer<ParseRes>): NodeFormat_Node {
    let tokensBytes: Array<UInt8> = input.toBytes()
    let pTokensBytes: CPointer<UInt8> = unsafePointerCastFromUint8Array(tokensBytes)
    if (pTokensBytes.isNull()) {
        throw Exception("empty pointer found.")
    }
    let parseResultRaw: CPointer<ParseRes> = nativeParse(pTokensBytes)
    if (parseResultRaw.isNull()) {
        LibC.free(pTokensBytes)
        throw Exception("Unable to get parseResult.")
    }

    /*
     * If not use isNull(), there will be segfault for some cases for cjnative backend.
     * isNull() API only exists for cjnative.
     */
    try {
        let pNode = parseResultRaw.read().node
        let node = genNodeFormatNode(pNode, pTokensBytes)
        return node
    } finally {
        LibC.free(parseResultRaw)
    }
}

func genNodeFormatNode(pNode: CPointer<UInt8>, pTokensBytes: CPointer<UInt8>) {
    if (pNode.isNull()) {
        unsafe { LibC.free(pTokensBytes) }
        throw IllegalArgumentException("Negative or zero length.\n")
    }
    let bufferSize: Int64 = Int64(getInt32FromUnsafePointer(pNode))
    if (bufferSize <= 0) {
        unsafe {
            LibC.free(pNode)
            LibC.free(pTokensBytes)
        }
        throw IllegalArgumentException("Negative or zero length.\n")
    }
    let flatBuf = Array<UInt8>(bufferSize, {x: Int64 => unsafe { pNode.read(x) }})
    unsafe {
        LibC.free(pNode)
        LibC.free(pTokensBytes)
    }
    return NodeFormat_Node(flatBuf[BUFFER_HEADER_SIZE..], 0)
}

// ======================== PART TWO: CPointer<UInt8>/Array casting APIs ========================
// The call of UnsafePointerCastFromUint8Array will be generated by compiler when we desugar a MacroDecl.
/**
 * @throws IllegalMemoryException while system error.
 */
func unsafePointerCastFromUint8Array(arr: Array<UInt8>): CPointer<UInt8> {
    let arrSize: Int64 = arr.size
    if (arrSize <= 0) {
        return CPointer<UInt8>()
    }
    var ptr: CPointer<UInt8> = unsafe { LibC.malloc<UInt8>(count: arrSize) }
    if (ptr.isNull()) {
        throw IllegalMemoryException("unsafePointerCastFromUint8Array malloc failed!")
    }
    for (i in 0..arrSize) {
        unsafe { ptr.write(i, arr[i]) }
    }
    return ptr
}