package utils

import (
	"reflect"
	"strings"
)

// 得到一个结构体（或指向结构体的指针）中其成员(tag指定，缺省是字段名)的路径（嵌入结构体或指向结构指针会被展开）
func GetStructObjNameIndex(obj interface{}, tag string) map[string][]int {
	objType := reflect.TypeOf(obj)
	return GetStructNameIndex(objType, tag)
}

func GetStructNameIndex(objType reflect.Type, tag string) (nameIndex map[string][]int) {
	if objType.Kind() == reflect.Ptr {
		objType = objType.Elem()
	}
	if objType.Kind() != reflect.Struct {
		panic("obj must be struct obj or pointer to struct of obj!")
	}
	nameIndex = make(map[string][]int)
	for i := 0; i < objType.NumField(); i++ {
		structField := objType.Field(i)
		fieldType := structField.Type

		if IndirectType(fieldType).Kind() == reflect.Struct && IsFieldEmbedded(&structField) { // 嵌入
			subNameIndex := GetStructNameIndex(fieldType, tag)
			for k, v := range subNameIndex {
				nameIndex[k] = append([]int{i}, v...)
			}
		} else {
			tagName := structField.Name
			if tag != "" {
				tagName2 := strings.Split(structField.Tag.Get(tag), ",")[0]
				if tagName2 == "-" {
					tagName = "" // 忽略
				} else if tagName2 != "" {
					tagName = tagName2
				}
			}
			if tagName != "" {
				nameIndex[tagName] = []int{i}
			}
		}
	}
	return nameIndex
}

// 判断成员是否是嵌入的，依据是成员名与类型名相同
func IsFieldEmbedded(structField *reflect.StructField) bool {
	return structField.Name == IndirectType(structField.Type).Name()
}

func IndirectType(objType reflect.Type) reflect.Type {
	if objType.Kind() == reflect.Ptr {
		objType = objType.Elem()
	}
	return objType
}

func GetConcretValue(value reflect.Value) reflect.Value {
	for {
		if value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
			value = value.Elem()
		} else {
			break
		}
	}
	return value
}

func Interface2Slice(value interface{}) (retVal []interface{}) {
	if value != nil {
		typeInfo := reflect.TypeOf(value)
		if typeInfo.Kind() != reflect.Slice {
			panic("list must be slice type!")
		}
		valueInfo := reflect.ValueOf(value)
		retVal = make([]interface{}, valueInfo.Len())
		for i := 0; i < valueInfo.Len(); i++ {
			retVal[i] = valueInfo.Index(i).Interface()
		}
	}
	return retVal
}
