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

// token.go
package expr

import (
	"fmt"
	"io"
	"slices"
)

type Token struct {
	row    int
	col    int
	Sym    Symbol
	source string
	Value  any
}

func (tk *Token) DevString() string {
	if tk.Value != nil {
		return fmt.Sprintf("[%d]%q{%v}", tk.Sym, tk.source, tk.Value)
	}
	return fmt.Sprintf("[%d]%q{}", tk.Sym, tk.source)
}

func (tk *Token) String() string {
	if tk.Value != nil {
		if s, ok := tk.Value.(string); ok {
			return fmt.Sprintf("%q", s)
		} else {
			return fmt.Sprintf("%v", tk.Value)
		}
	}
	return fmt.Sprintf("%s", tk.source)
}

func NewToken(row, col int, sym Symbol, source string) *Token {
	return &Token{row: row, col: col, Sym: sym, source: source}
}

func NewValueToken(row, col int, sym Symbol, source string, value any) *Token {
	return &Token{row: row, col: col, Sym: sym, source: source, Value: value}
}

func NewErrorToken(row, col int, err error) *Token {
	if err == io.EOF {
		return NewToken(row, col, SymEos, "<EOF>")
	}
	return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
}

func (tk *Token) IsEos() bool {
	return tk.Sym == SymEos
}

func (tk *Token) IsError() bool {
	return tk.Sym == SymError
}

func (tk *Token) IsTerm(termSymbols []Symbol) bool {
	return tk.IsEos() || tk.IsError() || tk.IsOneOf(termSymbols)
}

func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
	return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
}

func (tk *Token) IsSymbol(sym Symbol) bool {
	return tk.Sym == sym
}

func (self *Token) Errorf(template string, args ...any) (err error) {
	err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...)
	return
}

func (self *Token) Error() (err error) {
	if self.Sym == SymError {
		if msg, ok := self.Value.(error); ok {
			err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
		}
	}
	return
}

func (self *Token) Errors(msg string) (err error) {
	err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
	return
}

func (self *Token) ErrorExpectedGot(symbol string) (err error) {
	err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self)
	return
}

func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) {
	err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got)
	return
}
