package deepcopy

import (
	"fmt"
	"reflect"
	"testing"
	"time"
	"unsafe"

	"github.com/stretchr/testify/assert"
)

// hasSameDataPointer 检查两个切片是否共享相同的底层数组
func hasSameDataPointer(a1, b1 interface{}) bool {
	a := reflect.ValueOf(a1)
	b := reflect.ValueOf(b1)

	// 首先确保两个值的类型相同
	if a.Kind() != b.Kind() {
		return false
	}

	switch a.Kind() {
	case reflect.Slice:
		// 对于切片，比较底层数组指针
		return unsafe.Pointer(a.Pointer()) == unsafe.Pointer(b.Pointer())

	case reflect.Ptr:
		// 对于指针，直接比较指针值
		return a.Pointer() == b.Pointer()

	case reflect.Map:
		// 对于映射，比较映射指针
		return a.Pointer() == b.Pointer()

	case reflect.Chan:
		// 对于通道，比较通道指针
		return a.Pointer() == b.Pointer()

	default:
		// 其他类型可能不支持直接的内存比较
		return false
	}
	// aVal := reflect.ValueOf(a)
	// bVal := reflect.ValueOf(b)
	// if aVal.Kind() != reflect.Slice || bVal.Kind() != reflect.Slice {
	// 	return false
	// }
	// a1 := (*reflect.SliceHeader)(unsafe.Pointer(aVal.Pointer()))
	// b1 := (*reflect.SliceHeader)(unsafe.Pointer(bVal.Pointer()))
	// return a1.Data == b1.Data
}

// just basic is this working stuff
func TestSimple(t *testing.T) {
	t.Run("Copy[]string", func(t *testing.T) {
		original := []string{"a", "b", "c"}
		originalC := []string{"a", "b", "d"}
		if hasSameDataPointer(original, originalC) {
			fmt.Println("same")
		}

		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyS := copied.([]string)
		assert.False(t, hasSameDataPointer(original, cpyS), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyS), "length should match")
		assert.Equal(t, original, cpyS, "content should match")
	})

	t.Run("Copy[]bool", func(t *testing.T) {
		original := []bool{true, true, false, false}
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyB := copied.([]bool)
		assert.False(t, hasSameDataPointer(original, cpyB), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyB), "length should match")
		assert.Equal(t, original, cpyB, "content should match")
	})

	t.Run("Copy[]byte", func(t *testing.T) {
		original := []byte("hello")
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyBt := copied.([]byte)
		assert.False(t, hasSameDataPointer(original, cpyBt), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyBt), "length should match")
		assert.Equal(t, original, cpyBt, "content should match")
	})

	t.Run("Copy[]int", func(t *testing.T) {
		original := []int{42}
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyI := copied.([]int)
		assert.False(t, hasSameDataPointer(original, cpyI), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyI), "length should match")
		assert.Equal(t, original, cpyI, "content should match")
	})

	t.Run("Copy[]uint", func(t *testing.T) {
		original := []uint{1, 2, 3, 4, 5}
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyU := copied.([]uint)
		assert.False(t, hasSameDataPointer(original, cpyU), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyU), "length should match")
		assert.Equal(t, original, cpyU, "content should match")
	})

	t.Run("Copy[]float32", func(t *testing.T) {
		original := []float32{3.14}
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyF := copied.([]float32)
		assert.False(t, hasSameDataPointer(original, cpyF), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyF), "length should match")
		assert.Equal(t, original, cpyF, "content should match")
	})

	t.Run("Copy[]interface{}", func(t *testing.T) {
		original := []interface{}{"a", 42, true, 4.32}
		copied, err := Copy(original)
		assert.NoError(t, err)

		cpyIf := copied.([]interface{})
		assert.False(t, hasSameDataPointer(original, cpyIf), "expected different memory addresses")
		assert.Equal(t, len(original), len(cpyIf), "length should match")
		assert.Equal(t, original, cpyIf, "content should match")
	})
}

type Basics struct {
	String      string
	Strings     []string
	StringArr   [4]string
	Bool        bool
	Bools       []bool
	Byte        byte
	Bytes       []byte
	Int         int
	Ints        []int
	Int8        int8
	Int8s       []int8
	Int16       int16
	Int16s      []int16
	Int32       int32
	Int32s      []int32
	Int64       int64
	Int64s      []int64
	Uint        uint
	Uints       []uint
	Uint8       uint8
	Uint8s      []uint8
	Uint16      uint16
	Uint16s     []uint16
	Uint32      uint32
	Uint32s     []uint32
	Uint64      uint64
	Uint64s     []uint64
	Float32     float32
	Float32s    []float32
	Float64     float64
	Float64s    []float64
	Complex64   complex64
	Complex64s  []complex64
	Complex128  complex128
	Complex128s []complex128
	Interface   interface{}
	Interfaces  []interface{}
}

// These tests test that all supported basic types are copied correctly.  This
// is done by copying a struct with fields of most of the basic types as []T.
func TestMostTypes(t *testing.T) {
	test := Basics{
		String:      "kimchi",
		Strings:     []string{"uni", "ika"},
		StringArr:   [4]string{"malort", "barenjager", "fernet", "salmiakki"},
		Bool:        true,
		Bools:       []bool{true, false, true},
		Byte:        'z',
		Bytes:       []byte("abc"),
		Int:         42,
		Ints:        []int{0, 1, 3, 4},
		Int8:        8,
		Int8s:       []int8{8, 9, 10},
		Int16:       16,
		Int16s:      []int16{16, 17, 18, 19},
		Int32:       32,
		Int32s:      []int32{32, 33},
		Int64:       64,
		Int64s:      []int64{64},
		Uint:        420,
		Uints:       []uint{11, 12, 13},
		Uint8:       81,
		Uint8s:      []uint8{81, 82},
		Uint16:      160,
		Uint16s:     []uint16{160, 161, 162, 163, 164},
		Uint32:      320,
		Uint32s:     []uint32{320, 321},
		Uint64:      640,
		Uint64s:     []uint64{6400, 6401, 6402, 6403},
		Float32:     32.32,
		Float32s:    []float32{32.32, 33},
		Float64:     64.1,
		Float64s:    []float64{64, 65, 66},
		Complex64:   complex64(-64 + 12i),
		Complex64s:  []complex64{complex64(-65 + 11i), complex64(66 + 10i)},
		Complex128:  complex128(-128 + 12i),
		Complex128s: []complex128{complex128(-128 + 11i), complex128(129 + 10i)},
		Interfaces:  []interface{}{42, true, "pan-galactic"},
	}

	cpy, err := Copy(test)
	if err != nil {
		t.Fatal(err)
	}
	cpyBasics := cpy.(Basics)

	// see if they point to the same location
	if fmt.Sprintf("%p", &cpyBasics) == fmt.Sprintf("%p", &test) {
		t.Error("address of copy was the same as original; they should be different")
		return
	}

	// Go through each field and check to see it got copied properly
	if cpyBasics.String != test.String {
		t.Errorf("String: got %v; want %v", cpyBasics.String, test.String)
	}

	if hasSameDataPointer(test.Strings, cpyBasics.Strings) {
		t.Error("Strings: address of copy was the same as original; they should be different")
		goto StringArr
	}

	if len(cpyBasics.Strings) != len(test.Strings) {
		t.Errorf("Strings: len was %d; want %d", len(cpyBasics.Strings), len(test.Strings))
		goto StringArr
	}
	for i, v := range test.Strings {
		if v != cpyBasics.Strings[i] {
			t.Errorf("Strings: got %v at index %d of the copy; want %v", cpyBasics.Strings[i], i, v)
		}
	}

StringArr:
	if unsafe.Pointer(&test.StringArr) == unsafe.Pointer(&cpyBasics.StringArr) {
		t.Error("StringArr: address of copy was the same as original; they should be different")
		goto Bools
	}
	for i, v := range test.StringArr {
		if v != cpyBasics.StringArr[i] {
			t.Errorf("StringArr: got %v at index %d of the copy; want %v", cpyBasics.StringArr[i], i, v)
		}
	}

Bools:
	if cpyBasics.Bool != test.Bool {
		t.Errorf("Bool: got %v; want %v", cpyBasics.Bool, test.Bool)
	}

	if hasSameDataPointer(test.Bools, cpyBasics.Bools) {
		t.Error("Bools: address of copy was the same as original; they should be different")
		goto Bytes
	}
	if len(cpyBasics.Bools) != len(test.Bools) {
		t.Errorf("Bools: len was %d; want %d", len(cpyBasics.Bools), len(test.Bools))
		goto Bytes
	}
	for i, v := range test.Bools {
		if v != cpyBasics.Bools[i] {
			t.Errorf("Bools: got %v at index %d of the copy; want %v", cpyBasics.Bools[i], i, v)
		}
	}

Bytes:
	if cpyBasics.Byte != test.Byte {
		t.Errorf("Byte: got %v; want %v", cpyBasics.Byte, test.Byte)
	}

	if hasSameDataPointer(test.Bytes, cpyBasics.Bytes) {
		t.Error("Bytes: address of copy was the same as original; they should be different")
		goto Ints
	}
	if len(cpyBasics.Bytes) != len(test.Bytes) {
		t.Errorf("Bytes: len was %d; want %d", len(cpyBasics.Bytes), len(test.Bytes))
		goto Ints
	}
	for i, v := range test.Bytes {
		if v != cpyBasics.Bytes[i] {
			t.Errorf("Bytes: got %v at index %d of the copy; want %v", cpyBasics.Bytes[i], i, v)
		}
	}

Ints:
	if cpyBasics.Int != test.Int {
		t.Errorf("Int: got %v; want %v", cpyBasics.Int, test.Int)
	}

	if hasSameDataPointer(test.Ints, cpyBasics.Ints) {
		t.Error("Ints: address of copy was the same as original; they should be different")
		goto Int8s
	}
	if len(cpyBasics.Ints) != len(test.Ints) {
		t.Errorf("Ints: len was %d; want %d", len(cpyBasics.Ints), len(test.Ints))
		goto Int8s
	}
	for i, v := range test.Ints {
		if v != cpyBasics.Ints[i] {
			t.Errorf("Ints: got %v at index %d of the copy; want %v", cpyBasics.Ints[i], i, v)
		}
	}

Int8s:
	if cpyBasics.Int8 != test.Int8 {
		t.Errorf("Int8: got %v; want %v", cpyBasics.Int8, test.Int8)
	}

	if hasSameDataPointer(test.Int8s, cpyBasics.Int8s) {
		t.Error("Int8s: address of copy was the same as original; they should be different")
		goto Int16s
	}
	if len(cpyBasics.Int8s) != len(test.Int8s) {
		t.Errorf("Int8s: len was %d; want %d", len(cpyBasics.Int8s), len(test.Int8s))
		goto Int16s
	}
	for i, v := range test.Int8s {
		if v != cpyBasics.Int8s[i] {
			t.Errorf("Int8s: got %v at index %d of the copy; want %v", cpyBasics.Int8s[i], i, v)
		}
	}

Int16s:
	if cpyBasics.Int16 != test.Int16 {
		t.Errorf("Int16: got %v; want %v", cpyBasics.Int16, test.Int16)
	}

	if hasSameDataPointer(test.Int16s, cpyBasics.Int16s) {
		t.Error("Int16s: address of copy was the same as original; they should be different")
		goto Int32s
	}
	if len(cpyBasics.Int16s) != len(test.Int16s) {
		t.Errorf("Int16s: len was %d; want %d", len(cpyBasics.Int16s), len(test.Int16s))
		goto Int32s
	}
	for i, v := range test.Int16s {
		if v != cpyBasics.Int16s[i] {
			t.Errorf("Int16s: got %v at index %d of the copy; want %v", cpyBasics.Int16s[i], i, v)
		}
	}

Int32s:
	if cpyBasics.Int32 != test.Int32 {
		t.Errorf("Int32: got %v; want %v", cpyBasics.Int32, test.Int32)
	}

	if hasSameDataPointer(test.Int32s, cpyBasics.Int32s) {
		t.Error("Int32s: address of copy was the same as original; they should be different")
		goto Int64s
	}
	if len(cpyBasics.Int32s) != len(test.Int32s) {
		t.Errorf("Int32s: len was %d; want %d", len(cpyBasics.Int32s), len(test.Int32s))
		goto Int64s
	}
	for i, v := range test.Int32s {
		if v != cpyBasics.Int32s[i] {
			t.Errorf("Int32s: got %v at index %d of the copy; want %v", cpyBasics.Int32s[i], i, v)
		}
	}

Int64s:
	if cpyBasics.Int64 != test.Int64 {
		t.Errorf("Int64: got %v; want %v", cpyBasics.Int64, test.Int64)
	}

	if hasSameDataPointer(test.Int64s, cpyBasics.Int64s) {
		t.Error("Int64s: address of copy was the same as original; they should be different")
		goto Uints
	}
	if len(cpyBasics.Int64s) != len(test.Int64s) {
		t.Errorf("Int64s: len was %d; want %d", len(cpyBasics.Int64s), len(test.Int64s))
		goto Uints
	}
	for i, v := range test.Int64s {
		if v != cpyBasics.Int64s[i] {
			t.Errorf("Int64s: got %v at index %d of the copy; want %v", cpyBasics.Int64s[i], i, v)
		}
	}

Uints:
	if cpyBasics.Uint != test.Uint {
		t.Errorf("Uint: got %v; want %v", cpyBasics.Uint, test.Uint)
	}

	if hasSameDataPointer(test.Uints, cpyBasics.Uints) {
		t.Error("Uints: address of copy was the same as original; they should be different")
		goto Uint8s
	}
	if len(cpyBasics.Uints) != len(test.Uints) {
		t.Errorf("Uints: len was %d; want %d", len(cpyBasics.Uints), len(test.Uints))
		goto Uint8s
	}
	for i, v := range test.Uints {
		if v != cpyBasics.Uints[i] {
			t.Errorf("Uints: got %v at index %d of the copy; want %v", cpyBasics.Uints[i], i, v)
		}
	}

Uint8s:
	if cpyBasics.Uint8 != test.Uint8 {
		t.Errorf("Uint8: got %v; want %v", cpyBasics.Uint8, test.Uint8)
	}

	if hasSameDataPointer(test.Uint8s, cpyBasics.Uint8s) {
		t.Error("Uint8s: address of copy was the same as original; they should be different")
		goto Uint16s
	}
	if len(cpyBasics.Uint8s) != len(test.Uint8s) {
		t.Errorf("Uint8s: len was %d; want %d", len(cpyBasics.Uint8s), len(test.Uint8s))
		goto Uint16s
	}
	for i, v := range test.Uint8s {
		if v != cpyBasics.Uint8s[i] {
			t.Errorf("Uint8s: got %v at index %d of the copy; want %v", cpyBasics.Uint8s[i], i, v)
		}
	}

Uint16s:
	if cpyBasics.Uint16 != test.Uint16 {
		t.Errorf("Uint16: got %v; want %v", cpyBasics.Uint16, test.Uint16)
	}

	if hasSameDataPointer(test.Uint16s, cpyBasics.Uint16s) {
		t.Error("Uint16s: address of copy was the same as original; they should be different")
		goto Uint32s
	}
	if len(cpyBasics.Uint16s) != len(test.Uint16s) {
		t.Errorf("Uint16s: len was %d; want %d", len(cpyBasics.Uint16s), len(test.Uint16s))
		goto Uint32s
	}
	for i, v := range test.Uint16s {
		if v != cpyBasics.Uint16s[i] {
			t.Errorf("Uint16s: got %v at index %d of the copy; want %v", cpyBasics.Uint16s[i], i, v)
		}
	}

Uint32s:
	if cpyBasics.Uint32 != test.Uint32 {
		t.Errorf("Uint32: got %v; want %v", cpyBasics.Uint32, test.Uint32)
	}

	if hasSameDataPointer(test.Uint32s, cpyBasics.Uint32s) {
		t.Error("Uint32s: address of copy was the same as original; they should be different")
		goto Uint64s
	}
	if len(cpyBasics.Uint32s) != len(test.Uint32s) {
		t.Errorf("Uint32s: len was %d; want %d", len(cpyBasics.Uint32s), len(test.Uint32s))
		goto Uint64s
	}
	for i, v := range test.Uint32s {
		if v != cpyBasics.Uint32s[i] {
			t.Errorf("Uint32s: got %v at index %d of the copy; want %v", cpyBasics.Uint32s[i], i, v)
		}
	}

Uint64s:
	if cpyBasics.Uint64 != test.Uint64 {
		t.Errorf("Uint64: got %v; want %v", cpyBasics.Uint64, test.Uint64)
	}

	if hasSameDataPointer(test.Uint64s, cpyBasics.Uint64s) {
		t.Error("Uint64s: address of copy was the same as original; they should be different")
		goto Float32s
	}
	if len(cpyBasics.Uint64s) != len(test.Uint64s) {
		t.Errorf("Uint64s: len was %d; want %d", len(cpyBasics.Uint64s), len(test.Uint64s))
		goto Float32s
	}
	for i, v := range test.Uint64s {
		if v != cpyBasics.Uint64s[i] {
			t.Errorf("Uint64s: got %v at index %d of the copy; want %v", cpyBasics.Uint64s[i], i, v)
		}
	}

Float32s:
	if cpyBasics.Float32 != test.Float32 {
		t.Errorf("Float32: got %v; want %v", cpyBasics.Float32, test.Float32)
	}

	if hasSameDataPointer(test.Float32s, cpyBasics.Float32s) {
		t.Error("Float32s: address of copy was the same as original; they should be different")
		goto Float64s
	}
	if len(cpyBasics.Float32s) != len(test.Float32s) {
		t.Errorf("Float32s: len was %d; want %d", len(cpyBasics.Float32s), len(test.Float32s))
		goto Float64s
	}
	for i, v := range test.Float32s {
		if v != cpyBasics.Float32s[i] {
			t.Errorf("Float32s: got %v at index %d of the copy; want %v", cpyBasics.Float32s[i], i, v)
		}
	}

Float64s:
	if cpyBasics.Float64 != test.Float64 {
		t.Errorf("Float64: got %v; want %v", cpyBasics.Float64, test.Float64)
	}

	if hasSameDataPointer(test.Float64s, cpyBasics.Float64s) {
		t.Error("Float64s: address of copy was the same as original; they should be different")
		goto Complex64s
	}
	if len(cpyBasics.Float64s) != len(test.Float64s) {
		t.Errorf("Float64s: len was %d; want %d", len(cpyBasics.Float64s), len(test.Float64s))
		goto Complex64s
	}
	for i, v := range test.Float64s {
		if v != cpyBasics.Float64s[i] {
			t.Errorf("Float64s: got %v at index %d of the copy; want %v", cpyBasics.Float64s[i], i, v)
		}
	}

Complex64s:
	if cpyBasics.Complex64 != test.Complex64 {
		t.Errorf("Complex64: got %v; want %v", cpyBasics.Complex64, test.Complex64)
	}

	if hasSameDataPointer(test.Complex64s, cpyBasics.Complex64s) {
		t.Error("Complex64s: address of copy was the same as original; they should be different")
		goto Complex128s
	}
	if len(cpyBasics.Complex64s) != len(test.Complex64s) {
		t.Errorf("Complex64s: len was %d; want %d", len(cpyBasics.Complex64s), len(test.Complex64s))
		goto Complex128s
	}
	for i, v := range test.Complex64s {
		if v != cpyBasics.Complex64s[i] {
			t.Errorf("Complex64s: got %v at index %d of the copy; want %v", cpyBasics.Complex64s[i], i, v)
		}
	}

Complex128s:
	if cpyBasics.Complex128 != test.Complex128 {
		t.Errorf("Complex128s: got %v; want %v", cpyBasics.Complex128s, test.Complex128s)
	}

	if hasSameDataPointer(test.Complex128s, cpyBasics.Complex128s) {
		t.Error("Complex128s: address of copy was the same as original; they should be different")
		goto Interfaces
	}
	if len(cpyBasics.Complex128s) != len(test.Complex128s) {
		t.Errorf("Complex128s: len was %d; want %d", len(cpyBasics.Complex128s), len(test.Complex128s))
		goto Interfaces
	}
	for i, v := range test.Complex128s {
		if v != cpyBasics.Complex128s[i] {
			t.Errorf("Complex128s: got %v at index %d of the copy; want %v", cpyBasics.Complex128s[i], i, v)
		}
	}

Interfaces:
	if cpyBasics.Interface != test.Interface {
		t.Errorf("Interface: got %v; want %v", cpyBasics.Interface, test.Interface)
	}

	if hasSameDataPointer(test.Interfaces, cpyBasics.Interfaces) {
		t.Error("Interfaces: address of copy was the same as original; they should be different")
		return
	}
	if len(cpyBasics.Interfaces) != len(test.Interfaces) {
		t.Errorf("Interfaces: len was %d; want %d", len(cpyBasics.Interfaces), len(test.Interfaces))
		return
	}
	for i, v := range test.Interfaces {
		if v != cpyBasics.Interfaces[i] {
			t.Errorf("Interfaces: got %v at index %d of the copy; want %v", cpyBasics.Interfaces[i], i, v)
		}
	}
}

type A struct {
	Int    int
	String string
	UintSl []uint
	NilSl  []string
	Map    map[string]int
	MapB   map[string]*B
	SliceB []B
	B
	T time.Time
}

type B struct {
	Vals []string
}

type C struct {
	Vals []string
}

var AStruct = A{
	Int:    42,
	String: "Konichiwa",
	UintSl: []uint{0, 1, 2, 3},
	Map:    map[string]int{"a": 1, "b": 2},
	MapB: map[string]*B{
		"hi":  &B{Vals: []string{"hello", "bonjour"}},
		"bye": &B{Vals: []string{"good-bye", "au revoir"}},
	},
	SliceB: []B{
		B{Vals: []string{"Ciao", "Aloha"}},
	},
	B: B{Vals: []string{"42"}},
	T: time.Now(),
}

func TestStructA(t *testing.T) {
	cpy, err := Copy(&AStruct)
	if err != nil {
		t.Fatal(err)
	}
	cpyA := cpy.(*A)
	if cpyA == &AStruct {
		t.Error("expected copy to have a different address than the original; it was the same")
		return
	}
	if cpyA.Int != AStruct.Int {
		t.Errorf("A.Int: got %v, want %v", cpyA.Int, AStruct.Int)
	}
	if cpyA.String != AStruct.String {
		t.Errorf("A.String: got %v; want %v", cpyA.String, AStruct.String)
	}
	if hasSameDataPointer(AStruct.UintSl, cpyA.UintSl) {
		t.Error("A.Uintsl: expected the copies address to be different; it wasn't")
		goto NilSl
	}
	if len(cpyA.UintSl) != len(AStruct.UintSl) {
		t.Errorf("A.UintSl: got len of %d, want %d", len(cpyA.UintSl), len(AStruct.UintSl))
		goto NilSl
	}
	for i, v := range AStruct.UintSl {
		if cpyA.UintSl[i] != v {
			t.Errorf("A.UintSl %d: got %d, want %d", i, cpyA.UintSl[i], v)
		}
	}

NilSl:
	if cpyA.NilSl != nil {
		t.Error("A.NilSl: expected slice to be nil, it wasn't")
	}

	if *(*uintptr)(unsafe.Pointer(&cpyA.Map)) == *(*uintptr)(unsafe.Pointer(&AStruct.Map)) {
		t.Error("A.Map: expected the copy's address to be different; it wasn't")
		goto AMapB
	}
	if len(cpyA.Map) != len(AStruct.Map) {
		t.Errorf("A.Map: got len of %d, want %d", len(cpyA.Map), len(AStruct.Map))
		goto AMapB
	}
	for k, v := range AStruct.Map {
		val, ok := cpyA.Map[k]
		if !ok {
			t.Errorf("A.Map: expected the key %s to exist in the copy, it didn't", k)
			continue
		}
		if val != v {
			t.Errorf("A.Map[%s]: got %d, want %d", k, val, v)
		}
	}

AMapB:
	if *(*uintptr)(unsafe.Pointer(&cpyA.MapB)) == *(*uintptr)(unsafe.Pointer(&AStruct.MapB)) {
		t.Error("A.MapB: expected the copy's address to be different; it wasn't")
		goto ASliceB
	}
	if len(cpyA.MapB) != len(AStruct.MapB) {
		t.Errorf("A.MapB: got len of %d, want %d", len(cpyA.MapB), len(AStruct.MapB))
		goto ASliceB
	}
	for k, v := range AStruct.MapB {
		val, ok := cpyA.MapB[k]
		if !ok {
			t.Errorf("A.MapB: expected the key %s to exist in the copy, it didn't", k)
			continue
		}
		if unsafe.Pointer(val) == unsafe.Pointer(v) {
			t.Errorf("A.MapB[%s]: expected the addresses of the values to be different; they weren't", k)
			continue
		}
		// the slice headers should point to different data
		if hasSameDataPointer(v.Vals, val.Vals) {
			t.Errorf("%s: expected B's SliceHeaders to point to different Data locations; they did not.", k)
			continue
		}
		for i, vv := range v.Vals {
			if vv != val.Vals[i] {
				t.Errorf("A.MapB[%s].Vals[%d]: got %s want %s", k, i, vv, val.Vals[i])
			}
		}
	}

ASliceB:
	if hasSameDataPointer(AStruct.SliceB, cpyA.SliceB) {
		t.Error("A.SliceB: expected the copy's address to be different; it wasn't")
		goto B
	}

	if len(AStruct.SliceB) != len(cpyA.SliceB) {
		t.Errorf("A.SliceB: got length of %d; want %d", len(cpyA.SliceB), len(AStruct.SliceB))
		goto B
	}

	for i := range AStruct.SliceB {
		if unsafe.Pointer(&AStruct.SliceB[i]) == unsafe.Pointer(&cpyA.SliceB[i]) {
			t.Errorf("A.SliceB[%d]: expected them to have different addresses, they didn't", i)
			continue
		}
		if hasSameDataPointer(AStruct.SliceB[i].Vals, cpyA.SliceB[i].Vals) {
			t.Errorf("A.SliceB[%d]: expected B.Vals SliceHeader.Data to point to different locations; they did not", i)
			continue
		}
		if len(AStruct.SliceB[i].Vals) != len(cpyA.SliceB[i].Vals) {
			t.Errorf("A.SliceB[%d]: expected B's vals to have the same length, they didn't", i)
			continue
		}
		for j, val := range AStruct.SliceB[i].Vals {
			if val != cpyA.SliceB[i].Vals[j] {
				t.Errorf("A.SliceB[%d].Vals[%d]: got %v; want %v", i, j, cpyA.SliceB[i].Vals[j], val)
			}
		}
	}
B:
	if unsafe.Pointer(&AStruct.B) == unsafe.Pointer(&cpyA.B) {
		t.Error("A.B: expected them to have different addresses, they didn't")
		goto T
	}
	if hasSameDataPointer(AStruct.B.Vals, cpyA.B.Vals) {
		t.Error("A.B.Vals: expected the SliceHeaders.Data to point to different locations; they didn't")
		goto T
	}
	if len(AStruct.B.Vals) != len(cpyA.B.Vals) {
		t.Error("A.B.Vals: expected their lengths to be the same, they weren't")
		goto T
	}
	for i, v := range AStruct.B.Vals {
		if v != cpyA.B.Vals[i] {
			t.Errorf("A.B.Vals[%d]: got %s want %s", i, cpyA.B.Vals[i], v)
		}
	}
T:
	if fmt.Sprintf("%p", &AStruct.T) == fmt.Sprintf("%p", &cpyA.T) {
		t.Error("A.T: expected them to have different addresses, they didn't")
		return
	}
	if AStruct.T != cpyA.T {
		t.Errorf("A.T: got %v, want %v", cpyA.T, AStruct.T)
	}
}

type Unexported struct {
	A  string
	B  int
	aa string
	bb int
	cc []int
	dd map[string]string
}

func TestUnexportedFields(t *testing.T) {
	u := &Unexported{
		A:  "A",
		B:  42,
		aa: "aa",
		bb: 42,
		cc: []int{1, 2, 3},
		dd: map[string]string{"hello": "bonjour"},
	}
	cpy, err := Copy(u)
	if err != nil {
		t.Fatal(err)
	}
	cpyUnexported := cpy.(*Unexported)
	if cpyUnexported == u {
		t.Error("expected addresses to be different, they weren't")
		return
	}
	if u.A != cpyUnexported.A {
		t.Errorf("Unexported.A: got %s want %s", cpyUnexported.A, u.A)
	}
	if u.B != cpyUnexported.B {
		t.Errorf("Unexported.A: got %d want %d", cpyUnexported.B, u.B)
	}
	if cpyUnexported.aa != "" {
		t.Errorf("Unexported.aa: unexported field should not be set, it was set to %s", cpyUnexported.aa)
	}
	if cpyUnexported.bb != 0 {
		t.Errorf("Unexported.bb: unexported field should not be set, it was set to %d", cpyUnexported.bb)
	}
	if cpyUnexported.cc != nil {
		t.Errorf("Unexported.cc: unexported field should not be set, it was set to %#v", cpyUnexported.cc)
	}
	if cpyUnexported.dd != nil {
		t.Errorf("Unexported.dd: unexported field should not be set, it was set to %#v", cpyUnexported.dd)
	}
}

// Note: this test will fail until https://github.com/golang/go/issues/15716 is
// fixed and the version it is part of gets released.
type T struct {
	time.Time
}

func TestTimeCopy(t *testing.T) {
	tests := []struct {
		Y    int
		M    time.Month
		D    int
		h    int
		m    int
		s    int
		nsec int
		TZ   string
	}{
		{2016, time.July, 4, 23, 11, 33, 3000, "America/New_York"},
		{2015, time.October, 31, 9, 44, 23, 45935, "UTC"},
		{2014, time.May, 5, 22, 01, 50, 219300, "Europe/Prague"},
	}

	for i, test := range tests {
		l, err := time.LoadLocation(test.TZ)
		if err != nil {
			t.Errorf("%d: unexpected error: %s", i, err)
			continue
		}
		var x T
		x.Time = time.Date(test.Y, test.M, test.D, test.h, test.m, test.s, test.nsec, l)
		c, err := Copy(x)
		if err != nil {
			t.Fatal(err)
		}
		cpy := c.(T)
		if fmt.Sprintf("%p", &cpy) == fmt.Sprintf("%p", &x) {
			t.Errorf("%d: expected the copy to have a different address than the original value; they were the same: %p %p", i, &cpy, &x)
			continue
		}
		if x.UnixNano() != cpy.UnixNano() {
			t.Errorf("%d: nanotime: got %v; want %v", i, cpy.UnixNano(), x.UnixNano())
			continue
		}
		if x.Location() != cpy.Location() {
			t.Errorf("%d: location: got %q; want %q", i, cpy.Location(), x.Location())
		}
	}
}

func TestPointerToStruct(t *testing.T) {
	type Foo struct {
		Bar int
	}

	f := &Foo{Bar: 42}
	cpy, err := Copy(f)
	if err != nil {
		t.Fatal(err)
	}
	if f == cpy {
		t.Errorf("expected copy to point to a different location: orig: %p; copy: %p", f, cpy)
	}
	if !reflect.DeepEqual(f, cpy) {
		t.Errorf("expected the copy to be equal to the original (except for memory location); it wasn't: got %#v; want %#v", f, cpy)
	}
}

func TestIssue9(t *testing.T) {
	// simple pointer copy
	x := 42
	testA := map[string]*int{
		"a": nil,
		"b": &x,
	}
	copyA1, err := Copy(testA)
	if err != nil {
		t.Fatal(err)
	}
	copyA := copyA1.(map[string]*int)
	if unsafe.Pointer(&testA) == unsafe.Pointer(&copyA) {
		t.Fatalf("expected the map pointers to be different: testA: %v\tcopyA: %v", unsafe.Pointer(&testA), unsafe.Pointer(&copyA))
	}
	if !reflect.DeepEqual(testA, copyA) {
		t.Errorf("got %#v; want %#v", copyA, testA)
	}
	if testA["b"] == copyA["b"] {
		t.Errorf("entries for 'b' pointed to the same address: %v; expected them to point to different addresses", testA["b"])
	}

	// map copy
	type Foo struct {
		Alpha string
	}

	type Bar struct {
		Beta  string
		Gamma int
		Delta *Foo
	}

	type Biz struct {
		Epsilon map[int]*Bar
	}

	testB := Biz{
		Epsilon: map[int]*Bar{
			0: &Bar{},
			1: &Bar{
				Beta:  "don't panic",
				Gamma: 42,
				Delta: nil,
			},
			2: &Bar{
				Beta:  "sudo make me a sandwich.",
				Gamma: 11,
				Delta: &Foo{
					Alpha: "okay.",
				},
			},
		},
	}

	copyB1, err := Copy(testB)
	if err != nil {
		t.Fatal(err)
	}
	copyB := copyB1.(Biz)
	if !reflect.DeepEqual(testB, copyB) {
		t.Errorf("got %#v; want %#v", copyB, testB)
		return
	}

	// check that the maps point to different locations
	if unsafe.Pointer(&testB.Epsilon) == unsafe.Pointer(&copyB.Epsilon) {
		t.Fatalf("expected the map pointers to be different; they weren't: testB: %v\tcopyB: %v", unsafe.Pointer(&testB.Epsilon), unsafe.Pointer(&copyB.Epsilon))
	}

	for k, v := range testB.Epsilon {
		if v == nil && copyB.Epsilon[k] == nil {
			continue
		}
		if v == nil && copyB.Epsilon[k] != nil {
			t.Errorf("%d: expected copy of a nil entry to be nil; it wasn't: %#v", k, copyB.Epsilon[k])
			continue
		}
		if v == copyB.Epsilon[k] {
			t.Errorf("entries for '%d' pointed to the same address: %v; expected them to point to different addresses", k, v)
			continue
		}
		if v.Beta != copyB.Epsilon[k].Beta {
			t.Errorf("%d.Beta: got %q; want %q", k, copyB.Epsilon[k].Beta, v.Beta)
		}
		if v.Gamma != copyB.Epsilon[k].Gamma {
			t.Errorf("%d.Gamma: got %d; want %d", k, copyB.Epsilon[k].Gamma, v.Gamma)
		}
		if v.Delta == nil && copyB.Epsilon[k].Delta == nil {
			continue
		}
		if v.Delta == nil && copyB.Epsilon[k].Delta != nil {
			t.Errorf("%d.Delta: got %#v; want nil", k, copyB.Epsilon[k].Delta)
		}
		if v.Delta == copyB.Epsilon[k].Delta {
			t.Errorf("%d.Delta: expected the pointers to be different, they were the same: %v", k, v.Delta)
			continue
		}
		if v.Delta.Alpha != copyB.Epsilon[k].Delta.Alpha {
			t.Errorf("%d.Delta.Foo: got %q; want %q", k, v.Delta.Alpha, copyB.Epsilon[k].Delta.Alpha)
		}
	}

	// test that map keys are deep copied
	testC := map[*Foo][]string{
		&Foo{Alpha: "Henry Dorsett Case"}: []string{
			"Cutter",
		},
		&Foo{Alpha: "Molly Millions"}: []string{
			"Rose Kolodny",
			"Cat Mother",
			"Steppin' Razor",
		},
	}

	copyC1, err := Copy(testC)
	if err != nil {
		t.Fatal(err)
	}
	copyC := copyC1.(map[*Foo][]string)
	if unsafe.Pointer(&testC) == unsafe.Pointer(&copyC) {
		t.Fatalf("expected the map pointers to be different; they weren't: testB: %v\tcopyB: %v", unsafe.Pointer(&testB.Epsilon), unsafe.Pointer(&copyB.Epsilon))
	}

	// make sure the lengths are the same
	if len(testC) != len(copyC) {
		t.Fatalf("got len %d; want %d", len(copyC), len(testC))
	}

	// check that everything was deep copied: since the key is a pointer, we check to
	// see if the pointers are different but the values being pointed to are the same.
	for k, v := range testC {
		for kk, vv := range copyC {
			if *kk == *k {
				if kk == k {
					t.Errorf("key pointers should be different: orig: %p; copy: %p", k, kk)
				}
				// check that the slices are the same but different
				if !reflect.DeepEqual(v, vv) {
					t.Errorf("expected slice contents to be the same; they weren't: orig: %v; copy: %v", v, vv)
				}

				if hasSameDataPointer(v, vv) {
					t.Errorf("expected the SliceHeaders.Data to point to different locations; they didn't: %v", (*reflect.SliceHeader)(unsafe.Pointer(&v)).Data)
				}
				break
			}
		}
	}

	type Bizz struct {
		*Foo
	}

	testD := map[Bizz]string{
		Bizz{&Foo{"Neuromancer"}}: "Rio",
		Bizz{&Foo{"Wintermute"}}:  "Berne",
	}
	copyD1, err := Copy(testD)
	if err != nil {
		t.Fatal(err)
	}
	copyD := copyD1.(map[Bizz]string)
	if len(copyD) != len(testD) {
		t.Fatalf("copy had %d elements; expected %d", len(copyD), len(testD))
	}

	for k, v := range testD {
		var found bool
		for kk, vv := range copyD {
			if reflect.DeepEqual(k, kk) {
				found = true
				// check that Foo points to different locations
				if unsafe.Pointer(k.Foo) == unsafe.Pointer(kk.Foo) {
					t.Errorf("Expected Foo to point to different locations; they didn't: orig: %p; copy %p", k.Foo, kk.Foo)
					break
				}
				if *k.Foo != *kk.Foo {
					t.Errorf("Expected copy of the key's Foo field to have the same value as the original, it wasn't: orig: %#v; copy: %#v", k.Foo, kk.Foo)
				}
				if v != vv {
					t.Errorf("Expected the values to be the same; the weren't: got %v; want %v", vv, v)
				}
			}
		}
		if !found {
			t.Errorf("expected key %v to exist in the copy; it didn't", k)
		}
	}
}

type I struct {
	A string
}

func (i *I) DeepCopy() interface{} {
	return &I{A: "custom copy"}
}

type NestI struct {
	I *I
}

func TestInterface(t *testing.T) {
	i := &I{A: "A"}
	copied, err := Copy(i)
	if err != nil {
		t.Fatal(err)
	}
	copiedI := copied.(*I)
	if copiedI.A != "custom copy" {
		t.Errorf("expected value %v, but it's %v", "custom copy", copiedI.A)
	}
	// check for nesting values
	ni := &NestI{I: &I{A: "A"}}
	copiedNest, err := Copy(ni)
	if err != nil {
		t.Fatal(err)
	}
	copiedNestI := copiedNest.(*NestI)
	if copiedNestI.I.A != "custom copy" {
		t.Errorf("expected value %v, but it's %v", "custom copy", copiedNestI.I.A)
	}
}

func TestArray(t *testing.T) {
	type A [3][][]float64

	var a = A{
		{
			{
				1.0,
				2.0,
				3.0,
			},
		},
	}

	distination, err := Copy(a)
	if err != nil {
		t.Fatal(err)
	}
	a[0][0][0] = 4.0
	a[0][0][1] = 5.0
	a[0][0][2] = 6.0

	if distination.(A)[0][0][0] == a[0][0][0] {
		t.Fatal("Isn't deepcopied!", distination, a)
	}

	type I struct {
		I1 string
	}

	type B [3][]*I

	i := I{I1: "123"}

	var b = B{
		{
			&i,
		},
	}

	cb, err := Copy(b)
	if err != nil {
		t.Fatal(err)
	}
	i2 := I{I1: "456"}
	b[0][0] = &i2

	if cb.(B)[0][0] == b[0][0] {
		t.Fatal("Isn't deepcopied!", distination, a)
	}
}

func TestDeepcopy2(t *testing.T) {
	{
		type a [1][]int

		var source = a{
			{
				1,
			},
		}

		destination, err := Copy(source)
		if err != nil {
			t.Fatal(err)
		}
		if destination.(a)[0][0] != source[0][0] {
			t.Errorf("expected value %v, but it's %v", source[0][0], destination.(a)[0][0])
		}

		source[0][0] = 0
		if destination.(a)[0][0] == source[0][0] {
			t.Error("Fatal Isn't deepcopied!", destination, source)
		}
	}

	{
		type a [3][][]float64

		var source = a{
			{
				{
					1.0,
					2.0,
					3.0,
				},
			},
		}
		distination, err := Copy(source)
		if err != nil {
			t.Fatal(err)
		}
		if distination.(a)[0][0][0] != source[0][0][0] {
			t.Errorf("expected value %v, but it's %v", source[0][0][0], distination.(a)[0][0][0])
		}

		source[0][0][0] = 4.0
		source[0][0][1] = 5.0
		source[0][0][2] = 6.0

		if distination.(a)[0][0][0] == source[0][0][0] {
			t.Error("Fatal Isn't deepcopied!", distination, source)
		}
	}
}

func TestArrayOfSomething(t *testing.T) {
	verifyCases := map[string]struct {
		modifyAndVerify func(t *testing.T)
	}{
		// failed because the map(underlying is a pointer) will be copied directly
		"array of map": {
			modifyAndVerify: func(t *testing.T) {
				origin := [1]map[string]int{
					{
						"1": 1,
					},
				}
				copied, err := Copy(origin)
				if err != nil {
					t.Fatal(err)
				}
				assert.NotSame(t, &origin, &copied)
				assert.Equal(t, origin, copied)
				origin[0]["1"] = 999
				assert.Equal(t, 1, copied.([1]map[string]int)[0]["1"])
			},
		},
		// failed because the pointer of map will be copied directly
		"array of *map": {
			modifyAndVerify: func(t *testing.T) {
				origin := [1]*map[string]int{
					{
						"1": 1,
					},
				}
				copied, err := Copy(origin)
				if err != nil {
					t.Fatal(err)
				}
				assert.NotSame(t, origin, copied)
				assert.Equal(t, origin, copied)
				(*origin[0])["1"] = 999
				assert.Equal(t, 1, (*copied.([1]*map[string]int)[0])["1"])
			},
		},
		// failed because the pointer of int will be copied directly
		"array of *int": {
			modifyAndVerify: func(t *testing.T) {
				intp := func(i int) *int {
					return &i
				}
				arrayOfInt := [3]*int{intp(1), intp(2)}
				copied1, err := Copy(&arrayOfInt)
				if err != nil {
					t.Fatal(err)
				}
				copied := copied1.(*[3]*int)
				assert.NotSame(t, &arrayOfInt, copied)
				assert.NotSame(t, arrayOfInt[0], copied[0])
				assert.Equal(t, arrayOfInt[0], copied[0])
				arrayOfInt[0] = intp(999)
				assert.Equal(t, 1, *copied[0])
			},
		},
		// succeed because int will be copied by value
		"array of int": {
			modifyAndVerify: func(t *testing.T) {
				arrayOfInt := [3]int{1, 2}
				copied, err := Copy(&arrayOfInt)
				if err != nil {
					t.Fatal(err)
				}
				assert.NotSame(t, &arrayOfInt, copied)
				assert.Equal(t, arrayOfInt[0], copied.(*[3]int)[0])
				arrayOfInt[0] = 999
				assert.Equal(t, 1, copied.(*[3]int)[0])
			},
		},
	}

	for key, tt := range verifyCases {
		t.Run(key, tt.modifyAndVerify)
	}
}

func TestMustCopy(t *testing.T) {
	type TB struct {
		B1 int64
	}
	type T1 struct {
		A  string
		TB *TB
	}

	oldT := &T1{
		A: "A",
		TB: &TB{
			B1: 0,
		},
	}
	newT := MustCopy(oldT).(*T1)
	oldT.TB.B1 = 1
	fmt.Println(newT.TB.B1)
}
