package btc

import (
	"encoding/json"
	"errors"
	"reflect"
)

type RPCMarshaler interface {
	Marshal(v interface{}) ([]byte, error)
}

type JSONMarshalerV2 struct{}

func (JSONMarshalerV2) Marshal(v interface{}) ([]byte, error) {
	d, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	return d, nil
}

var InvalidValue = errors.New("Invalid value to marshal")

type JSONMarshalerV1 struct{}

func (JSONMarshalerV1) Marshal(v interface{}) ([]byte, error) {
	u := cmdUntypedParams{}

	switch v := v.(type) {
	case *CmdGetBlock:
		var t bool
		if v.Params.Verbosity > 0 {
			t = true
		}
		u.Method = v.Method
		u.Params = append(u.Params, v.Params.BlockHash)
		u.Params = append(u.Params, t)
	case *CmdGetRawTransaction:
		var n int
		if v.Params.Verbose {
			n = 1
		}
		u.Method = v.Method
		u.Params = append(u.Params, v.Params.Txid)
		u.Params = append(u.Params, n)
	default:
		{
			v := reflect.ValueOf(v).Elem()

			f := v.FieldByName("Method")
			if !f.IsValid() || f.Kind() != reflect.String {
				return nil, InvalidValue
			}
			u.Method = f.String()

			f = v.FieldByName("Params")
			if f.IsValid() {
				var arr []interface{}
				switch f.Kind() {
				case reflect.Slice:
					arr = make([]interface{}, f.Len())
					for i := 0; i < f.Len(); i++ {
						arr[i] = f.Index(i).Interface()
					}
				case reflect.Struct:
					arr = make([]interface{}, f.NumField())
					for i := 0; i < f.NumField(); i++ {
						arr[i] = f.Field(i).Interface()
					}
				default:
					return nil, InvalidValue
				}
				u.Params = arr
			}
		}
	}

	d, err := json.Marshal(u)
	if err != nil {
		return nil, err
	}

	return d, nil
}

type cmdUntypedParams struct {
	Method string        `json:"method"`
	Params []interface{} `json:"params,omitempty"`
}
