package muesli

import "fmt"

// Door is an example of how to wrap a custom type in Muesli.
type Door struct {
    *BasicObject
	name string
	locked bool
	opened bool
}

var _ Value = &Door{}

const DoorType = TypeValue("Door")

func (door * Door) String() string {
	return fmt.Sprintf("Door: %s %v %v", door.name, door.locked, door.opened)
}


func (*Door) Type() TypeValue {
	return DoorType
}

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

func door(vm *VM, val ...Value) [] Value {
	var name string
	var locked bool
	_, err := ParseOptArgs(val, 1, &name, &locked)
	if err != nil {
		return Fail(err)
	}
	d := &Door{name: name, locked: locked, opened:false}
    d.BasicObject = NewBasicObject(d, nil)
    d.Getter("name", func() Value {
            return StringValue(d.name)
        })
	d.Getter("locked", func() Value {
            return BoolValue(d.locked)
        })
	d.Getter("opened", func() Value {
            return BoolValue(d.opened)
        })
    d.Method("open", func(...Value) []Value { 
        d.Open()
        return Ok(d)
        })
    return Ok(d)
}

func (door * Door) Open() {
	door.opened = true
}

func openDoor(vm *VM, val ...Value) [] Value {
	var door *Door
	_, err := ParseArgs(val, &door)
	if err != nil {
		return Fail(err)
	}
	if door == nil {
		return Fail(NewErrorValuef("Door may not be nil."))
	}		
	door.Open()
	return Ok(door)
}

func nameDoor(vm *VM, val ...Value) [] Value {
	var door *Door
	var name string
	_, err := ParseArgs(val, &door, &name)
	if err != nil {
		return Fail(err)
	}
	if door == nil {
		return Fail(NewErrorValuef("Door may not be nil."))
	}		
	if name == "" {
		return Fail(NewErrorValuef("Name may not be empty."))
	}	
		
	door.name = name
	return Ok(door)
}

func RegisterDoor(vm * VM) {
	vm.RegisterBuiltin("door", door)
	vm.RegisterBuiltin("openDoor", openDoor).Takes(DoorType).Returns(DoorType)
	vm.RegisterBuiltin("nameDoor", nameDoor).Takes(DoorType).Returns(DoorType)
    vm.Register("Door", DoorType)
	vm.AddOverloadByName("open", "openDoor", 0)
	vm.AddOverloads("name", Over("nameDoor", 0, DoorType))
}