// Package ast parses the clang AST output into AST structures.
package ast

import (
	"fmt"
	"strconv"
	"strings"

	"git.wow.st/gmp/nswrap/util"
)

var (
	TrackPositions bool = false
	Debug               = false
)

// Node represents any node in the AST.
type Node interface {
	Address() Address
	Children() []Node
	AddChild(node Node)
	Position() Position
}

// Address contains the memory address (originally outputted as a hexadecimal
// string) from the clang AST. The address are not predictable between run and
// are only useful for identifying nodes in a single AST.
//
// The Address is used like a primary key when storing the tree as a flat
// structure.
type Address uint64

// ParseAddress returns the integer representation of the hexadecimal address
// (like 0x7f8a1d8ccfd0). If the address cannot be parsed, 0 is returned.
func ParseAddress(address string) Address {
	if !TrackPositions {
		return 0
	}
	addr, _ := strconv.ParseUint(address, 0, 64)

	return Address(addr)
}

// Parse takes the coloured output of the clang AST command and returns a root
// node for the AST.
func Parse(fullline string) Node {
	line := fullline

	// This is a special case. I'm not sure if it's a bug in the clang AST
	// dumper. It should have children.
	if line == "array filler" {
		return parseArrayFiller(line)
	}

	parts := strings.SplitN(line, " ", 2)
	nodeName := parts[0]

	if nodeName == "super" || nodeName == "getter" { // special ObjC case
		parts = strings.SplitN(parts[1], " ", 2)
		nodeName += " " + parts[0]
	}
	// skip node name
	if len(parts) > 1 {
		line = parts[1]
	}

	switch nodeName {
	case "AlignedAttr":
		return parseAlignedAttr(line)
	case "AllocSizeAttr":
		return parseAllocSizeAttr(line)
	case "AlwaysInlineAttr":
		return parseAlwaysInlineAttr(line)
	case "ArraySubscriptExpr":
		return parseArraySubscriptExpr(line)
	case "ArcWeakrefUnavailableAttr":
		return parseArcWeakrefUnavailableAttr(line)
	case "AsmLabelAttr":
		return parseAsmLabelAttr(line)
	case "AttributedType":
		return parseAttributedType(line)
	case "AvailabilityAttr":
		return parseAvailabilityAttr(line)
	case "BinaryOperator":
		return parseBinaryOperator(line)
	case "BlockCommandComment":
		return parseBlockCommandComment(line)
	case "BlockPointerType":
		return parseBlockPointerType(line)
	case "BreakStmt":
		return parseBreakStmt(line)
	case "BuiltinType":
		return parseBuiltinType(line)
	case "CallExpr":
		return parseCallExpr(line)
	case "ConvertVectorExpr":
		return parseConvertVectorExpr(line)
	case "CaseStmt":
		return parseCaseStmt(line)
	case "CFAuditedTransferAttr":
		return parseCFAuditedTransferAttr(line)
	case "CFConsumedAttr":
		return parseCFConsumedAttr(line)
	case "CFReturnsRetainedAttr":
		return parseCFReturnsRetainedAttr(line)
	case "CFReturnsNotRetainedAttr":
		return parseCFReturnsNotRetainedAttr(line)
	case "CharacterLiteral":
		return parseCharacterLiteral(line)
	case "CompoundLiteralExpr":
		return parseCompoundLiteralExpr(line)
	case "CompoundStmt":
		return parseCompoundStmt(line)
	case "ConditionalOperator":
		return parseConditionalOperator(line)
	case "ConstAttr":
		return parseConstAttr(line)
	case "ConstantArrayType":
		return parseConstantArrayType(line)
	case "ContinueStmt":
		return parseContinueStmt(line)
	case "CompoundAssignOperator":
		return parseCompoundAssignOperator(line)
	case "CStyleCastExpr":
		return parseCStyleCastExpr(line)
	case "DeclRefExpr":
		return parseDeclRefExpr(line)
	case "DeclStmt":
		return parseDeclStmt(line)
	case "DefaultStmt":
		return parseDefaultStmt(line)
	case "DeprecatedAttr":
		return parseDeprecatedAttr(line)
	case "DisableTailCallsAttr":
		return parseDisableTailCallsAttr(line)
	case "DoStmt":
		return parseDoStmt(line)
	case "ElaboratedType":
		return parseElaboratedType(line)
	case "EmptyDecl":
		return parseEmptyDecl(line)
	case "Enum":
		return parseEnum(line)
	case "EnumConstantDecl":
		return parseEnumConstantDecl(line)
	case "EnumDecl":
		return parseEnumDecl(line)
	case "EnumExtensibilityAttr":
		return parseEnumExtensibilityAttr(line)
	case "EnumType":
		return parseEnumType(line)
	case "Field":
		return parseField(line)
	case "FieldDecl":
		return parseFieldDecl(line)
	case "FlagEnumAttr":
		return parseFlagEnumAttr(line)
	case "FloatingLiteral":
		return parseFloatingLiteral(line)
	case "FormatAttr":
		return parseFormatAttr(line)
	case "FormatArgAttr":
		return parseFormatArgAttr(line)
	case "FunctionDecl":
		return parseFunctionDecl(line)
	case "FullComment":
		return parseFullComment(line)
	case "FunctionProtoType":
		return parseFunctionProtoType(line)
	case "ForStmt":
		return parseForStmt(line)
	case "HTMLStartTagComment":
		return parseHTMLStartTagComment(line)
	case "HTMLEndTagComment":
		return parseHTMLEndTagComment(line)
	case "GCCAsmStmt":
		return parseGCCAsmStmt(line)
	case "GotoStmt":
		return parseGotoStmt(line)
	case "IBActionAttr":
		return parseIBActionAttr(line)
	case "IBOutletAttr":
		return parseIBOutletAttr(line)
	case "IfStmt":
		return parseIfStmt(line)
	case "ImplicitCastExpr":
		return parseImplicitCastExpr(line)
	case "ImplicitValueInitExpr":
		return parseImplicitValueInitExpr(line)
	case "IncompleteArrayType":
		return parseIncompleteArrayType(line)
	case "IndirectFieldDecl":
		return parseIndirectFieldDecl(line)
	case "InitListExpr":
		return parseInitListExpr(line)
	case "InlineCommandComment":
		return parseInlineCommandComment(line)
	case "IntegerLiteral":
		return parseIntegerLiteral(line)
	case "LabelStmt":
		return parseLabelStmt(line)
	case "MallocAttr":
		return parseMallocAttr(line)
	case "MaxFieldAlignmentAttr":
		return parseMaxFieldAlignmentAttr(line)
	case "MayAliasAttr":
		return parseMayAliasAttr(line)
	case "MemberExpr":
		return parseMemberExpr(line)
	case "MinVectorWidthAttr":
		return parseMinVectorWidthAttr(line)
	case "ModeAttr":
		return parseModeAttr(line)
	case "NoDebugAttr":
		return parseNoDebugAttr(line)
	case "NoEscapeAttr":
		return parseNoEscapeAttr(line)
	case "NoInlineAttr":
		return parseNoInlineAttr(line)
	case "NoThrowAttr":
		return parseNoThrowAttr(line)
	case "NonNullAttr":
		return parseNonNullAttr(line)
	case "NotTailCalledAttr":
		return parseNotTailCalledAttr(line)
	case "NSConsumedAttr":
		return parseNSConsumedAttr(line)
	case "NSConsumesSelfAttr":
		return parseNSConsumesSelfAttr(line)
	case "NSErrorDomainAttr":
		return parseNSErrorDomainAttr(line)
	case "NSReturnsRetainedAttr":
		return parseNSReturnsRetainedAttr(line)
	case "ObjCBoolLiteralExpr":
		return parseObjCBoolLiteralExpr(line)
	case "ObjCBoxableAttr":
		return parseObjCBoxableAttr(line)
	case "ObjCBridgeAttr":
		return parseObjCBridgeAttr(line)
	case "ObjCBridgeRelatedAttr":
		return parseObjCBridgeRelatedAttr(line)
	case "ObjCBridgeMutableAttr":
		return parseObjCBridgeMutableAttr(line)
	case "ObjCCategoryDecl":
		return parseObjCCategoryDecl(line)
	case "ObjCDesignatedInitializerAttr":
		return parseObjCDesignatedInitializerAttr(line)
	case "ObjCExceptionAttr":
		return parseObjCExceptionAttr(line)
	case "ObjCExplicitProtocolImplAttr":
		return parseObjCExplicitProtocolImplAttr(line)
	case "ObjCIndependentClassAttr":
		return parseObjCIndependentClassAttr(line)
	case "ObjCInterface":
		return parseObjCInterface(line, false)
	case "super ObjCInterface":
		return parseObjCInterface(line, true)
	case "ObjCInterfaceDecl":
		return parseObjCInterfaceDecl(line)
	case "ObjCInterfaceType":
		return parseObjCInterfaceType(line)
	case "ObjCIvarDecl":
		return parseObjCIvarDecl(line)
	case "getter ObjCMethod":
		return parseObjCMethod(line)
	case "ObjCMethod":
		return parseObjCMethod(line)
	case "ObjCMessageExpr":
		return parseObjCMessageExpr(line)
	case "ObjCMethodDecl":
		return parseObjCMethodDecl(line)
	case "ObjCObjectType":
		return parseObjCObjectType(line)
	case "ObjCObjectPointerType":
		return parseObjCObjectPointerType(line)
	case "ObjCProtocol":
		return parseObjCProtocol(line)
	case "ObjCReturnsInnerPointerAttr":
		return parseObjCReturnsInnerPointerAttr(line)
	case "ObjCRequiresSuperAttr":
		return parseObjCRequiresSuperAttr(line)
	case "ObjCProtocolDecl":
		return parseObjCProtocolDecl(line)
	case "ObjCRootClassAttr":
		return parseObjCRootClassAttr(line)
	case "ObjCPropertyDecl":
		return parseObjCPropertyDecl(line)
	case "ObjCTypeParamDecl":
		return parseObjCTypeParamDecl(line)
	case "OffsetOfExpr":
		return parseOffsetOfExpr(line)
	case "PackedAttr":
		return parsePackedAttr(line)
	case "ParagraphComment":
		return parseParagraphComment(line)
	case "ParamCommandComment":
		return parseParamCommandComment(line)
	case "ParenExpr":
		return parseParenExpr(line)
	case "ParenType":
		return parseParenType(line)
	case "ParmVarDecl":
		return parseParmVarDecl(line)
	case "PointerType":
		return parsePointerType(line)
	case "DecayedType":
		return parseDecayedType(line)
	case "PredefinedExpr":
		return parsePredefinedExpr(line)
	case "PureAttr":
		return parsePureAttr(line)
	case "QualType":
		return parseQualType(line)
	case "Record":
		return parseRecord(line)
	case "RecordDecl":
		return parseRecordDecl(line)
	case "RecordType":
		return parseRecordType(line)
	case "RestrictAttr":
		return parseRestrictAttr(line)
	case "ReturnStmt":
		return parseReturnStmt(line)
	case "ReturnsTwiceAttr":
		return parseReturnsTwiceAttr(line)
	case "SentinelAttr":
		return parseSentinelAttr(line)
	case "ShuffleVectorExpr":
		return parseShuffleVectorExpr(line)
	case "StmtExpr":
		return parseStmtExpr(line)
	case "StringLiteral":
		return parseStringLiteral(line)
	case "SwiftBridgedTypedefAttr":
		return parseSwiftBridgedTypedefAttr(line)
	case "SwiftErrorAttr":
		return parseSwiftErrorAttr(line)
	case "SwiftNameAttr":
		return parseSwiftNameAttr(line)
	case "SwiftNewtypeAttr":
		return parseSwiftNewtypeAttr(line)
	case "SwiftPrivateAttr":
		return parseSwiftPrivateAttr(line)
	case "SwitchStmt":
		return parseSwitchStmt(line)
	case "TargetAttr":
		return parseTargetAttr(line)
	case "TextComment":
		return parseTextComment(line)
	case "TranslationUnitDecl":
		return parseTranslationUnitDecl(line)
	case "TransparentUnionAttr":
		return parseTransparentUnionAttr(line)
	case "Typedef":
		return parseTypedef(line)
	case "TypedefDecl":
		return parseTypedefDecl(line)
	case "TypedefType":
		return parseTypedefType(line)
	case "UnaryExprOrTypeTraitExpr":
		return parseUnaryExprOrTypeTraitExpr(line)
	case "UnaryOperator":
		return parseUnaryOperator(line)
	case "UnavailableAttr":
		return parseUnavailableAttr(line)
	case "UsedAttr":
		return parseUsedAttr(line)
	case "UnusedAttr":
		return parseUnusedAttr(line)
	case "VAArgExpr":
		return parseVAArgExpr(line)
	case "VarDecl":
		return parseVarDecl(line)
	case "VectorType":
		return parseVectorType(line)
	case "VerbatimBlockComment":
		return parseVerbatimBlockComment(line)
	case "VerbatimBlockLineComment":
		return parseVerbatimBlockLineComment(line)
	case "VerbatimLineComment":
		return parseVerbatimLineComment(line)
	case "VisibilityAttr":
		return parseVisibilityAttr(line)
	case "WarnUnusedResultAttr":
		return parseWarnUnusedResultAttr(line)
	case "WeakAttr":
		return parseWeakAttr(line)
	case "WeakImportAttr":
		return parseWeakImportAttr(line)
	case "WhileStmt":
		return parseWhileStmt(line)
	case "...":
		return parseVariadic(line)
	case "NullStmt":
		return nil
	default:
		return parseUnknown(nodeName, line)
	}
}

func groupsFromRegex(rx, line string) map[string]string {
	// We remove tabs and newlines from the regex. This is purely cosmetic,
	// as the regex input can be quite long and it's nice for the caller to
	// be able to format it in a more readable way.
	fullRegexp := "^(?P<address>[0-9a-fx]+) " +
		strings.Replace(strings.Replace(rx, "\n", "", -1), "\t", "", -1)
	rx = fullRegexp + "[\\s]*$"

	re := util.GetRegex(rx)
	match := re.FindStringSubmatch(line)
	if len(match) == 0 {
		if Debug {
			fmt.Printf("AST parser: could not match regexp with string. Regexp:\n" + rx + "\nOriginal line:\n" + line + "\n\n")
		}
		return nil
	}

	result := make(map[string]string)
	for i, name := range re.SubexpNames() {
		if i != 0 {
			result[name] = match[i]
		}
	}

	return result
}
