// deepcopy makes deep copies of things. A standard copy will copy the
// pointers: deep copy copies the values pointed to.  Unexported field
// values are not copied.
//
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
// License: MIT, for more details check the included LICENSE file.
package deepcopy

import (
	"errors"
	"reflect"
	"time"
	// _ "github.com/stretchr/testify/assert"
)

// Interface for delegating copy process to type
type DeepCopyer interface {
	DeepCopy() interface{}
}

// // Iface is an alias to Copy; this exists for backwards compatibility reasons.
// func Iface(iface interface{}) interface{} {
// 	return Copy(iface)
// }

// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}.  The returned value will need to be asserted to the
// correct type.
// 只支持常规类型的复制，例如：简单类型，时间，map,slice,array,指针，结构体及结构体
// 不要用于有循环引用的数据
// 多个指针指向同一个变量，复制后会生成多个
// 未导出类型不复制；
func Copy(src interface{}) interface{} {
	if src == nil {
		return nil
	}

	// Make the interface a reflect.Value
	original := reflect.ValueOf(src)

	// Make a copy of the same type as the original.
	cpy := reflect.New(original.Type()).Elem()

	// Recursively copy the original.
	deep := 0
	err := copyRecursive(original, cpy, deep)
	if err != nil {
		panic(err)
	}

	// Return the copy as an interface.
	return cpy.Interface()
}

// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
// 支持：简单类型，时间，map,slice,array,指针，结构体及结构体匿名变量；
// 其他不支持（reflect.Chan, reflect.Func, reflect.Invalid, reflect.UnsafePointer）
// 未导出类型不复制；
// 多个指针指向同一个变量，复制后会生成多个
// 不要用于有循环引用的数据，深度只能防死循环，不能防重复构造；
// noCopy结构未处理
// original 类型值
// cpy 新类型值 指针的解引用
func copyRecursive(original, cpy reflect.Value, deep int) error {
	deep += 1
	if deep > 20 {
		return errors.New("max deep")
	}

	// check for implement deepcopy.Interface
	if original.CanInterface() {
		if copier, ok := original.Interface().(DeepCopyer); ok {
			cpy.Set(reflect.ValueOf(copier.DeepCopy()))
			return nil
		}
	}
	// fmt.Println(original.Type().Name(), original.Type().String())

	var err error
	// handle according to original's Kind
	switch original.Kind() {
	case reflect.Ptr:
		// Get the actual value being pointed to.
		originalValue := original.Elem()

		// if  it isn't valid, return.
		if !originalValue.IsValid() {
			return nil
		}
		cpy.Set(reflect.New(originalValue.Type()))
		err = copyRecursive(originalValue, cpy.Elem(), deep)

	case reflect.Interface:
		// If this is a nil, don't do anything
		if original.IsNil() {
			return nil
		}
		// Get the value for the interface, not the pointer.
		originalValue := original.Elem()

		// Get the value by calling Elem().
		copyValue := reflect.New(originalValue.Type()).Elem()
		err = copyRecursive(originalValue, copyValue, deep)
		cpy.Set(copyValue)

	case reflect.Struct:
		t, ok := original.Interface().(time.Time)
		if ok {
			cpy.Set(reflect.ValueOf(t))
			return nil
		}
		oriType := original.Type()
		if _, ok := oriType.FieldByName("noCopy"); ok {
			return nil
		}
		// Go through each field of the struct and copy it.
		num := original.NumField()
		for i := 0; i < num; i++ {
			if !oriType.Field(i).IsExported() {
				continue
			}

			err = copyRecursive(original.Field(i), cpy.Field(i), deep)
			if err != nil {
				break
			}
		}

	case reflect.Slice:
		if original.IsNil() {
			return nil
		}
		// Make a new slice and copy each element.
		cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
		for i := 0; i < original.Len(); i++ {
			err = copyRecursive(original.Index(i), cpy.Index(i), deep)
			if err != nil {
				break
			}
		}

	case reflect.Map:
		if original.IsNil() {
			return nil
		}
		cpy.Set(reflect.MakeMap(original.Type()))
		for _, key := range original.MapKeys() {
			originalValue := original.MapIndex(key)
			copyValue := reflect.New(originalValue.Type()).Elem()
			err = copyRecursive(originalValue, copyValue, deep)
			if err != nil {
				break
			}

			copyKey := reflect.New(key.Type()).Elem()
			err = copyRecursive(key, copyKey, deep)
			if err != nil {
				break
			}
			cpy.SetMapIndex(copyKey, copyValue)
		}

	case reflect.Array:
		for i := 0; i < original.Len(); i++ {
			copyRecursive(original.Index(i), cpy.Index(i), deep)
		}

	case reflect.Uintptr, reflect.Chan, reflect.Func, reflect.Invalid, reflect.UnsafePointer:
		return nil

	default:
		cpy.Set(original)
	}
	return err
}
