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

// token.go
package scan

import (
	"fmt"
	"io"
	"slices"

	"git.portale-stac.it/go-pkg/expr/sym"
)

type Token struct {
	row    int
	col    int
	Sym    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 s //fmt.Sprintf("%q", s)
		} else {
			return fmt.Sprintf("%v", tk.Value)
		}
	}
	return tk.source
}

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

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

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

func (tk *Token) Source() string {
	return tk.source
}

func (tk *Token) Row() int {
	return tk.row
}

func (tk *Token) Col() int {
	return tk.col
}

func (tk *Token) Clone() (c *Token) {
	return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
}

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

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

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

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

func (tk *Token) IsOneOfA(symbols ...sym.Symbol) bool {
	return slices.Index(symbols, tk.Sym) >= 0
}

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

func (tk *Token) SetSymbol(symbol sym.Symbol) {
	tk.Sym = symbol
}

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

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

func (tk *Token) ErrorText() (err string) {
	if tk.Sym == sym.SymError {
		if msg, ok := tk.Value.(error); ok {
			err = msg.Error()
		}
	}
	return
}

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

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

func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
	return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
}

func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
	err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
	return
}

func (tk *Token) ErrorExpectedOneOfGot(expected ...sym.Symbol) (err error) {
	return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym))
}
