package attl

//Converter is an interface that Values can optionally implement
// to allow conversion to other arbitrary types at run time.
type Converter interface {
	Convert(to interface{}) *Error
}

func (from Int) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *int8:
		(*toPtr) = int8(from)
	case *int16:
		(*toPtr) = int16(from)
	case *int32:
		(*toPtr) = int32(from)
	case *int64:
		(*toPtr) = int64(from)
	case *int:
		(*toPtr) = int(from)
	case *bool:
		(*toPtr) = (from != 0)
	case *Bool:
		(*toPtr) = (from != 0)
	case *float32:
		(*toPtr) = float32(from)
	case *float64:
		(*toPtr) = float64(from)
	case *Int:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert Int value")
	}
	return nil
}

func (from Bool) Convert(to interface{}) *Error {
	iVal := 0
	if from {
		iVal = -1
	}
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *int8:
		(*toPtr) = int8(iVal)
	case *int16:
		(*toPtr) = int16(iVal)
	case *int32:
		(*toPtr) = int32(iVal)
	case *int64:
		(*toPtr) = int64(iVal)
	case *int:
		(*toPtr) = int(iVal)
	case *bool:
		(*toPtr) = bool(from)
	case *Bool:
		(*toPtr) = from
	case *float32:
		(*toPtr) = float32(iVal)
	case *float64:
		(*toPtr) = float64(iVal)
	case *Int:
		(*toPtr) = Int(iVal)
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert Int value")
	}
	return nil
}

func (from Word) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *bool:
		(*toPtr) = (from.String() != "")
	case *Bool:
		(*toPtr) = (from.String() != "")
	case *Word:
		(*toPtr) = from
	case *Type:
		(*toPtr) = Type(string(from))
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert Word value")
	}
	return nil
}

func (from Type) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *bool:
		(*toPtr) = (from.String() != "")
	case *Bool:
		(*toPtr) = (from.String() != "")
	case *Type:
		(*toPtr) = from
	case *Word:
		(*toPtr) = Word(string(from))
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert Word value")
	}
	return nil
}

func (from String) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *bool:
		(*toPtr) = (from.String() != "")
	case *Bool:
		(*toPtr) = (from.String() != "")
	case **Error:
		(*toPtr) = ErrorFromString(from.String())
	case *String:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert String value")
	}
	return nil
}

func (from *Error) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *string:
		(*toPtr) = from.String()
	case *bool:
		(*toPtr) = (from == nil)
	case *Bool:
		(*toPtr) = (from == nil)
	case *error:
		(*toPtr) = from
	case **Error:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert Error value")
	}
	return nil
}

func (from Block) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *bool:
		(*toPtr) = (len(from.Statements) > 0)
	case *Bool:
		(*toPtr) = (len(from.Statements) > 0)
	case *Block:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert block value")
	}
	return nil
}

func (from Map) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *bool:
		(*toPtr) = (len(from) > 0)
	case *Bool:
		(*toPtr) = (len(from) > 0)
	case *Map:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert map value")
	}
	return nil
}

func (from List) Convert(to interface{}) *Error {
	switch toPtr := to.(type) {
	case *bool:
		(*toPtr) = (len(from) > 0)
	case *Bool:
		(*toPtr) = (len(from) > 0)
	case *List:
		(*toPtr) = from
	case *Value:
		(*toPtr) = from
	default:
		return ErrorFromString("Cannot convert map value")
	}
	return nil
}

func ValToBool(val Value) bool {
	if val == nil {
		return false
	}
	switch check := val.(type) {
	case *Error:
		return check == nil
	case Int:
		return (int(check) != 0)
	case Bool:
		return bool(check)
	default:
		return val.String() != ""
	}
}

func Convert(val Value, to interface{}) *Error {
	if converter, ok := val.(Converter); ok {
		return converter.Convert(to)
	} else if pval, ok := to.(*Value); ok {
		*pval = val
		return nil
	}
	return ErrorFromString("Value cannot be converted")
}

// StringList makes a List from string arguments
func StringList(sa ...string) List {
	list := List{}
	for _, s := range sa {
		list = append(list, String(s))
	}
	return list
}

// Converts a list to raw strings
func (l List) ToStrings() []string {
	res := []string{}
	for _, s := range l {
		res = append(res, s.String())
	}
	return res
}
