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 i => CreateIntType(i, variableName),
            NubFloatType f => CreateFloatType(f, variableName),
            NubPointerType p => CreatePointerType(p, variableName),
            NubSliceType => "nub_slice" + (variableName != null ? $" {variableName}" : ""),
            NubStringType => "nub_string" + (variableName != null ? $" {variableName}" : ""),
            NubConstArrayType a => CreateConstArrayType(a, variableName, constArraysAsPointers),
            NubArrayType a => CreateArrayType(a, variableName),
            NubFuncType f => CreateFuncType(f, variableName),
            // NubStructType s => $"{s.Module}_{s.Name}_{NameMangler.Mangle(s)}" + (variableName != null ? $" {variableName}" : ""),
            NubStructType s => $"{s.Module}_{s.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 ? "char" : "unsigned char",
            16 => intType.Signed ? "short" : "unsigned short",
            32 => intType.Signed ? "int" : "unsigned int",
            64 => intType.Signed ? "long long" : "unsigned long long",
            _ => 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 constArraysAsPointers)
    {
        var elementType = Create(arr.ElementType);

        // Treat const arrays as pointers unless in a struct definition
        if (constArraysAsPointers)
        {
            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})";
    }
}