using NubLang.Ast;

namespace NubLang.Generation;

public static class CType
{
    public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true)
    {
        return type switch
        {
            NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""),
            NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""),
            NubIntType intType => CreateIntType(intType, variableName),
            NubFloatType floatType => CreateFloatType(floatType, variableName),
            NubCStringType => "char*" + (variableName != null ? $" {variableName}" : ""),
            NubPointerType ptr => CreatePointerType(ptr, variableName),
            NubSliceType => "slice" + (variableName != null ? $" {variableName}" : ""),
            NubStringType => "string" + (variableName != null ? $" {variableName}" : ""),
            NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers),
            NubArrayType arr => CreateArrayType(arr, variableName),
            NubFuncType fn => CreateFuncType(fn, variableName),
            NubStructType st => $"{st.Module}_{st.Name}" + (variableName != null ? $" {variableName}" : ""),
            _ => throw new NotSupportedException($"C type generation not supported for: {type}")
        };
    }

    private static string CreateIntType(NubIntType intType, string? varName)
    {
        var cType = intType.Width switch
        {
            8 => intType.Signed ? "int8_t" : "uint8_t",
            16 => intType.Signed ? "int16_t" : "uint16_t",
            32 => intType.Signed ? "int32_t" : "uint32_t",
            64 => intType.Signed ? "int64_t" : "uint64_t",
            _ => throw new NotSupportedException($"Unsupported integer width: {intType.Width}")
        };
        return cType + (varName != null ? $" {varName}" : "");
    }

    private static string CreateFloatType(NubFloatType floatType, string? varName)
    {
        var cType = floatType.Width switch
        {
            32 => "float",
            64 => "double",
            _ => throw new NotSupportedException($"Unsupported float width: {floatType.Width}")
        };
        return cType + (varName != null ? $" {varName}" : "");
    }

    private static string CreatePointerType(NubPointerType ptr, string? varName)
    {
        var baseType = Create(ptr.BaseType);
        return baseType + "*" + (varName != null ? $" {varName}" : "");
    }

    private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef)
    {
        var elementType = Create(arr.ElementType);

        // Treat const arrays as pointers unless in a struct definition
        if (!inStructDef)
        {
            return elementType + "*" + (varName != null ? $" {varName}" : "");
        }

        if (varName != null)
        {
            return $"{elementType} {varName}[{arr.Size}]";
        }

        return $"{elementType}[{arr.Size}]";
    }

    private static string CreateArrayType(NubArrayType arr, string? varName)
    {
        var elementType = Create(arr.ElementType);
        return elementType + "*" + (varName != null ? $" {varName}" : "");
    }

    private static string CreateFuncType(NubFuncType fn, string? varName)
    {
        var returnType = Create(fn.ReturnType);
        var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p)));

        if (string.IsNullOrEmpty(parameters))
        {
            parameters = "void";
        }

        if (varName != null)
        {
            return $"{returnType} (*{varName})({parameters})";
        }

        return $"{returnType} (*)({parameters})";
    }
}