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

@Intrinsic
func sizeOfType<T>(): UIntNative where T <: CType

@Intrinsic
func alignOfType<T>(): UIntNative where T <: CType

@FastNative
foreign func malloc(size: UIntNative): CPointer<Unit>

@FastNative
foreign func free(p: CPointer<Unit>): Unit

let SECUREC_MEM_MAX_LEN = Int64(Int32.Max)

public func sizeOf<T>(): UIntNative where T <: CType {
    return sizeOfType<T>()
}

public func alignOf<T>(): UIntNative where T <: CType {
    return alignOfType<T>()
}

private unsafe func freeWrapper(p: CPointer<Unit>): Unit {
    free(p)
}

public struct LibC {
    public static func malloc<T>(count!: Int64 = 1): CPointer<T> where T <: CType {
        if (count < 0) {
            throw IllegalArgumentException("The value of count cannot be negative")
        }
        let itemSize = sizeOf<T>()
        if (UIntNative.Max / itemSize < UIntNative(count)) {
            return CPointer<T>()
        }
        let size = UIntNative(count) * sizeOf<T>()
        return CPointer<T>(unsafe { malloc(size) })
    }

    public unsafe static func free<T>(p: CPointer<T>): Unit where T <: CType {
        freeWrapper(CPointer<Unit>(p))
    }

    public unsafe static func mallocCString(str: String): CString {
        var length: Int64 = str.size
        var cp: CPointer<UInt8> = LibC.malloc<UInt8>(count: length + 1)
        if (cp.isNull()) {
            throw IllegalMemoryException("Malloc in mallocCString failed.")
        }
        cp.write(length, 0)
        if (length == 0) {
            return CString(cp)
        }
        let ptrArr: CPointer<UInt8> = str.acquireRaw()
        try {
            // If the length of the string is greater than the safe length of the safe function,
            // it needs to be copied multiple times.
            var remain = length
            var offset = 0
            while (remain >= SECUREC_MEM_MAX_LEN) {
                let rc = memcpy_s(cp + offset, UIntNative(SECUREC_MEM_MAX_LEN), ptrArr + offset, UIntNative(SECUREC_MEM_MAX_LEN))
                if (rc != 0) {
                    LibC.free(cp)
                    throw IllegalMemoryException("memcpy_s in mallocCString failed with error code: ${rc}.")
                }
                offset += SECUREC_MEM_MAX_LEN
                remain -= SECUREC_MEM_MAX_LEN
            }
            if (remain > 0) {
                let rc = memcpy_s(cp + offset, UIntNative(remain), ptrArr + offset, UIntNative(remain))
                if (rc != 0) {
                    LibC.free(cp)
                    throw IllegalMemoryException("memcpy_s in mallocCString failed with error code: ${rc}.")
                }
            }
            return CString(cp)
        } finally {
            str.releaseRaw(ptrArr)
        }
    }

    public unsafe static func free(cstr: CString): Unit {
        var ptr = cstr.getChars()
        freeWrapper(CPointer<Unit>(ptr))
    }
}