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

// Symbol.go
package expr

import (
	"strings"
)

var symbolMap map[Symbol]symbolSpec

type symbolClass int16

const (
	symClassOperator symbolClass = iota
	symClassPostOp
	symClassIdentifier
	symClassDelimiter
	symClassParenthesis
	symClassDeclaration
	symClassValue
	symClassOther
)

type symbolSpec struct {
	repr string
	kind symbolClass
}

func init() {

	symbolMap = map[Symbol]symbolSpec{
		SymUnknown:         {"<unknown>", symClassOther}, //  -1: Unknown symbol
		SymNone:            {"<null>", symClassOther},    //   0: Null value for variable of type symbol
		SymError:           {"<error>", symClassOther},   //   1: Error reading from stream
		SymEos:             {"<eos>", symClassOther},     //   2: End of stream
		SymMinus:           {"-", symClassOperator},      //   3: '-'
		SymMinusEqual:      {"-=", symClassOperator},     //   4: '-='
		SymDoubleMinus:     {"--", symClassOperator},     //   5: '--'
		SymPlus:            {"+", symClassOperator},      //   6: '+'
		SymPlusEqual:       {"+=", symClassOperator},     //   7: '+='
		SymDoublePlus:      {"++", symClassOperator},     //   8: '++'
		SymStar:            {"*", symClassOperator},      //   9: '*'
		SymDoubleStar:      {"**", symClassOperator},     //  10: '**'
		SymSlash:           {"/", symClassOperator},      //  11: '/'
		SymBackSlash:       {"\\", symClassOperator},     //  12: '\'
		SymVertBar:         {"|", symClassOperator},      //  13: '|'
		SymDoubleVertBar:   {"||", symClassOperator},     //  14: '||'
		SymComma:           {",", symClassOperator},      //  15: ','
		SymColon:           {":", symClassOperator},      //  16: ':'
		SymSemiColon:       {";", symClassOperator},      //  17: ';'
		SymDot:             {".", symClassOperator},      //  18: '.'
		SymDotSlash:        {"./", symClassOperator},     //  19: './'
		SymQuote:           {"'", symClassDelimiter},     //  20: '\''
		SymDoubleQuote:     {"\"", symClassDelimiter},    //  21: '"'
		SymBackTick:        {"`", symClassOperator},      //  22: '`'
		SymExclamation:     {"!", symClassPostOp},        //  23: '!'
		SymQuestion:        {"?", symClassOperator},      //  24: '?'
		SymAmpersand:       {"&", symClassOperator},      //  25: '&'
		SymDoubleAmpersand: {"&&", symClassOperator},     //  26: '&&'
		SymPercent:         {"%", symClassOperator},      //  27: '%'
		SymAt:              {"@", symClassOperator},      //  28: '@'
		SymUndescore:       {"_", symClassOperator},      //  29: '_'
		SymEqual:           {"=", symClassOperator},      //  30: '='
		SymDoubleEqual:     {"==", symClassOperator},     //  31: '=='
		SymLess:            {"<", symClassOperator},      //  32: '<'
		SymLessOrEqual:     {"<=", symClassOperator},     //  33: '<='
		SymGreater:         {">", symClassOperator},      //  34: '>'
		SymGreaterOrEqual:  {">=", symClassOperator},     //  35: '>='
		SymLessGreater:     {"<>", symClassOperator},     //  36: '<>'
		SymNotEqual:        {"!=", symClassOperator},     //  37: '!='
		SymDollar:          {"$", symClassOperator},      //  38: '$'
		SymHash:            {"#", symClassOperator},      //  39: '#'
		SymOpenRound:       {"(", symClassParenthesis},   //  40: '('
		SymClosedRound:     {")", symClassParenthesis},   //  41: ')'
		SymOpenSquare:      {"[", symClassParenthesis},   //  42: '['
		SymClosedSquare:    {"]", symClassParenthesis},   //  43: ']'
		SymOpenBrace:       {"{", symClassParenthesis},   //  44: '{'
		SymClosedBrace:     {"}", symClassParenthesis},   //  45: '}'
		SymTilde:           {"~", symClassOperator},      //  46: '~'
		SymDoubleQuestion:  {"??", symClassOperator},     //  47: '??'
		SymQuestionEqual:   {"?=", symClassOperator},     //  48: '?='
		SymQuestionExclam:  {"?!", symClassOperator},     //  49: '?!'
		SymDoubleAt:        {"@@", symClassOperator},     //  50: '@@'
		SymDoubleColon:     {"::", symClassOperator},     //  51: '::'
		SymDoubleGreater:   {">>", symClassOperator},     //  52: '>>'
		SymDoubleLess:      {"<<", symClassOperator},     //  53: '<<'
		SymCaret:           {"^", symClassOperator},      //  54: '^'
		SymDollarRound:     {"$(", symClassOperator},     //  55: '$('
		SymOpenClosedRound: {"()", symClassPostOp},       //  56: '()'
		SymDoubleDollar:    {"$$", symClassOperator},     //  57: '$$'
		SymDoubleDot:       {"..", symClassOperator},     //  58: '..'
		SymTripleDot:       {"...", symClassOperator},    //  59: '...'
		SymStarEqual:       {"*=", symClassOperator},     //  60: '*='
		SymSlashEqual:      {"/=", symClassOperator},     //  61: '/='
		SymPercEqual:       {"%=", symClassOperator},     //  62: '%='
		SymPlusGreater:     {"+>", symClassOperator},     //  63: '+>'
		SymLessPlus:        {"<+", symClassOperator},     //  64: '<+'
		// SymChangeSign
		// SymUnchangeSign
		// SymIdentifier
		// SymBool
		// SymInteger
		// SymVariable
		// SymFloat
		// SymFraction
		// SymString
		// SymIterator
		// SymOr:  "or",
		// SymAnd: "and",
		// SymNot: "not",
		// SymComment
		// SymFuncCall
		// SymFuncDef
		// SymList
		// SymDict
		// SymIndex
		// SymExpression
		// SymSelector     // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
		// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
		// // SymOpenComment                       //   0: '/*'
		// // SymClosedComment                     //   0: '*/'
		// // SymOneLineComment                    //   0: '//'
		// keywordBase
		SymKwAnd:     {"and", symClassOperator},
		SymKwNot:     {"not", symClassOperator},
		SymKwOr:      {"or", symClassOperator},
		SymKwBut:     {"but", symClassOperator},
		SymKwFunc:    {"func(", symClassDeclaration},
		SymKwBuiltin: {"builtin", symClassOperator},
		SymKwPlugin:  {"plugin", symClassOperator},
		SymKwIn:      {"in", symClassOperator},
		SymKwInclude: {"include", symClassOperator},
		SymKwNil:     {"nil", symClassValue},
		SymKwUnset:   {"unset", symClassOperator},
	}
}

func SymToString(sym Symbol) string {
	if s, ok := symbolMap[sym]; ok {
		return s.repr
	}
	return ""
}

func SymListToString(symList []Symbol, quote bool) string {
	var sb strings.Builder
	if len(symList) == 0 {
		sb.WriteString("<nothing>")
	} else {
		for _, sym := range symList {
			if sb.Len() > 0 {
				sb.WriteByte(',')
				sb.WriteByte(' ')
			}
			if quote {
				sb.WriteByte('`')
			}
			sb.WriteString(SymToString(sym))
			if quote {
				sb.WriteByte('`')
			}
		}
	}
	return sb.String()
}

func StringEndsWithOperator(s string) bool {
	return endingOperator(s) != SymNone
}

func endingOperator(s string) (sym Symbol) {
	sym = SymNone
	lower := strings.ToLower(s)
	for symbol, spec := range symbolMap {
		if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
			sym = symbol
			break
		}
	}
	return
}