package xmlrpc
import (
	"bytes"
	"encoding/xml"
	"fmt"
	"reflect"
	"strconv"
	"time"
)
type encodeFunc func(reflect.Value) ([]byte, error)
func marshal(v interface{}) ([]byte, error) {
	if v == nil {
		return []byte{}, nil
	}
	val := reflect.ValueOf(v)
	return encodeValue(val)
}
func encodeValue(val reflect.Value) ([]byte, error) {
	var b []byte
	var err error
	if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
		if val.IsNil() {
			return []byte(""), nil
		}
		val = val.Elem()
	}
	switch val.Kind() {
	case reflect.Struct:
		switch val.Interface().(type) {
		case time.Time:
			t := val.Interface().(time.Time)
			b = []byte(fmt.Sprintf("%s", t.Format(iso8601)))
		default:
			b, err = encodeStruct(val)
		}
	case reflect.Map:
		b, err = encodeMap(val)
	case reflect.Slice:
		b, err = encodeSlice(val)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		b = []byte(fmt.Sprintf("%s", strconv.FormatInt(val.Int(), 10)))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		b = []byte(fmt.Sprintf("%s", strconv.FormatUint(val.Uint(), 10)))
	case reflect.Float32, reflect.Float64:
		b = []byte(fmt.Sprintf("%s",
			strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
	case reflect.Bool:
		if val.Bool() {
			b = []byte("1")
		} else {
			b = []byte("0")
		}
	case reflect.String:
		var buf bytes.Buffer
		xml.Escape(&buf, []byte(val.String()))
		if _, ok := val.Interface().(Base64); ok {
			b = []byte(fmt.Sprintf("%s", buf.String()))
		} else {
			b = []byte(fmt.Sprintf("%s", buf.String()))
		}
	default:
		return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
	}
	if err != nil {
		return nil, err
	}
	return []byte(fmt.Sprintf("%s", string(b))), nil
}
func encodeStruct(val reflect.Value) ([]byte, error) {
	var b bytes.Buffer
	b.WriteString("")
	t := val.Type()
	for i := 0; i < t.NumField(); i++ {
		b.WriteString("")
		f := t.Field(i)
		name := f.Tag.Get("xmlrpc")
		if name == "" {
			name = f.Name
		}
		b.WriteString(fmt.Sprintf("%s", name))
		p, err := encodeValue(val.FieldByName(f.Name))
		if err != nil {
			return nil, err
		}
		b.Write(p)
		b.WriteString("")
	}
	b.WriteString("")
	return b.Bytes(), nil
}
func encodeMap(val reflect.Value) ([]byte, error) {
	var t = val.Type()
	if t.Key().Kind() != reflect.String {
		return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
	}
	var b bytes.Buffer
	b.WriteString("")
	keys := val.MapKeys()
	for i := 0; i < val.Len(); i++ {
		key := keys[i]
		kval := val.MapIndex(key)
		b.WriteString("")
		b.WriteString(fmt.Sprintf("%s", key.String()))
		p, err := encodeValue(kval)
		if err != nil {
			return nil, err
		}
		b.Write(p)
		b.WriteString("")
	}
	b.WriteString("")
	return b.Bytes(), nil
}
func encodeSlice(val reflect.Value) ([]byte, error) {
	var b bytes.Buffer
	b.WriteString("")
	for i := 0; i < val.Len(); i++ {
		p, err := encodeValue(val.Index(i))
		if err != nil {
			return nil, err
		}
		b.Write(p)
	}
	b.WriteString("")
	return b.Bytes(), nil
}