package parse

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"unsafe"

	"github.com/goplus/llgo/c"
	"github.com/goplus/llgo/c/cjson"
	"github.com/goplus/llgo/c/clang"
	"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
	"github.com/goplus/llgo/chore/llcppg/ast"
	"github.com/goplus/llgo/chore/llcppg/token"
)

type FileEntry struct {
	Path string
	Doc  *ast.File
}

type Converter struct {
	Files     []*FileEntry
	FileOrder []string // todo(zzy): more efficient struct
	curLoc    ast.Location
	index     *clang.Index
	unit      *clang.TranslationUnit

	indent int // for verbose debug
}

var tagMap = map[string]ast.Tag{
	"struct": ast.Struct,
	"union":  ast.Union,
	"enum":   ast.Enum,
	"class":  ast.Class,
}

type Config struct {
	File  string
	Temp  bool
	Args  []string
	IsCpp bool
}

func NewConverter(config *clangutils.Config) (*Converter, error) {
	if debugParse {
		fmt.Fprintln(os.Stderr, "NewConverter: config")
		fmt.Fprintln(os.Stderr, "config.File", config.File)
		fmt.Fprintln(os.Stderr, "config.Args", config.Args)
		fmt.Fprintln(os.Stderr, "config.IsCpp", config.IsCpp)
		fmt.Fprintln(os.Stderr, "config.Temp", config.Temp)
	}

	index, unit, err := clangutils.CreateTranslationUnit(config)
	if err != nil {
		return nil, err
	}

	return &Converter{
		Files: make([]*FileEntry, 0),
		index: index,
		unit:  unit,
	}, nil
}

func (ct *Converter) Dispose() {
	ct.logln("Dispose")
	ct.index.Dispose()
	ct.unit.Dispose()
}

func (ct *Converter) GetTokens(cursor clang.Cursor) []*ast.Token {
	ran := cursor.Extent()
	var numTokens c.Uint
	var tokens *clang.Token
	ct.unit.Tokenize(ran, &tokens, &numTokens)
	defer ct.unit.DisposeTokens(tokens, numTokens)

	tokensSlice := unsafe.Slice(tokens, int(numTokens))

	result := make([]*ast.Token, 0, int(numTokens))
	for _, tok := range tokensSlice {
		tokStr := ct.unit.Token(tok)
		result = append(result, &ast.Token{
			Token: toToken(tok),
			Lit:   c.GoString(tokStr.CStr()),
		})
		tokStr.Dispose()
	}
	return result
}

func (ct *Converter) logBase() string {
	return strings.Repeat(" ", ct.indent)
}
func (ct *Converter) incIndent() {
	ct.indent++
}

func (ct *Converter) decIndent() {
	if ct.indent > 0 {
		ct.indent--
	}
}

func (ct *Converter) logf(format string, args ...interface{}) {
	if debugParse {
		fmt.Fprintf(os.Stderr, ct.logBase()+format, args...)
	}
}
func (ct *Converter) logln(args ...interface{}) {
	if debugParse {
		if len(args) > 0 {
			firstArg := fmt.Sprintf("%s%v", ct.logBase(), args[0])
			fmt.Fprintln(os.Stderr, append([]interface{}{firstArg}, args[1:]...)...)
		} else {
			fmt.Fprintln(os.Stderr, ct.logBase())
		}
	}
}

func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
	loc := cursor.Location()
	var file clang.File
	loc.SpellingLocation(&file, nil, nil, nil)

	filePath := toStr(file.FileName())

	if filePath == "" {
		//todo(zzy): For some built-in macros, there is no file.
		ct.curLoc = ast.Location{File: ""}
		return
	}
	ct.curLoc = ast.Location{File: filePath}
}

func (ct *Converter) GetCurFile() *ast.File {
	if ct.curLoc.File == "" {
		ct.logln("GetCurFile: NO FILE")
		return nil
	}
	// todo(zzy): more efficient
	for i, entry := range ct.Files {
		if entry.Path == ct.curLoc.File {
			ct.logln("GetCurFile: found", ct.curLoc.File)
			return ct.Files[i].Doc
		}
	}
	ct.logln("GetCurFile: Create New ast.File", ct.curLoc.File)
	newDoc := &ast.File{}
	ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc})
	return newDoc
}

func (ct *Converter) CreateDeclBase(cursor clang.Cursor) ast.DeclBase {
	base := ast.DeclBase{
		Loc:    &ct.curLoc,
		Parent: ct.BuildScopingExpr(cursor.SemanticParent()),
	}
	commentGroup, isDoc := ct.ParseCommentGroup(cursor)
	if isDoc {
		base.Doc = commentGroup
	}
	return base
}

// extracts and parses comments associated with a given Clang cursor,
// distinguishing between documentation comments and line comments.
// It uses libclang to parse only Doxygen-style comments.

// Reference for Doxygen documentation blocks: https://www.doxygen.nl/manual/docblocks.html

// The function determines whether a comment is a documentation comment or a line comment by
// comparing the range of the comment node with the range of the declaration node in the AST.

// Note: In cases where both documentation comments and line comments conceptually exist,
// only the line comment will be preserved.
func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.CommentGroup, isDoc bool) {
	rawComment := toStr(cursor.RawCommentText())
	commentGroup := &ast.CommentGroup{}
	if rawComment != "" {
		commentRange := cursor.CommentRange()
		cursorRange := cursor.Extent()
		isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart())
		commentGroup = ct.ParseComment(rawComment)
		if len(commentGroup.List) > 0 {
			return commentGroup, isDoc
		}
	}
	return nil, false
}

func (ct *Converter) ParseComment(rawComment string) *ast.CommentGroup {
	lines := strings.Split(rawComment, "\n")
	commentGroup := &ast.CommentGroup{}
	for _, line := range lines {
		commentGroup.List = append(commentGroup.List, &ast.Comment{Text: line + "\n"})
	}
	return commentGroup
}

// visit top decls (struct,class,function,enum & macro,include)
func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResult {
	ct.incIndent()
	defer ct.decIndent()

	ct.UpdateLoc(cursor)

	curFile := ct.GetCurFile()

	name := toStr(cursor.String())
	ct.logf("visitTop: Cursor: %s\n", name)

	if curFile == nil {
		return clang.ChildVisit_Continue
	}

	switch cursor.Kind {
	case clang.CursorInclusionDirective:
		include := ct.ProcessInclude(cursor)
		curFile.Includes = append(curFile.Includes, include)
		ct.logln("visitTop: ProcessInclude END ", include.Path)
	case clang.CursorMacroDefinition:
		macro := ct.ProcessMacro(cursor)
		curFile.Macros = append(curFile.Macros, macro)
		ct.logln("visitTop: ProcessMacro END ", macro.Name, "Tokens Length:", len(macro.Tokens))
	case clang.CursorEnumDecl:
		enum := ct.ProcessEnumDecl(cursor)
		curFile.Decls = append(curFile.Decls, enum)

		ct.logf("visitTop: ProcessEnumDecl END")
		if enum.Name != nil {
			ct.logln(enum.Name.Name)
		} else {
			ct.logln("ANONY")
		}

	case clang.CursorClassDecl:
		classDecl := ct.ProcessClassDecl(cursor)
		curFile.Decls = append(curFile.Decls, classDecl)
		// class havent anonymous situation
		ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name)
	case clang.CursorStructDecl:
		structDecl := ct.ProcessStructDecl(cursor)
		curFile.Decls = append(curFile.Decls, structDecl)

		ct.logf("visitTop: ProcessStructDecl END")
		if structDecl.Name != nil {
			ct.logln(structDecl.Name.Name)
		} else {
			ct.logln("ANONY")
		}
	case clang.CursorUnionDecl:
		unionDecl := ct.ProcessUnionDecl(cursor)
		curFile.Decls = append(curFile.Decls, unionDecl)

		ct.logf("visitTop: ProcessUnionDecl END")
		if unionDecl.Name != nil {
			ct.logln(unionDecl.Name.Name)
		} else {
			ct.logln("ANONY")
		}
	case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
		// Handle functions and class methods (including out-of-class method)
		// Example: void MyClass::myMethod() { ... } out-of-class method
		funcDecl := ct.ProcessFuncDecl(cursor)
		curFile.Decls = append(curFile.Decls, funcDecl)
		ct.logln("visitTop: ProcessFuncDecl END", funcDecl.Name.Name, funcDecl.MangledName, "isStatic:", funcDecl.IsStatic, "isInline:", funcDecl.IsInline)
	case clang.CursorTypedefDecl:
		typedefDecl := ct.ProcessTypeDefDecl(cursor)
		if typedefDecl == nil {
			return clang.ChildVisit_Continue
		}
		curFile.Decls = append(curFile.Decls, typedefDecl)
		ct.logln("visitTop: ProcessTypeDefDecl END", typedefDecl.Name.Name)
	case clang.CursorNamespace:
		VisitChildren(cursor, ct.visitTop)
	}
	return clang.ChildVisit_Continue
}

func (ct *Converter) Convert() ([]*FileEntry, error) {
	cursor := ct.unit.Cursor()
	// visit top decls (struct,class,function & macro,include)
	VisitChildren(cursor, ct.visitTop)
	return ct.Files, nil
}

type Visitor func(cursor, parent clang.Cursor) clang.ChildVisitResult

func VisitChildren(cursor clang.Cursor, fn Visitor) c.Uint {
	return clang.VisitChildren(cursor, func(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
		cfn := *(*Visitor)(clientData)
		return cfn(cursor, parent)
	}, unsafe.Pointer(&fn))
}

func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
	ct.incIndent()
	defer ct.decIndent()

	typeName, typeKind := getTypeDesc(t)
	ct.logln("ProcessType: TypeName:", typeName, "TypeKind:", typeKind)

	if t.Kind >= clang.TypeFirstBuiltin && t.Kind <= clang.TypeLastBuiltin {
		return ct.ProcessBuiltinType(t)
	}

	if t.Kind == clang.TypeElaborated {
		return ct.ProcessElaboratedType(t)
	}

	if t.Kind == clang.TypeTypedef {
		return ct.ProcessTypeDefType(t)
	}

	var expr ast.Expr
	switch t.Kind {
	case clang.TypePointer:
		name, kind := getTypeDesc(t.PointeeType())
		ct.logln("ProcessType: PointerType  Pointee TypeName:", name, "TypeKind:", kind)
		expr = &ast.PointerType{X: ct.ProcessType(t.PointeeType())}
	case clang.TypeLValueReference:
		name, kind := getTypeDesc(t.NonReferenceType())
		ct.logln("ProcessType: LvalueRefType  NonReference TypeName:", name, "TypeKind:", kind)
		expr = &ast.LvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
	case clang.TypeRValueReference:
		name, kind := getTypeDesc(t.NonReferenceType())
		ct.logln("ProcessType: RvalueRefType  NonReference TypeName:", name, "TypeKind:", kind)
		expr = &ast.RvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
	case clang.TypeFunctionProto, clang.TypeFunctionNoProto:
		// treating TypeFunctionNoProto as a general function without parameters
		// function type will only collect return type, params will be collected in ProcessFuncDecl
		name, kind := getTypeDesc(t)
		ct.logln("ProcessType: FunctionType  TypeName:", name, "TypeKind:", kind)
		expr = ct.ProcessFunctionType(t)
	case clang.TypeConstantArray, clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray:
		if t.Kind == clang.TypeConstantArray {
			len := (*c.Char)(c.Malloc(unsafe.Sizeof(c.Char(0)) * 20))
			c.Sprintf(len, c.Str("%lld"), t.ArraySize())
			defer c.Free(unsafe.Pointer(len))
			expr = &ast.ArrayType{
				Elt: ct.ProcessType(t.ArrayElementType()),
				Len: &ast.BasicLit{Kind: ast.IntLit, Value: c.GoString(len)},
			}
		} else if t.Kind == clang.TypeIncompleteArray {
			// incomplete array havent len expr
			expr = &ast.ArrayType{
				Elt: ct.ProcessType(t.ArrayElementType()),
			}
		}
	default:
		name, kind := getTypeDesc(t)
		ct.logln("ProcessType: Unknown Type TypeName:", name, "TypeKind:", kind)
	}
	return expr
}

// For function types, we can only obtain the parameter types, but not the parameter names.
// This is because we cannot reverse-lookup the corresponding declaration node from a function type.
// Note: For function declarations, parameter names are collected in the ProcessFuncDecl method.
func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType {
	ct.incIndent()
	defer ct.decIndent()
	typeName, typeKind := getTypeDesc(t)
	ct.logln("ProcessFunctionType: TypeName:", typeName, "TypeKind:", typeKind)
	// Note: Attempting to get the type declaration for a function type will result in CursorNoDeclFound
	// cursor := t.TypeDeclaration()
	// This would return CursorNoDeclFound
	resType := t.ResultType()

	name, kind := getTypeDesc(resType)
	ct.logln("ProcessFunctionType: ResultType TypeName:", name, "TypeKind:", kind)

	ret := ct.ProcessType(resType)
	params := &ast.FieldList{}
	numArgs := t.NumArgTypes()
	for i := 0; i < int(numArgs); i++ {
		argType := t.ArgType(c.Uint(i))
		params.List = append(params.List, &ast.Field{
			Type: ct.ProcessType(argType),
		})
	}
	if t.IsFunctionTypeVariadic() != 0 {
		params.List = append(params.List, &ast.Field{
			Type: &ast.Variadic{},
		})
	}

	return &ast.FuncType{
		Ret:    ret,
		Params: params,
	}
}

func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
	ct.incIndent()
	defer ct.decIndent()
	name, kind := getCursorDesc(cursor)
	ct.logln("ProcessTypeDefDecl: CursorName:", name, "CursorKind:", kind, "CursorTypeKind:", toStr(cursor.Type().Kind.String()))

	typ := ct.ProcessUnderlyingType(cursor)
	// For cases like: typedef struct { int x; } Name;
	// libclang incorrectly reports the anonymous structure as a named structure
	// with the same name as the typedef. Since the anonymous structure definition
	// has already been collected when processing its declaration cursor,
	// we skip this redundant typedef declaration by returning nil.
	if typ == nil {
		return nil
	}

	decl := &ast.TypedefDecl{
		DeclBase: ct.CreateDeclBase(cursor),
		Name:     &ast.Ident{Name: name},
		Type:     typ,
	}
	return decl
}

func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
	underlyingTyp := cursor.TypedefDeclUnderlyingType()

	if underlyingTyp.Kind != clang.TypeElaborated {
		ct.logln("ProcessUnderlyingType: not elaborated")
		return ct.ProcessType(underlyingTyp)
	}

	referTypeCursor := underlyingTyp.TypeDeclaration()
	if toStr(cursor.String()) == toStr(referTypeCursor.String()) && isCursorChildOf(referTypeCursor, cursor) {
		ct.logln("ProcessUnderlyingType: is self reference")
		return nil
	}

	return ct.ProcessElaboratedType(underlyingTyp)
}

// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
	ct.incIndent()
	defer ct.decIndent()
	name, kind := getCursorDesc(cursor)
	mangledName := toStr(cursor.Mangling())
	ct.logln("ProcessFuncDecl: CursorName:", name, "CursorKind:", kind)

	// function type will only collect return type
	// ProcessType can't get the field names,will collect in follows
	funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType)
	if !ok {
		ct.logln("ProcessFuncDecl: failed to process function type")
		return nil
	}
	ct.logln("ProcessFuncDecl: ProcessFieldList")
	params := ct.ProcessFieldList(cursor)
	funcType.Params = params

	// Linux has one less leading underscore than macOS, so remove one leading underscore on macOS
	if runtime.GOOS == "darwin" {
		mangledName = strings.TrimPrefix(mangledName, "_")
	}

	funcDecl := &ast.FuncDecl{
		DeclBase:    ct.CreateDeclBase(cursor),
		Name:        &ast.Ident{Name: name},
		Type:        funcType,
		MangledName: mangledName,
	}

	if cursor.IsFunctionInlined() != 0 {
		funcDecl.IsInline = true
	}

	if isMethod(cursor) {
		ct.logln("ProcessFuncDecl: is method, ProcessMethodAttributes")
		ct.ProcessMethodAttributes(cursor, funcDecl)
	} else {
		if cursor.StorageClass() == clang.SCStatic {
			funcDecl.IsStatic = true
		}
	}

	return funcDecl
}

// get Methods Attributes
func (ct *Converter) ProcessMethodAttributes(cursor clang.Cursor, fn *ast.FuncDecl) {
	if parent := cursor.SemanticParent(); parent.Equal(cursor.LexicalParent()) != 1 {
		fn.DeclBase.Parent = ct.BuildScopingExpr(cursor.SemanticParent())
	}

	switch cursor.Kind {
	case clang.CursorDestructor:
		fn.IsDestructor = true
	case clang.CursorConstructor:
		fn.IsConstructor = true
		if cursor.IsExplicit() != 0 {
			fn.IsExplicit = true
		}
	}

	if cursor.IsStatic() != 0 {
		fn.IsStatic = true
	}
	if cursor.IsVirtual() != 0 || cursor.IsPureVirtual() != 0 {
		fn.IsVirtual = true
	}
	if cursor.IsConst() != 0 {
		fn.IsConst = true
	}

	var numOverridden c.Uint
	var overridden *clang.Cursor
	cursor.OverriddenCursors(&overridden, &numOverridden)
	if numOverridden > 0 {
		fn.IsOverride = true
	}
	overridden.DisposeOverriddenCursors()
}

func (ct *Converter) ProcessEnumType(cursor clang.Cursor) *ast.EnumType {
	items := make([]*ast.EnumItem, 0)

	VisitChildren(cursor, func(cursor, parent clang.Cursor) clang.ChildVisitResult {
		if cursor.Kind == clang.CursorEnumConstantDecl {
			name := cursor.String()
			defer name.Dispose()

			val := (*c.Char)(c.Malloc(unsafe.Sizeof(c.Char(0)) * 20))
			c.Sprintf(val, c.Str("%lld"), cursor.EnumConstantDeclValue())
			defer c.Free(unsafe.Pointer(val))

			enum := &ast.EnumItem{
				Name: &ast.Ident{Name: c.GoString(name.CStr())},
				Value: &ast.BasicLit{
					Kind:  ast.IntLit,
					Value: c.GoString(val),
				},
			}
			items = append(items, enum)
		}
		return clang.ChildVisit_Continue
	})

	return &ast.EnumType{
		Items: items,
	}
}

func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl {
	cursorName, cursorKind := getCursorDesc(cursor)
	ct.logln("ProcessEnumDecl: CursorName:", cursorName, "CursorKind:", cursorKind)

	decl := &ast.EnumTypeDecl{
		DeclBase: ct.CreateDeclBase(cursor),
		Type:     ct.ProcessEnumType(cursor),
	}

	anony := cursor.IsAnonymous()
	if anony == 0 {
		decl.Name = &ast.Ident{Name: cursorName}
		ct.logln("ProcessEnumDecl: has name", cursorName)
	} else {
		ct.logln("ProcessRecordDecl: is anonymous")
	}

	return decl
}

// current only collect macro which defined in file
func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro {
	name := toStr(cursor.String())

	macro := &ast.Macro{
		Name:   name,
		Tokens: ct.GetTokens(cursor),
	}
	return macro
}

func (ct *Converter) ProcessInclude(cursor clang.Cursor) *ast.Include {
	name := toStr(cursor.String())
	return &ast.Include{Path: name}
}

func (ct *Converter) createBaseField(cursor clang.Cursor) *ast.Field {
	ct.incIndent()
	defer ct.decIndent()

	fieldName := toStr(cursor.String())

	typ := cursor.Type()
	typeName, typeKind := getTypeDesc(typ)

	ct.logf("createBaseField: ProcessType %s TypeKind: %s", typeName, typeKind)

	field := &ast.Field{
		Type: ct.ProcessType(typ),
	}

	commentGroup, isDoc := ct.ParseCommentGroup(cursor)
	if commentGroup != nil {
		if isDoc {
			field.Doc = commentGroup
		} else {
			field.Comment = commentGroup
		}
	}
	if fieldName != "" {
		field.Names = []*ast.Ident{{Name: fieldName}}
	}
	return field
}

// For Record Type(struct,union ...) & Func 's FieldList
func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList {
	ct.incIndent()
	defer ct.decIndent()

	params := &ast.FieldList{}
	ct.logln("ProcessFieldList: VisitChildren")
	VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
		switch subcsr.Kind {
		case clang.CursorParmDecl, clang.CursorFieldDecl:
			// In C language, parameter lists do not have similar parameter grouping in Go.
			// func foo(a, b int)

			// For follows struct, it will also parse to two FieldDecl
			// struct A {
			// 	int a, b;
			// };
			if subcsr.Kind == clang.CursorFieldDecl {
				ct.logln("ProcessFieldList: CursorFieldDecl")
			} else {
				ct.logln("ProcessFieldList: CursorParmDecl")
			}

			field := ct.createBaseField(subcsr)
			if subcsr.Kind == clang.CursorFieldDecl {
				field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
			}

			params.List = append(params.List, field)

		case clang.CursorVarDecl:
			if subcsr.StorageClass() == clang.SCStatic {
				// static member variable
				field := ct.createBaseField(subcsr)
				field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
				field.IsStatic = true
				params.List = append(params.List, field)
			}
		}
		return clang.ChildVisit_Continue
	})

	if (cursor.Kind == clang.CursorFunctionDecl || isMethod(cursor)) && cursor.IsVariadic() != 0 {
		params.List = append(params.List, &ast.Field{
			Type: &ast.Variadic{},
		})
	}
	return params
}

// Note:Public Method is considered
func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
	methods := make([]*ast.FuncDecl, 0)
	VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
		if isMethod(subcsr) && subcsr.CXXAccessSpecifier() == clang.CXXPublic {
			method := ct.ProcessFuncDecl(subcsr)
			if method != nil {
				methods = append(methods, method)
			}
		}
		return clang.ChildVisit_Continue
	})
	return methods
}

func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
	ct.incIndent()
	defer ct.decIndent()
	cursorName, cursorKind := getCursorDesc(cursor)
	ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind)

	decl := &ast.TypeDecl{
		DeclBase: ct.CreateDeclBase(cursor),
		Type:     ct.ProcessRecordType(cursor),
	}

	anony := cursor.IsAnonymousRecordDecl()
	if anony == 0 {
		decl.Name = &ast.Ident{Name: cursorName}
		ct.logln("ProcessRecordDecl: has name", cursorName)
	} else {
		ct.logln("ProcessRecordDecl: is anonymous")
	}

	return decl
}

func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
	return ct.ProcessRecordDecl(cursor)
}

func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
	return ct.ProcessRecordDecl(cursor)
}

func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
	cursorName, cursorKind := getCursorDesc(cursor)
	ct.logln("ProcessClassDecl: CursorName:", cursorName, "CursorKind:", cursorKind)

	// Pushing class scope before processing its type and popping after
	base := ct.CreateDeclBase(cursor)
	typ := ct.ProcessRecordType(cursor)

	decl := &ast.TypeDecl{
		DeclBase: base,
		Name:     &ast.Ident{Name: cursorName},
		Type:     typ,
	}

	return decl
}

func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
	ct.incIndent()
	defer ct.decIndent()

	cursorName, cursorKind := getCursorDesc(cursor)
	ct.logln("ProcessRecordType: CursorName:", cursorName, "CursorKind:", cursorKind)

	tag := toTag(cursor.Kind)
	ct.logln("ProcessRecordType: toTag", tag)

	ct.logln("ProcessRecordType: ProcessFieldList")
	fields := ct.ProcessFieldList(cursor)

	ct.logln("ProcessRecordType: ProcessMethods")
	methods := ct.ProcessMethods(cursor)

	return &ast.RecordType{
		Tag:     tag,
		Fields:  fields,
		Methods: methods,
	}
}

// process ElaboratedType Reference
//
// 1. Named elaborated type references:
// - Examples: struct MyStruct, union MyUnion, class MyClass, enum MyEnum
// - Handling: Constructed as TagExpr or ScopingExpr references
//
// 2. Anonymous elaborated type references:
// - Examples: struct { int x; int y; }, union { int a; float b; }
// - Handling: Retrieve their corresponding concrete types
func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
	ct.incIndent()
	defer ct.decIndent()
	typeName, typeKind := getTypeDesc(t)
	ct.logln("ProcessElaboratedType: TypeName:", typeName, "TypeKind:", typeKind)

	decl := t.TypeDeclaration()

	if decl.IsAnonymous() != 0 {
		// anonymous type refer (except anonymous RecordType&EnumType in TypedefDecl)
		if decl.Kind == clang.CursorEnumDecl {
			return ct.ProcessEnumType(decl)
		}
		return ct.ProcessRecordType(decl)
	}

	// for elaborated type, it could have a tag description
	// like struct A, union B, class C, enum D
	parts := strings.SplitN(typeName, " ", 2)
	if len(parts) == 2 {
		if tagValue, ok := tagMap[parts[0]]; ok {
			return &ast.TagExpr{
				Tag:  tagValue,
				Name: ct.BuildScopingExpr(decl),
			}
		}
	}

	return ct.BuildScopingExpr(decl)
}

func (ct *Converter) ProcessTypeDefType(t clang.Type) ast.Expr {
	cursor := t.TypeDeclaration()
	ct.logln("ProcessTypeDefType: Typedef TypeDeclaration", toStr(cursor.String()), toStr(t.String()))
	if name := toStr(cursor.String()); name != "" {
		return &ast.Ident{Name: name}
	}
	ct.logln("ProcessTypeDefType: typedef type have no name")
	return nil
}

func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType {
	ct.incIndent()
	defer ct.decIndent()
	typeName, typeKind := getTypeDesc(t)
	ct.logln("ProcessBuiltinType: TypeName:", typeName, "TypeKind:", typeKind)

	kind := ast.Void
	var flags ast.TypeFlag

	switch t.Kind {
	case clang.TypeVoid:
		kind = ast.Void
	case clang.TypeBool:
		kind = ast.Bool
	case clang.TypeCharU, clang.TypeUChar, clang.TypeCharS, clang.TypeSChar:
		kind = ast.Char
	case clang.TypeChar16:
		kind = ast.Char16
	case clang.TypeChar32:
		kind = ast.Char32
	case clang.TypeWChar:
		kind = ast.WChar
	case clang.TypeShort, clang.TypeUShort:
		kind = ast.Int
		flags |= ast.Short
	case clang.TypeInt, clang.TypeUInt:
		kind = ast.Int
	case clang.TypeLong, clang.TypeULong:
		kind = ast.Int
		flags |= ast.Long
	case clang.TypeLongLong, clang.TypeULongLong:
		kind = ast.Int
		flags |= ast.LongLong
	case clang.TypeInt128, clang.TypeUInt128:
		kind = ast.Int128
	case clang.TypeFloat:
		kind = ast.Float
	case clang.TypeHalf, clang.TypeFloat16:
		kind = ast.Float16
	case clang.TypeDouble:
		kind = ast.Float
		flags |= ast.Double
	case clang.TypeLongDouble:
		kind = ast.Float
		flags |= ast.Long | ast.Double
	case clang.TypeFloat128:
		kind = ast.Float128
	case clang.TypeComplex:
		kind = ast.Complex
		complexKind := t.ElementType().Kind
		if complexKind == clang.TypeLongDouble {
			flags |= ast.Long | ast.Double
		} else if complexKind == clang.TypeDouble {
			flags |= ast.Double
		}
		// float complfex flag is not set
	default:
		// like IBM128,NullPtr,Accum
		kindStr := toStr(t.Kind.String())
		fmt.Fprintln(os.Stderr, "todo: unknown builtin type:", kindStr)
	}

	if IsExplicitSigned(t) {
		flags |= ast.Signed
	} else if IsExplicitUnsigned(t) {
		flags |= ast.Unsigned
	}

	return &ast.BuiltinType{
		Kind:  kind,
		Flags: flags,
	}
}

// Constructs a complete scoping expression by traversing the semantic parents, starting from the given clang.Cursor
// For anonymous decl of typedef references, use their anonymous name
func (ct *Converter) BuildScopingExpr(cursor clang.Cursor) ast.Expr {
	parts := clangutils.BuildScopingParts(cursor)
	return buildScopingFromParts(parts)
}

func (ct *Converter) MarshalASTFiles() *cjson.JSON {
	return MarshalASTFiles(ct.Files)
}

func (ct *Converter) MarshalOutputASTFiles() *cjson.JSON {
	return MarshalOutputASTFiles(ct.Files)
}

func IsExplicitSigned(t clang.Type) bool {
	return t.Kind == clang.TypeCharS || t.Kind == clang.TypeSChar
}

func IsExplicitUnsigned(t clang.Type) bool {
	return t.Kind == clang.TypeCharU || t.Kind == clang.TypeUChar ||
		t.Kind == clang.TypeUShort || t.Kind == clang.TypeUInt ||
		t.Kind == clang.TypeULong || t.Kind == clang.TypeULongLong ||
		t.Kind == clang.TypeUInt128
}

func toTag(kind clang.CursorKind) ast.Tag {
	switch kind {
	case clang.CursorStructDecl:
		return ast.Struct
	case clang.CursorUnionDecl:
		return ast.Union
	case clang.CursorClassDecl:
		return ast.Class
	default:
		panic(fmt.Sprintf("Unexpected cursor kind in toTag: %v", kind))
	}
}

func toToken(tok clang.Token) token.Token {
	if tok.Kind() < clang.Punctuation || tok.Kind() > clang.Comment {
		return token.ILLEGAL
	} else {
		return token.Token(tok.Kind() + 1)
	}
}
func isMethod(cursor clang.Cursor) bool {
	return cursor.Kind == clang.CursorCXXMethod || cursor.Kind == clang.CursorConstructor || cursor.Kind == clang.CursorDestructor
}

func buildScopingFromParts(parts []string) ast.Expr {
	if len(parts) == 0 {
		return nil
	}

	var expr ast.Expr = &ast.Ident{Name: parts[0]}
	for _, part := range parts[1:] {
		expr = &ast.ScopingExpr{
			Parent: expr,
			X:      &ast.Ident{Name: part},
		}
	}
	return expr
}

// isCursorChildOf checks if the child cursor is contained within the parent cursor.
// This function is necessary because libclang doesn't correctly report the lexical
// or semantic parent for anonymous structs inside typedefs. By comparing source ranges,
// we can determine if one cursor is nested inside another.
func isCursorChildOf(child, parent clang.Cursor) bool {
	return isRangeChildOf(child.Extent(), parent.Extent())
}

func isRangeChildOf(childRange, parentRange clang.SourceRange) bool {
	return getOffset(childRange.RangeStart()) >= getOffset(parentRange.RangeStart()) &&
		getOffset(childRange.RangeEnd()) <= getOffset(parentRange.RangeEnd())
}

func getOffset(location clang.SourceLocation) c.Uint {
	var offset c.Uint
	location.SpellingLocation(nil, nil, nil, &offset)
	return offset
}

func toStr(clangStr clang.String) (str string) {
	defer clangStr.Dispose()
	if clangStr.CStr() != nil {
		str = c.GoString(clangStr.CStr())
	}
	return
}

func getTypeDesc(t clang.Type) (name string, kind string) {
	name = toStr(t.String())
	kind = toStr(t.Kind.String())
	return
}

func getCursorDesc(cursor clang.Cursor) (name string, kind string) {
	name = toStr(cursor.String())
	kind = toStr(cursor.Kind.String())
	return
}
