package muesli

import "fmt"

/* Muesli has no key words by default, but they can be defined if desired
 * for ease of use. A key word is replaced by a token during lexing. */
type Keyword struct {
	Name string
	TokenKind
	Value 
} 


var DefaultKeywords []*Keyword = []*Keyword{
	&Keyword { "true", TokenKindBoolean, TrueValue },
	&Keyword { "false", TokenKindBoolean, FalseValue },
	&Keyword { "nil", TokenKindNil, NilValue },
	&Keyword { "do", TokenKindOpenBlock, StringValue("{") },
	&Keyword { "end", TokenKindCloseBlock, StringValue("}") },
	&Keyword { "as", TokenKindOpenParen, StringValue("(") },
	&Keyword { "so", TokenKindCloseParen, StringValue(")") },
	&Keyword { "list", TokenKindOpenList, StringValue("[") },
	&Keyword { "done", TokenKindCloseList, StringValue("]") },
    &Keyword { "the", TokenKindGet, StringValue("$") },
	&Keyword { "a", TokenKindGet, StringValue("$") },
	&Keyword { "an", TokenKindGet, StringValue("$") },
}

var DefaultKeywordsFR []*Keyword = []*Keyword{
	&Keyword { "vrai", TokenKindBoolean, TrueValue },
	&Keyword { "faux", TokenKindBoolean, FalseValue },
	&Keyword { "nul", TokenKindNil, NilValue },
	&Keyword { "fais", TokenKindOpenBlock, StringValue("{") },
	&Keyword { "fin", TokenKindCloseBlock, StringValue("}") },
	&Keyword { "comme", TokenKindOpenParen, StringValue("(") },
	&Keyword { "ça", TokenKindCloseParen, StringValue(")") },
	&Keyword { "liste", TokenKindOpenList, StringValue("[") },
	&Keyword { "fini", TokenKindCloseList, StringValue("]") },
    &Keyword { "le", TokenKindGet, StringValue("$") },
    &Keyword { "la", TokenKindGet, StringValue("$") },
}


var _ Value = &Keyword{}

const KeywordType = TypeValue("Keyword")

func (kw * Keyword) String() string {
	return fmt.Sprintf("Keyword: %s %v %v", kw.Name, kw.TokenKind, kw.Value)
}

func (*Keyword) Type() TypeValue {
	return KeywordType
}

func (from * Keyword) Convert(to interface{}) error {
	switch toPtr := to.(type) {
		case **Keyword:
			(*toPtr) = from
		case *Keyword:
			(*toPtr) = *from
		case *Value:
			(*toPtr) = from
		default:
			return NewErrorValuef("Cannot convert Keyword value %v to %v", from, to)
	}
	return nil
}

func (vm * VM) AddKeyword(kw *Keyword) *Keyword {
    found := vm.Lookup(KeywordName)
    list, ok := found.(*ListValue)
    if !ok || list == nil  {
        list = NewListValue()
        vm.Register(KeywordName, list)
    }
    list.Append(kw)
    return kw
}


func keyword(vm *VM, val ...Value) []Value {
	var name string = val[0].String()
	var tokenName string = val[1].String()
    var value = val[2]
    tokenkind, ok := NamesTokenKind[tokenName]
    if ! ok {
        return Fail(NewErrorValuef("Token kind unknown: %s", tokenName))
    }
    
    kw := &Keyword{ Name: name, TokenKind: tokenkind, Value: value }
	return Ok(vm.AddKeyword(kw))
}

func keywords(vm *VM, val ...Value) []Value {
    kws := []Value{}
    for i := 2 ; i < len(val) ; i += 3 {
    	var name string = val[i-2].String()
        var tokenName string = val[i-1].String()
        var value = val[i]
        tokenkind, ok := NamesTokenKind[tokenName]
        if !ok {
            return Fail(NewErrorValuef("Token kind unknown: %s", tokenName))
        }
        kw := &Keyword{ Name: name, TokenKind: tokenkind, Value: value }
        kws = append(kws, kw)
    }
    
    listValue := NewListValue(kws...)    
    vm.RegisterTop(KeywordName, listValue)
    return Ok(listValue)
}


const KeywordName = "muesli__keywords__"

func (vm * VM) StoreKeywords(keywords []*Keyword) *ListValue {
    vm.Register("Keyword", KeywordType) 
    vm.RegisterBuiltin("keyword", keyword).Takes(StringType, StringType, AnyType).Returns(KeywordType)
    vm.RegisterBuiltin("keywords", keyword).Takes(StringType, StringType, AnyType).Returns(ListType)
    list := NewValueArray()
    for _, kw := range keywords {
        list = append(list, kw)
    }    
    listValue := NewListValue(list...)
    vm.RegisterTop(KeywordName, listValue)
    return listValue
}


func (vm * VM) LoadKeywords() ([]*Keyword) {
    found := vm.Lookup(KeywordName)
    list, ok := found.(*ListValue)
    if !ok || list == nil {
        return nil
    }
    
    result := []*Keyword{}
    for _, kw := range list.List {
        result = append(result, kw.(*Keyword))
    }
    return result
}