// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// parser.go
package expr

import (
	"errors"
	"fmt"
)

//-------- parser

type parser struct {
	ctx exprContext
}

func NewParser(ctx exprContext) (p *parser) {
	p = &parser{
		ctx: ctx,
	}
	return p
}

func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
	// name, _ := tk.Value.(string)
	// funcObj := self.ctx.GetFuncInfo(name)
	// if funcObj == nil {
	// 	err = fmt.Errorf("unknown function %s()", name)
	// 	return
	// }
	// maxArgs := funcObj.MaxArgs()
	// if maxArgs < 0 {
	// 	maxArgs = funcObj.MinArgs() + 10
	// }
	// args := make([]*term, 0, maxArgs)
	args := make([]*term, 0, 10)
	lastSym := SymUnknown
	for lastSym != SymClosedRound && lastSym != SymEos {
		var subTree *ast
		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
			if subTree.root != nil {
				args = append(args, subTree.root)
			}
		} else {
			break
		}
		lastSym = scanner.Previous().Sym
	}
	if err == nil {
		// TODO Check arguments
		if lastSym != SymClosedRound {
			err = errors.New("unterminate arguments list")
		} else {
			tree = newFuncCallTerm(tk, args)
		}
	}
	return
}

func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
	// Example: "add = func(x,y) {x+y}
	var body *ast
	args := make([]*term, 0)
	tk := scanner.Next()
	for tk.Sym != SymClosedRound && tk.Sym != SymEos {
		if tk.Sym == SymIdentifier {
			t := newTerm(tk, nil)
			args = append(args, t)
		} else {
			err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
			break
		}
		tk = scanner.Next()
	}
	if err == nil && tk.Sym != SymClosedRound {
		err = tk.Errorf("unterminate function params list")
	}
	if err == nil {
		tk = scanner.Next()
		if tk.Sym == SymOpenBrace {
			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
		}
	}
	if err == nil {
		// TODO Check arguments
		if scanner.Previous().Sym != SymClosedBrace {
			err = scanner.Previous().Errorf("not properly terminated function body")
		} else {
			tk = scanner.makeValueToken(SymExpression, "", body)
			tree = newFuncDefTerm(tk, args)
		}
	}
	return
}

func (self *parser) parseList(scanner *scanner, allowVarRef bool) (tree *term, err error) {
	args := make([]*term, 0)
	lastSym := SymUnknown
	for lastSym != SymClosedSquare && lastSym != SymEos {
		var subTree *ast
		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
			if subTree.root != nil {
				args = append(args, subTree.root)
			}
		} else {
			break
		}
		lastSym = scanner.Previous().Sym
	}
	if err == nil {
		// TODO Check arguments
		if lastSym != SymClosedSquare {
			err = scanner.Previous().Errorf("unterminate items list")
		} else {
			tree = newListTerm(args)
		}
	}
	return
}

func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
	return self.parseGeneral(scanner, true, false, termSymbols...)
}

func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
	return self.parseGeneral(scanner, false, allowVarRef, termSymbols...)
}

func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
	tree = NewAst()
	firstToken := true
	lastSym := SymUnknown
	for tk := scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = scanner.Next() {
		if tk.Sym == SymComment {
			continue
		}

		if tk.Sym == SymSemiColon {
			if allowForest {
				tree.ToForest()
				firstToken = true
				continue
			} else {
				err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source)
				break
			}
		}

		//fmt.Println("Token:", tk)
		if firstToken {
			if tk.Sym == SymMinus {
				tk.Sym = SymChangeSign
			} else if tk.Sym == SymPlus {
				tk.Sym = SymUnchangeSign
			}
			firstToken = false
		}

		switch tk.Sym {
		case SymOpenRound:
			var subTree *ast
			if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
				subTree.root.priority = priValue
				tree.addTerm(subTree.root)
			}
		case SymFuncCall:
			var funcCallTerm *term
			if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil {
				err = tree.addTerm(funcCallTerm)
			}
		case SymOpenSquare:
			var listTerm *term
			if listTerm, err = self.parseList(scanner, allowVarRef); err == nil {
				err = tree.addTerm(listTerm)
			}
		case SymEqual:
			if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
				err = tree.addToken(tk)
			}
		case SymFuncDef:
			var funcDefTerm *term
			if funcDefTerm, err = self.parseFuncDef(scanner); err == nil {
				err = tree.addTerm(funcDefTerm)
			}
		case SymIdentifier:
			if tk.source[0] == '@' && !allowVarRef {
				err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
			} else {
				err = tree.addToken(tk)
			}
		default:
			err = tree.addToken(tk)
		}
		lastSym = tk.Sym
	}
	return
}

func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
	if lastSym != wantedSym {
		err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
	}
	return
}