package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	"honnef.co/go/tools/pattern"
)

func match(fset *token.FileSet, pat pattern.Pattern, f *ast.File) {
	entryNodesMap := make(map[reflect.Type]struct{})
	for _, node := range pat.EntryNodes {
		entryNodesMap[reflect.TypeOf(node)] = struct{}{}
	}
	ast.Inspect(f, func(node ast.Node) bool {
		if node == nil {
			return true
		}

		if _, ok := entryNodesMap[reflect.TypeOf(node)]; ok {
			m := &pattern.Matcher{}
			if m.Match(pat, node) {
				fmt.Printf("%s: ", fset.Position(node.Pos()))
				format.Node(os.Stdout, fset, node)
				fmt.Println()
			}

			// OPT(dh): we could further speed this up by not
			// chasing down impossible subtrees. For example,
			// we'll never find an ImportSpec beneath a FuncLit.
		}
		return true
	})

}

func main() {
	flag.Parse()
	// XXX don't use MustParse, handle error
	p := &pattern.Parser{}
	q, err := p.Parse(flag.Args()[0])
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	dir := flag.Args()[1]
	// XXX should we create a new fileset per file? what if we're
	// checking millions of files, will this use up a lot of memory?
	fset := token.NewFileSet()
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			// XXX error handling
			panic(err)
		}
		if !strings.HasSuffix(path, ".go") {
			return nil
		}
		// XXX don't try to parse irregular files or directories
		f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
		if err != nil {
			// XXX log error?
			return nil
		}

		match(fset, q, f)

		return nil
	})
}
