// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package deep

import (
	"reflect"
)

// Copy returns a deep copy of the source object.
func Copy(src interface{}) interface{} {

	if src == nil {
		return nil
	}

	prime := reflect.ValueOf(src)

	clone := reflect.New(prime.Type()).Elem()

	copy(prime, clone)

	return clone.Interface()

}

func copy(prime, clone reflect.Value) {

	switch prime.Kind() {

	case reflect.Ptr:
		value := prime.Elem()
		if !value.IsValid() {
			return
		}
		clone.Set(reflect.New(value.Type()))
		copy(value, clone.Elem())

	case reflect.Interface:
		if prime.IsNil() {
			return
		}
		value := prime.Elem()
		alike := reflect.New(value.Type()).Elem()
		copy(value, alike)
		clone.Set(alike)

	case reflect.Struct:
		for i := 0; i < prime.NumField(); i++ {
			if prime.Type().Field(i).PkgPath != "" {
				continue
			}
			copy(prime.Field(i), clone.Field(i))
		}

	case reflect.Slice:
		clone.Set(reflect.MakeSlice(prime.Type(), prime.Len(), prime.Cap()))
		for i := 0; i < prime.Len(); i++ {
			copy(prime.Index(i), clone.Index(i))
		}

	case reflect.Map:
		clone.Set(reflect.MakeMap(prime.Type()))
		for _, key := range prime.MapKeys() {
			value := prime.MapIndex(key)
			alike := reflect.New(value.Type()).Elem()
			copy(value, alike)
			clone.SetMapIndex(key, alike)
		}

	default:
		clone.Set(prime)

	}

}