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

// parser.go
package expr

import (
	"errors"
)

//-------- 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) {
	args := make([]*term, 0, 10)
	itemExpected := false
	lastSym := SymUnknown
	for lastSym != SymClosedRound && lastSym != SymEos {
		var subTree *ast
		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
			break
		}
		prev := scanner.Previous()
		if subTree.root != nil {
			args = append(args, subTree.root)
		} else if itemExpected {
			err = prev.ErrorExpectedGot("function-param-value")
			break
		}

		itemExpected = prev.Sym == SymComma
		lastSym = scanner.Previous().Sym
	}
	if err == nil {
		if lastSym != SymClosedRound {
			err = errors.New("unterminated 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)
	lastSym := SymUnknown
	defaultParamsStarted := false
	itemExpected := false
	tk := scanner.Previous()
	for lastSym != SymClosedRound && lastSym != SymEos {
		tk = scanner.Next()
		if tk.IsSymbol(SymIdentifier) {
			param := newTerm(tk)
			args = append(args, param)
			tk = scanner.Next()
			if tk.Sym == SymEqual {
				var paramExpr *ast
				defaultParamsStarted = true
				if paramExpr, err = self.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
					break
				}
				param.forceChild(paramExpr.root)
			} else if defaultParamsStarted {
				err = tk.Errorf("can't mix default and non-default parameters")
				break
			}
		} else if itemExpected {
			prev := scanner.Previous()
			err = prev.ErrorExpectedGot("function-param-spec")
			break
		}
		lastSym = scanner.Previous().Sym
		itemExpected = lastSym == SymComma
	}

	if err == nil && lastSym != SymClosedRound {
		err = tk.ErrorExpectedGot(")")
	}
	if err == nil {
		tk = scanner.Next()
		if tk.IsSymbol(SymOpenBrace) {
			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
		} else {
			err = tk.ErrorExpectedGot("{")
		}
	}
	if err == nil {
		if scanner.Previous().Sym != SymClosedBrace {
			err = scanner.Previous().ErrorExpectedGot("}")
		} else {
			tk = scanner.makeValueToken(SymExpression, "", body)
			tree = newFuncDefTerm(tk, args)
		}
	}
	return
}

func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
	r, c := scanner.lastPos()
	args := make([]*term, 0)
	lastSym := SymUnknown
	itemExpected := false
	for lastSym != SymClosedSquare && lastSym != SymEos {
		var subTree *ast
		zeroRequired := scanner.current.Sym == SymColon
		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
			root := subTree.root
			if root != nil {
				if !parsingIndeces && root.symbol() == SymColon {
					err = root.Errorf("unexpected range expression")
					break
				}
				args = append(args, root)
				if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
					if len(root.children) == 1 {
						root.children = append(root.children, root.children[0])
					} else if len(root.children) > 1 {
						err = root.Errorf("invalid range specification")
						break
					}
					zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
					zeroTerm := newTerm(zeroTk)
					zeroTerm.setParent(root)
					root.children[0] = zeroTerm
				}
			} else if itemExpected {
				prev := scanner.Previous()
				err = prev.ErrorExpectedGot("list-item")
				break
			}
		} else {
			break
		}
		lastSym = scanner.Previous().Sym
		itemExpected = lastSym == SymComma
	}
	if err == nil {
		if lastSym != SymClosedSquare {
			err = scanner.Previous().ErrorExpectedGot("]")
		} else {
			subtree = newListTerm(r, c, args)
		}
	}
	return
}

func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
	tk := scanner.Previous()
	args := make([]*term, 0)
	lastSym := SymUnknown
	itemExpected := false
	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 if itemExpected {
				prev := scanner.Previous()
				err = prev.ErrorExpectedGot("iterator-param")
				break
			}
		} else {
			break
		}
		lastSym = scanner.Previous().Sym
		itemExpected = lastSym == SymComma
	}
	if err == nil {
		if lastSym != SymClosedRound {
			err = scanner.Previous().ErrorExpectedGot(")")
		} else {
			subtree = newIteratorTerm(tk, args)
		}
	}
	return
}

func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
	tk := scanner.Next()
	if tk.Sym == SymError {
		err = tk.Error()
		return
	}
	if tk.Sym == SymClosedBrace || tk.Sym == SymEos {
		return
	}
	if tk.Sym == SymInteger || tk.Sym == SymString {
		tkSep := scanner.Next()
		if tkSep.Sym != SymColon {
			err = tkSep.ErrorExpectedGot(":")
		} else {
			key = tk.Value
		}
	} else {
		err = tk.ErrorExpectedGot("dictionary-key or }")
	}
	return
}

func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
	args := make(map[any]*term, 0)
	lastSym := SymUnknown
	itemExpected := false
	for lastSym != SymClosedBrace && lastSym != SymEos {
		var subTree *ast
		var key any
		if key, err = self.parseDictKey(scanner, allowVarRef); err != nil {
			break
		} else if key == nil {
			tk := scanner.Previous()
			lastSym = tk.Sym
			if itemExpected {
				err = tk.ErrorExpectedGot("dictionary-key")
			}
			break
		}
		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
			if subTree.root != nil {
				args[key] = subTree.root
			} else if key != nil {
				prev := scanner.Previous()
				err = prev.ErrorExpectedGot("dictionary-value")
				break
			}
		} else {
			break
		}
		lastSym = scanner.Previous().Sym
		itemExpected = lastSym == SymComma
	}
	if err == nil {
		if lastSym != SymClosedBrace {
			err = scanner.Previous().ErrorExpectedGot("}")
		} else {
			subtree = newDictTerm(args)
			// subtree = newMapTerm(args)
		}
	}
	return
}

func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
	var filterList *term
	var caseExpr *ast
	tk := scanner.Next()
	startRow := tk.row
	startCol := tk.col
	if tk.Sym == SymOpenSquare {
		if defaultCase {
			err = tk.Errorf("case list in default clause")
			return
		}
		if filterList, err = self.parseList(scanner, false, allowVarRef); err != nil {
			return
		}
		tk = scanner.Next()
		startRow = tk.row
		startCol = tk.col
	} else if !defaultCase {
		filterList = newListTerm(startRow, startCol, make([]*term, 0))
	}

	if tk.Sym == SymOpenBrace {
		if caseExpr, err = self.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
			return
		}
	} else {
		err = tk.ErrorExpectedGot("{")
	}

	if err == nil {
		caseTerm = newSelectorCaseTerm(startRow, startCol, filterList, caseExpr)
	}
	return
}

func addSelectorCase(selectorTerm, caseTerm *term) {
	if len(selectorTerm.children) < 2 {
		caseListTerm := newListTermA(caseTerm)
		selectorTerm.children = append(selectorTerm.children, caseListTerm)
	} else {
		caseListTerm := selectorTerm.children[1]
		caseList, _ := caseListTerm.value().([]*term)
		caseList = append(caseList, caseTerm)
		caseListTerm.tk.Value = caseList
	}
	caseTerm.parent = selectorTerm
}

func (self *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
	var caseTerm *term
	tk := scanner.makeToken(SymSelector, '?')
	if selectorTerm, err = tree.addToken2(tk); err != nil {
		return
	}

	if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, false); err == nil {
		addSelectorCase(selectorTerm, caseTerm)
	}
	return
}

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

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

func couldBeACollection(t *term) bool {
	var sym = SymUnknown
	if t != nil {
		sym = t.symbol()
	}
	return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
}

func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
	var selectorTerm *term = nil
	var currentTerm *term = nil
	var tk *Token
	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
				currentTerm = nil
				selectorTerm = nil
				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
				err = tree.addTerm(newExprTerm(subTree.root))
				currentTerm = subTree.root
			}
		case SymFuncCall:
			var funcCallTerm *term
			if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil {
				err = tree.addTerm(funcCallTerm)
				currentTerm = funcCallTerm
			}
		case SymOpenSquare:
			var listTerm *term
			parsingIndeces := couldBeACollection(currentTerm)
			if listTerm, err = self.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
				if parsingIndeces {
					indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
					indexTerm := newTerm(indexTk)
					if err = tree.addTerm(indexTerm); err == nil {
						err = tree.addTerm(listTerm)
					}
				} else {
					err = tree.addTerm(listTerm)
				}
				currentTerm = listTerm
			}
		case SymOpenBrace:
			if currentTerm != nil && currentTerm.symbol() == SymColon {
				err = currentTerm.Errorf(`selector-case outside of a selector context`)
			} else {
				var mapTerm *term
				if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
					err = tree.addTerm(mapTerm)
					currentTerm = mapTerm
				}
			}
		case SymEqual:
			// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
			currentTerm, err = tree.addToken2(tk)
			// }
		case SymFuncDef:
			var funcDefTerm *term
			if funcDefTerm, err = self.parseFuncDef(scanner); err == nil {
				err = tree.addTerm(funcDefTerm)
				currentTerm = funcDefTerm
			}
		case SymDollarRound:
			var iterDefTerm *term
			if iterDefTerm, err = self.parseIterDef(scanner, allowVarRef); err == nil {
				err = tree.addTerm(iterDefTerm)
				currentTerm = iterDefTerm
			}
		case SymIdentifier:
			if tk.source[0] == '@' && !allowVarRef {
				err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
			} else {
				currentTerm, err = tree.addToken2(tk)
			}
		case SymQuestion:
			if selectorTerm, err = self.parseSelector(scanner, tree, allowVarRef); err == nil {
				currentTerm = selectorTerm
			}
		case SymColon, SymDoubleColon:
			var caseTerm *term
			if selectorTerm != nil {
				if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
					addSelectorCase(selectorTerm, caseTerm)
					currentTerm = caseTerm
					if tk.Sym == SymDoubleColon {
						selectorTerm = nil
					}
				}
			} else {
				currentTerm, err = tree.addToken2(tk)
			}
		default:
			currentTerm, err = tree.addToken2(tk)
		}

		if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
			selectorTerm = nil

		}
		// lastSym = tk.Sym
	}
	if err == nil {
		err = tk.Error()
	}
	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
// }