// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
// Use of this file is governed by the BSD 3-clause license that
// can be found in the LICENSE.txt file in the project root.

package antlr

import "fmt"

/** A set of utility routines useful for all kinds of ANTLR trees. */

// TreesStringTree prints out a whole tree in LISP form. [getNodeText] is used on the
// node payloads to get the text for the nodes. Detects parse trees and extracts data appropriately.
func TreesStringTree(tree Tree, ruleNames []string, recog Recognizer) string {

	if recog != nil {
		ruleNames = recog.GetRuleNames()
	}

	s := TreesGetNodeText(tree, ruleNames, nil)

	s = EscapeWhitespace(s, false)
	c := tree.GetChildCount()
	if c == 0 {
		return s
	}
	res := "(" + s + " "
	if c > 0 {
		s = TreesStringTree(tree.GetChild(0), ruleNames, nil)
		res += s
	}
	for i := 1; i < c; i++ {
		s = TreesStringTree(tree.GetChild(i), ruleNames, nil)
		res += " " + s
	}
	res += ")"
	return res
}

func TreesGetNodeText(t Tree, ruleNames []string, recog Parser) string {
	if recog != nil {
		ruleNames = recog.GetRuleNames()
	}

	if ruleNames != nil {
		switch t2 := t.(type) {
		case RuleNode:
			t3 := t2.GetRuleContext()
			altNumber := t3.GetAltNumber()

			if altNumber != ATNInvalidAltNumber {
				return fmt.Sprintf("%s:%d", ruleNames[t3.GetRuleIndex()], altNumber)
			}
			return ruleNames[t3.GetRuleIndex()]
		case ErrorNode:
			return fmt.Sprint(t2)
		case TerminalNode:
			if t2.GetSymbol() != nil {
				return t2.GetSymbol().GetText()
			}
		}
	}

	// no recognition for rule names
	payload := t.GetPayload()
	if p2, ok := payload.(Token); ok {
		return p2.GetText()
	}

	return fmt.Sprint(t.GetPayload())
}

// TreesGetChildren returns am ordered list of all children of this node
//
//goland:noinspection GoUnusedExportedFunction
func TreesGetChildren(t Tree) []Tree {
	list := make([]Tree, 0)
	for i := 0; i < t.GetChildCount(); i++ {
		list = append(list, t.GetChild(i))
	}
	return list
}

// TreesgetAncestors returns a list of all ancestors of this node.  The first node of list is the root
// and the last node is the parent of this node.
//
//goland:noinspection GoUnusedExportedFunction
func TreesgetAncestors(t Tree) []Tree {
	ancestors := make([]Tree, 0)
	t = t.GetParent()
	for t != nil {
		f := []Tree{t}
		ancestors = append(f, ancestors...)
		t = t.GetParent()
	}
	return ancestors
}

//goland:noinspection GoUnusedExportedFunction
func TreesFindAllTokenNodes(t ParseTree, ttype int) []ParseTree {
	return TreesfindAllNodes(t, ttype, true)
}

//goland:noinspection GoUnusedExportedFunction
func TreesfindAllRuleNodes(t ParseTree, ruleIndex int) []ParseTree {
	return TreesfindAllNodes(t, ruleIndex, false)
}

func TreesfindAllNodes(t ParseTree, index int, findTokens bool) []ParseTree {
	nodes := make([]ParseTree, 0)
	treesFindAllNodes(t, index, findTokens, &nodes)
	return nodes
}

func treesFindAllNodes(t ParseTree, index int, findTokens bool, nodes *[]ParseTree) {
	// check this node (the root) first

	t2, ok := t.(TerminalNode)
	t3, ok2 := t.(ParserRuleContext)

	if findTokens && ok {
		if t2.GetSymbol().GetTokenType() == index {
			*nodes = append(*nodes, t2)
		}
	} else if !findTokens && ok2 {
		if t3.GetRuleIndex() == index {
			*nodes = append(*nodes, t3)
		}
	}
	// check children
	for i := 0; i < t.GetChildCount(); i++ {
		treesFindAllNodes(t.GetChild(i).(ParseTree), index, findTokens, nodes)
	}
}

//goland:noinspection GoUnusedExportedFunction
func TreesDescendants(t ParseTree) []ParseTree {
	nodes := []ParseTree{t}
	for i := 0; i < t.GetChildCount(); i++ {
		nodes = append(nodes, TreesDescendants(t.GetChild(i).(ParseTree))...)
	}
	return nodes
}
