// 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
	symClassCommand
	symClassIdentifier
	symClassDelimiter
	symClassParenthesis
	symClassDeclaration
	symClassValue
	symClassOther
)

type symbolSpec struct {
	repr   string
	kind   symbolClass
	opType termPosition
}

func init() {

	symbolMap = map[Symbol]symbolSpec{
		SymUnknown:            {"<unknown>", symClassOther, posLeaf},  //  -1: Unknown symbol
		SymNone:               {"<null>", symClassOther, posLeaf},     //   0: Null value for variable of type symbol
		SymError:              {"<error>", symClassOther, posLeaf},    //   1: Error reading from stream
		SymEos:                {"<eos>", symClassOther, posLeaf},      //   2: End of stream
		SymMinus:              {"-", symClassOperator, posInfix},      //   3: '-'
		SymMinusEqual:         {"-=", symClassOperator, posInfix},     //   4: '-='
		SymDoubleMinus:        {"--", symClassOperator, posPostfix},   //   5: '--'
		SymPlus:               {"+", symClassOperator, posInfix},      //   6: '+'
		SymPlusEqual:          {"+=", symClassOperator, posInfix},     //   7: '+='
		SymDoublePlus:         {"++", symClassOperator, posPostfix},   //   8: '++'
		SymStar:               {"*", symClassOperator, posInfix},      //   9: '*'
		SymDoubleStar:         {"**", symClassOperator, posInfix},     //  10: '**'
		SymSlash:              {"/", symClassOperator, posInfix},      //  11: '/'
		SymBackSlash:          {"\\", symClassOperator, posLeaf},      //  12: '\'
		SymVertBar:            {"|", symClassOperator, posInfix},      //  13: '|'
		SymDoubleVertBar:      {"||", symClassOperator, posInfix},     //  14: '||'
		SymComma:              {",", symClassOperator, posInfix},      //  15: ','
		SymColon:              {":", symClassOperator, posInfix},      //  16: ':'
		SymSemiColon:          {";", symClassOperator, posInfix},      //  17: ';'
		SymDot:                {".", symClassOperator, posInfix},      //  18: '.'
		SymDotSlash:           {"./", symClassOperator, posInfix},     //  19: './'
		SymQuote:              {"'", symClassDelimiter, posLeaf},      //  20: '\''
		SymDoubleQuote:        {"\"", symClassDelimiter, posLeaf},     //  21: '"'
		SymBackTick:           {"`", symClassDelimiter, posLeaf},      //  22: '`'
		SymExclamation:        {"!", symClassOperator, posPostfix},    //  23: '!'
		SymQuestion:           {"?", symClassOperator, posInfix},      //  24: '?'
		SymAmpersand:          {"&", symClassOperator, posInfix},      //  25: '&'
		SymDoubleAmpersand:    {"&&", symClassOperator, posInfix},     //  26: '&&'
		SymPercent:            {"%", symClassOperator, posInfix},      //  27: '%'
		SymAt:                 {"@", symClassOperator, posPrefix},     //  28: '@'
		SymUndescore:          {"_", symClassIdentifier, posLeaf},     //  29: '_'
		SymEqual:              {"=", symClassOperator, posInfix},      //  30: '='
		SymDoubleEqual:        {"==", symClassOperator, posInfix},     //  31: '=='
		SymLess:               {"<", symClassOperator, posInfix},      //  32: '<'
		SymLessOrEqual:        {"<=", symClassOperator, posInfix},     //  33: '<='
		SymGreater:            {">", symClassOperator, posInfix},      //  34: '>'
		SymGreaterOrEqual:     {">=", symClassOperator, posInfix},     //  35: '>='
		SymLessGreater:        {"<>", symClassOperator, posInfix},     //  36: '<>'
		SymNotEqual:           {"!=", symClassOperator, posInfix},     //  37: '!='
		SymDollar:             {"$", symClassOperator, posPrefix},     //  38: '$'
		SymHash:               {"#", symClassOperator, posPrefix},     //  39: '#'
		SymOpenRound:          {"(", symClassParenthesis, posPrefix},  //  40: '('
		SymClosedRound:        {")", symClassParenthesis, posPostfix}, //  41: ')'
		SymOpenSquare:         {"[", symClassParenthesis, posPrefix},  //  42: '['
		SymClosedSquare:       {"]", symClassParenthesis, posPostfix}, //  43: ']'
		SymOpenBrace:          {"{", symClassParenthesis, posPrefix},  //  44: '{'
		SymClosedBrace:        {"}", symClassParenthesis, posPostfix}, //  45: '}'
		SymTilde:              {"~", symClassOperator, posPrefix},     //  46: '~'
		SymDoubleQuestion:     {"??", symClassOperator, posInfix},     //  47: '??'
		SymQuestionEqual:      {"?=", symClassOperator, posInfix},     //  48: '?='
		SymQuestionExclam:     {"?!", symClassOperator, posInfix},     //  49: '?!'
		SymDoubleAt:           {"@@", symClassCommand, posLeaf},       //  50: '@@'
		SymDoubleColon:        {"::", symClassOperator, posInfix},     //  51: '::'
		SymDoubleGreater:      {">>", symClassOperator, posInfix},     //  52: '>>'
		SymDoubleLess:         {"<<", symClassOperator, posInfix},     //  53: '<<'
		SymCaret:              {"^", symClassOperator, posInfix},      //  54: '^'
		SymDollarRound:        {"$(", symClassOperator, posPrefix},    //  55: '$('
		SymOpenClosedRound:    {"()", symClassOperator, posPostfix},   //  56: '()'
		SymDoubleDollar:       {"$$", symClassCommand, posLeaf},       //  57: '$$'
		SymDoubleDot:          {"..", symClassOperator, posInfix},     //  58: '..'
		SymTripleDot:          {"...", symClassOperator, posPostfix},  //  59: '...'
		SymStarEqual:          {"*=", symClassOperator, posInfix},     //  60: '*='
		SymSlashEqual:         {"/=", symClassOperator, posInfix},     //  61: '/='
		SymPercEqual:          {"%=", symClassOperator, posInfix},     //  62: '%='
		SymDoubleLessEqual:    {"<<=", symClassOperator, posInfix},    //  63: '<<='
		SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix},    //  64: '>>='
		SymAmpersandEqual:     {"&=", symClassOperator, posInfix},     //  65: '&='
		SymVertBarEqual:       {"|=", symClassOperator, posInfix},     //  65: '|='
		SymCaretEqual:         {"^=", symClassOperator, posInfix},     //  66: '^='
		SymPlusGreater:        {"+>", symClassOperator, posInfix},     //  67: '+>'
		SymLessPlus:           {"<+", symClassOperator, posInfix},     //  68: '<+'
		SymPreInc:             {"++", symClassOperator, posPrefix},    //    : '++'
		SymPreDec:             {"--", symClassOperator, posPrefix},    //    : '--'
		// 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, posInfix},
		SymKwNot:     {"not", symClassOperator, posInfix},
		SymKwOr:      {"or", symClassOperator, posInfix},
		SymKwBut:     {"but", symClassOperator, posInfix},
		SymKwFunc:    {"func(", symClassDeclaration, posPrefix},
		SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
		SymKwPlugin:  {"plugin", symClassOperator, posPrefix},
		SymKwIn:      {"in", symClassOperator, posInfix},
		SymKwInclude: {"include", symClassOperator, posPrefix},
		SymKwNil:     {"nil", symClassValue, posLeaf},
		SymKwUnset:   {"unset", symClassOperator, posPrefix},
	}
}

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) {
	var matchLength = 0
	sym = SymNone
	lower := strings.TrimRight(strings.ToLower(s), " \t")
	for symbol, spec := range symbolMap {
		if strings.HasSuffix(lower, spec.repr) {
			if len(spec.repr) > matchLength {
				matchLength = len(spec.repr)
				if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
					sym = symbol
				} else {
					sym = SymNone
				}
			}
		}
	}
	return
}
