package muesli

import (
	"fmt"
)

/* Position of a token in an input stream */
type Position struct {
	FileName string
	Line     int
	Column   int
}

func (p Position) String() string {
	return fmt.Sprintf("%s:%d:%d", p.FileName, p.Line, p.Column)
}

/* Token Kind. Uses a rune to easily handle single character tokens. */
type TokenKind rune

const (
	TokenKindNone       = TokenKind(0)
	TokenKindInteger    = TokenKind('i')
	TokenKindFloat      = TokenKind('f')
	TokenKindString     = TokenKind('s')
	TokenKindSymbol     = TokenKind('S')
	TokenKindBoolean    = TokenKind('b')
    TokenKindNil        = TokenKind('N')
	TokenKindWord       = TokenKind('w')
	TokenKindType       = TokenKind('t')
	TokenKindOperator   = TokenKind('o')
	TokenKindRedirect   = TokenKind('R')
    TokenKindMethod     = TokenKind('M')	
    TokenKindGet        = TokenKind('$')
	TokenKindSet        = TokenKind('=')
	TokenKindOpenBlock  = TokenKind('{')
	TokenKindCloseBlock = TokenKind('}')
	TokenKindOpenList   = TokenKind('[')
	TokenKindCloseList  = TokenKind(']')
	TokenKindOpenParen  = TokenKind('(')
	TokenKindCloseParen = TokenKind(')')
	TokenKindError      = TokenKind('!')
	TokenKindEOX        = TokenKind('\n')
	TokenKindEOF        = TokenKind(0x255)
)

/* Names of the different token types. */
var TokenKindNames map[TokenKind]string = map[TokenKind]string{
	TokenKindNone:       "None",
	TokenKindInteger:    "Integer",
	TokenKindFloat:      "Float",
	TokenKindSymbol:     "Symbol",
	TokenKindString:     "String",
	TokenKindBoolean:    "Boolean",
    TokenKindNil:        "Nil",
	TokenKindWord:       "Word",
	TokenKindType:       "Type",
	TokenKindOperator:   "Operator",   
	TokenKindRedirect:   "Redirect",
    TokenKindMethod:     "Method",
	TokenKindGet:        "Get",
	TokenKindSet:        "Set",
	TokenKindOpenBlock:  "OpenBlock",
	TokenKindCloseBlock: "CloseBlock",
	TokenKindOpenList:   "OpenList",
	TokenKindCloseList:  "CloseList",
	TokenKindOpenParen:  "OpenParen",
	TokenKindCloseParen: "CloseParen",
	TokenKindError:      "Error",
	TokenKindEOX:        "EOX",
	TokenKindEOF:        "EOF",
}

/* Reverse map of the names of the different token types. */
var NamesTokenKind map[string] TokenKind = map[string]TokenKind{}

func init () {
	for k, v := range TokenKindNames {
		NamesTokenKind[v] = k
	}
}

/* Transforms a token kind to a String */
func (kind TokenKind) String() string {
	name, ok := TokenKindNames[kind]
	if !ok {
		return "Unknown TokenKind!"
	}
	return name
}

type Token struct {
	TokenKind
	Value
	Position
}

func (token Token) String() string {
	if token.Value == nil {
		return fmt.Sprintf("%s:%s:nil", token.Position.String(), token.TokenKind.String())

	}
	return fmt.Sprintf("%s:%s:%v", token.Position.String(), token.TokenKind.String(), token.Value)
}

func (token Token) Error() string {
	if token.TokenKind == TokenKindError {
		return token.Value.String()
	}
	return "No error"
}

/* Returns whether or not the token is the last to be expected,
 * that is either an error or EOF. */
func (token Token) IsLast() bool {
	switch token.TokenKind {
	case TokenKindError, TokenKindEOF, TokenKindNone:
		return true
	default:
		return false
	}
}

/* Creates a new token. */
func NewToken(kind TokenKind, val Value, pos Position) Token {
	return Token{kind, val, pos}
}

/* Creates a token with type TokenTypeNone */
func NoToken() Token {
	return NewToken(TokenKindNone, NilValue, Position{})
}

func (token Token) IsNone() bool {
	return token.TokenKind == TokenKindNone
}

func (token Token) IsError() bool {
	return token.TokenKind == TokenKindError
}