//协议反序列化
package util

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
	"reflect"
	"runtime/debug"
	"sync"
	"unsafe"
)

//将二进制协议转为结构体
func UnPack(data []byte, v interface{}) error {
	d := decodeState{*bytes.NewBuffer(data)}
	return d.unmarshal(v)
}

func (d *decodeState) unmarshal(v interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			if je, ok := r.(error); ok {
				err = je
			} else {
				err = errors.New("unpack error:" + fmt.Sprint(r) + " debug:" + string(debug.Stack()))
			}
		}
	}()
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return errors.New("unpack not support type:" + reflect.TypeOf(v).String())
	}
	d.reflectValue(rv.Elem())
	return
}

//按长度读取字符串
func (d *decodeState) ReadBytesLen(l uint32) []byte {
	buf := make([]byte, l)
	_, err := io.ReadFull(d, buf)
	if err != nil {
		panic(err)
	}
	return buf
}

//读取数字类型(需要明确v指针类型)
func (d *decodeState) ReadInt(v interface{}) {
	err := binary.Read(d, binary.BigEndian, v)
	if err != nil {
		panic(err)
	}
}

type decodeState struct {
	bytes.Buffer
}

func (d *decodeState) reflectValue(v reflect.Value) {
	typeDecoder(v.Type())(d, v)
}

type decoderFunc func(e *decodeState, v reflect.Value)

var decoderCache sync.Map // map[reflect.Type]decoderFunc

func typeDecoder(t reflect.Type) decoderFunc {
	if fi, ok := decoderCache.Load(t); ok {
		return fi.(decoderFunc)
	}
	var (
		wg sync.WaitGroup
		f  decoderFunc
	)
	wg.Add(1)
	fi, loaded := decoderCache.LoadOrStore(t, decoderFunc(func(e *decodeState, v reflect.Value) {
		wg.Wait()
		f(e, v)
	}))
	if loaded {
		return fi.(decoderFunc)
	}
	f = newTypeDecoder(t)
	wg.Done()
	decoderCache.Store(t, f)
	return f
}
func newTypeDecoder(t reflect.Type) decoderFunc {
	switch t.Kind() {
	case reflect.Bool:
		return boolDecoder
	case reflect.Int:
		return intDecoder
	case reflect.Int32:
		return int32Decoder
	case reflect.Uint:
		return uintDecoder
	case reflect.Uint32:
		return uint32Decoder
	case reflect.Int8:
		return int8Decoder
	case reflect.Int16:
		return int16Decoder
	case reflect.Int64:
		return int64Decoder
	case reflect.Uint8:
		return uint8Decoder
	case reflect.Uint16:
		return uint16Decoder
	case reflect.Uint64:
		return uint64Decoder
	case reflect.Float32:
		return float32Decoder
	case reflect.Float64:
		return float64Decoder
	case reflect.String:
		return stringDecoder
	case reflect.Struct:
		return newStructDecoder(t)
	case reflect.Slice:
		return newSliceDecoder(t)
	case reflect.Array:
		return newArrayDecoder(t)
	default:
		panic(errors.New("proto unpack: unsupported type: " + t.String()))
	}
}

func boolDecoder(d *decodeState, v reflect.Value) {
	b, err := d.ReadByte()
	if err != nil {
		panic(err)
	}
	if b == 1 {
		v.SetBool(true)
	} else {
		v.SetBool(false)
	}
}

//读取四个字节
func intDecoder(d *decodeState, v reflect.Value) {
	var t int32
	d.ReadInt(&t)
	v.SetInt(int64(t))
}

//读取四个字节
func int32Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*int32)((unsafe.Pointer)(v.UnsafeAddr())))
}

//读取四个字节
func uintDecoder(d *decodeState, v reflect.Value) {
	var t uint32
	d.ReadInt(&t)
	v.SetUint(uint64(t))
}
func uint32Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*uint32)((unsafe.Pointer)(v.UnsafeAddr())))
}

func int8Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*int8)((unsafe.Pointer)(v.UnsafeAddr())))
}

func int16Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*int16)((unsafe.Pointer)(v.UnsafeAddr())))
}

func int64Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*int64)((unsafe.Pointer)(v.UnsafeAddr())))
}

func uint8Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*uint8)((unsafe.Pointer)(v.UnsafeAddr())))
}

func uint16Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*uint16)((unsafe.Pointer)(v.UnsafeAddr())))
}

func uint64Decoder(d *decodeState, v reflect.Value) {
	d.ReadInt((*uint64)((unsafe.Pointer)(v.UnsafeAddr())))
}

//float32
func float32Decoder(d *decodeState, v reflect.Value) {
	var b uint32 = 0
	d.ReadInt(&b)
	v.SetFloat(float64(math.Float32frombits(b)))
}

//float64
func float64Decoder(d *decodeState, v reflect.Value) {
	var b uint64 = 0
	d.ReadInt(&b)
	v.SetFloat(math.Float64frombits(b))
}

//string解析
func stringDecoder(d *decodeState, v reflect.Value) {
	var l uint32 = 0
	d.ReadInt(&l)
	if l == 0 {
		return
	}
	b := make([]byte, l)
	_, err := io.ReadFull(d, b)
	if err != nil {
		panic(err)
	}
	v.SetString(string(b))
}

func newSliceDecoder(t reflect.Type) decoderFunc {
	if t.Elem().Kind() == reflect.Uint8 {
		return decodeByteSlice
	}
	dec := sliceDecoder{newArrayDecoder(t)}
	return dec.decode
}

func newArrayDecoder(t reflect.Type) decoderFunc {
	dec := arrayDecoder{typeDecoder(t.Elem())}
	return dec.decode
}

func decodeByteSlice(d *decodeState, v reflect.Value) {
	var l uint32
	d.ReadInt(&l)
	if l == 0 {
		return
	}
	v.SetBytes(d.ReadBytesLen(l))
}

type sliceDecoder struct {
	arrayDec decoderFunc
}

type arrayDecoder struct {
	elemDec decoderFunc
}

func (se sliceDecoder) decode(d *decodeState, v reflect.Value) {
	se.arrayDec(d, v)
}

func (ae arrayDecoder) decode(d *decodeState, v reflect.Value) {
	var l uint32
	d.ReadInt(&l)
	if l == 0 {
		return
	}
	L := int(l)
	if v.Kind() == reflect.Slice {
		v.Set(reflect.MakeSlice(v.Type(), L, L))
	}
	for i := 0; i < L; i++ {
		ae.elemDec(d, v.Index(i))
	}
}

type structDecoder struct {
	fields []deField
}

func (se structDecoder) decode(d *decodeState, v reflect.Value) {
	for i := range se.fields {
		f := &se.fields[i]
		f.decoder(d, v.Field(i))
	}
}

var deFieldCache sync.Map // map[reflect.Type]structDecoder

type deField struct {
	decoder decoderFunc
}

func newStructDecoder(t reflect.Type) decoderFunc {
	return cachedDeFields(t).decode
}

func cachedDeFields(t reflect.Type) structDecoder {
	if f, ok := deFieldCache.Load(t); ok {
		return f.(structDecoder)
	}
	f, _ := deFieldCache.LoadOrStore(t, typeDeFields(t))
	return f.(structDecoder)
}

func typeDeFields(t reflect.Type) structDecoder {
	var fields []deField
	for i := 0; i < t.NumField(); i++ {
		sf := t.Field(i)
		st := sf.Type
		if st == t {
			panic("proto:not support recursive type:" + st.String())
		}
		if sf.PkgPath != "" {
			panic("proto:not support type:" + t.String() + ",name:" + sf.Name)
		}
		fields = append(fields, deField{decoder: typeDecoder(st)})
	}
	return structDecoder{fields}
}
