func ValueType(n *Node) string {
	if n.IsTime() {
		return "time"
	}
	if n.IsDuration() {
		return "duration"
	}
	if n.IsStruct() {
		return n.ValueType()
	}
	kind := n.FieldValue.Kind()
	suffix := ""
	if n.IsSlice() {
		suffix = "s"
		baseType := reflect.TypeOf(n.FieldValue.Interface()).Elem()
		kind = baseType.Kind()
	}
	switch kind {
	case reflect.String:
		return "string" + suffix
	case reflect.Bool:
		return "bool" + suffix
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return "int" + suffix
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return "uint" + suffix
	case reflect.Float32, reflect.Float64:
		return "float" + suffix
	default:
		return ""
	}
}
ValueType returns a simple string representation of the type.
package node
import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
	"github.com/pcelvng/go-config/util"
)
type Nodes struct {
	nodesMap   map[string]*Node  
	nodesSlice []*Node           
	v          interface{}       
}
func (ns *Nodes) Map() map[string]*Node {
	return ns.nodesMap
}
func (ns *Nodes) List() []*Node {
	return ns.nodesSlice
}
func (ns *Nodes) StructPtr() interface{} {
	return ns.v
}
func (ns *Nodes) SetTag(fieldName, key, value string) error {
	n, ok := ns.nodesMap[fieldName]
	if !ok {
		return errors.New("unable to set field tag: no field found by name '" + fieldName + "'")
	}
	n.SetTag(key, value)
	return nil
}
type Node struct {
	Prefix string  
	FieldValue reflect.Value
	Field      reflect.StructField
	Index int
	tag map[string]string
	meta map[string]string
}
func (n *Node) FieldName() string {
	return n.Field.Name
}
func (n *Node) FullName() string {
	if n.Prefix == "" {
		return n.FieldName()
	}
	return n.Prefix + "." + n.FieldName()
}
func (n *Node) ParentName() string {
	return n.Prefix
}
func (n *Node) ParentsNames() []string {
	names := make([]string, 0)
	if n.ParentName() == "" {
		return names
	}
	lineage := strings.Split(n.ParentName(), ".")
	for i := 0; i < len(lineage); i++ {
		names = append(names, strings.Join(lineage[:i+1], "."))
	}
	return names
}
func (n *Node) ValueType() string {
	return n.FieldValue.Type().String()
}
func (n *Node) Kind() reflect.Kind {
	return n.FieldValue.Kind()
}
func (n *Node) SetTag(key, value string) {
	if value == "" || key == "" {
		return
	}
	n.tag[key] = value
}
func (n *Node) GetTag(key string) string {
	if v, ok := n.tag[key]; ok {
		return v
	}
	return n.Field.Tag.Get(key)
}
func (n *Node) SetMeta(key, value string) {
	if value == "" || key == "" {
		return
	}
	n.meta[key] = value
}
func (n *Node) GetMeta(key string) string {
	return n.meta[key]
}
func (n *Node) GetBoolTag(key string) bool {
	bv, _ := strconv.ParseBool(n.GetTag(key))
	return bv
}
func (n *Node) SetBoolTag(key string, value bool) {
	n.SetTag(key, strconv.FormatBool(value))
}
func (n *Node) IsStruct() bool {
	return n.Kind() == reflect.Struct
}
func (n *Node) IsString() bool {
	return n.Kind() == reflect.String
}
func (n *Node) IsDuration() bool {
	return n.ValueType() == "time.Duration"
}
func (n *Node) IsTime() bool {
	if n == nil {
		return false
	}
	return n.ValueType() == "time.Time"
}
func (n *Node) IsSlice() bool {
	return n.Kind() == reflect.Slice
}
func (n *Node) IsStringSlice() bool {
	if n.Kind() == reflect.Slice {
		baseType := reflect.TypeOf(n.FieldValue.Interface()).Elem()
		return baseType.Kind() == reflect.String
	}
	return false
}
func (n *Node) IsBool() bool {
	return n.Kind() == reflect.Bool
}
func fieldString(value reflect.Value) string {
	switch value.Kind() {
	case reflect.String:
		return value.String()
	case reflect.Bool:
		return strconv.FormatBool(value.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if value.Type().String() == "time.Duration" {
			return value.Interface().(time.Duration).String()
		}
		return strconv.FormatInt(value.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(value.Uint(), 10)
	case reflect.Float32, reflect.Float64:
		f := fmt.Sprintf("%f", value.Float())  
		f = strings.TrimRight(f, "0")
		return strings.TrimRight(f, ".")  
	default:
		panic(fmt.Sprintf("unexpected king to convert to string '%v'", value.Kind().String()))
	}
}
func (n *Node) String() string {
	return fieldString(n.FieldValue)
}
func (n *Node) SliceString() []string {
	if !n.IsSlice() {
		panic(fmt.Sprintf("field value '%v' must be a slice and instead was '%v'",
			n.FullName(),
			n.FieldValue.Kind().String()),
		)
	}
	items := make([]string, 0)
	for i := 0; i < n.FieldValue.Len(); i++ {
		itemValue := n.FieldValue.Index(i)
		if itemValue.Kind() == reflect.Ptr {
			itemValue = itemValue.Elem()
		}
		items = append(items, fieldString(itemValue))
	}
	return items
}
func (n *Node) TimeString(timeFmt string) string {
	if !n.IsTime() {
		panic(fmt.Sprintf("field '%v' must be a 'time.Time' value type but is '%v' instead",
			n.FullName(),
			n.ValueType(),
		))
	}
	timeFmt = NormTimeFormat(timeFmt)
	tv := n.FieldValue.Interface().(time.Time)
	if tv.IsZero() {
		return ""
	}
	return tv.Format(timeFmt)
}
func (n *Node) SetFieldValue(s string) error {
	switch n.Kind() {
	case reflect.Ptr:
		panic(fmt.Sprintf("node '%s' type is a pointer", n.FullName()))
	case reflect.Slice:
		panic(fmt.Sprintf("node '%s' type is a slice - call SetSlice method instead", n.FullName()))
	case reflect.Struct:
		panic(fmt.Sprintf("node '%s' type is a struct - call SetStruct method instead", n.FullName()))
	}
	return setField(n.FieldValue, s)
}
func (n *Node) SetSlice(vals []string) error {
	if !n.IsSlice() {
		panic(fmt.Sprintf("field value '%v' must be a slice and instead was '%v'",
			n.FullName(),
			n.FieldValue.Kind().String()),
		)
	}
	fValue := n.FieldValue
	baseType := reflect.TypeOf(fValue.Interface()).Elem()
	slice := reflect.MakeSlice(fValue.Type(), 0, len(vals))
	for _, v := range vals {
		baseValue := reflect.New(baseType).Elem()
		err := setField(baseValue, v)
		if err != nil {
			return err
		}
		slice = reflect.Append(slice, baseValue)
	}
	fValue.Set(slice)
	return nil
}
func setField(value reflect.Value, s string) error {
	switch value.Kind() {
	case reflect.String:
		value.SetString(s)
	case reflect.Bool:
		switch strings.ToLower(s) {
		case "true":
			value.SetBool(true)
		case "false", "":
			value.SetBool(false)
		default:
			return fmt.Errorf("cannot assign '%v' to bool type", s)
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if value.Type().String() == "time.Duration" {
			d, err := time.ParseDuration(s)
			if err != nil {
				return err
			}
			value.SetInt(int64(d))
			return nil
		}
		i, err := strconv.ParseInt(s, 10, 0)
		if err != nil {
			return err
		}
		value.SetInt(i)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		i, err := strconv.ParseUint(s, 10, 0)
		if err != nil {
			return err
		}
		value.SetUint(i)
	case reflect.Float32, reflect.Float64:
		f, err := strconv.ParseFloat(s, 0)
		if err != nil {
			return err
		}
		value.SetFloat(f)
	case reflect.Ptr:
		z := reflect.New(value.Type().Elem())
		err := setField(z.Elem(), s)
		if err != nil {
			return err
		}
		value.Set(z)
	default:
		return fmt.Errorf("unsupported type '%v'", value.Kind())
	}
	return nil
}
func (n *Node) SetStruct(v interface{}) {
	if n.FieldValue.Kind() != reflect.Struct {
		panic(
			fmt.Sprintf("attempting to assign struct to '%s' on non-struct type '%s'",
				n.FullName(), n.ValueType()),
		)
	}
	n.FieldValue.Set(reflect.ValueOf(v))
}
var timeFormats = map[string]string{
	"ANSIC":       time.ANSIC,
	"UnixDate":    time.UnixDate,
	"RubyDate":    time.RubyDate,
	"RFC822":      time.RFC822,
	"RFC822Z":     time.RFC822Z,
	"RFC850":      time.RFC850,
	"RFC1123":     time.RFC1123,
	"RFC1123Z":    time.RFC1123Z,
	"RFC3339":     time.RFC3339,
	"RFC3339Nano": time.RFC3339Nano,
	"Kitchen":     time.Kitchen,
	"Stamp":       time.Stamp,
	"StampMilli":  time.StampMilli,
	"StampMicro":  time.StampMicro,
	"StampNano":   time.StampNano,
}
func NormTimeFormat(timeFmt string) string {
	if timeFmt == "" {
		return time.RFC3339
	}
	mappedFmt, ok := timeFormats[timeFmt]
	if ok {
		return mappedFmt
	}
	return timeFmt
}
func (n *Node) SetTime(tv, timeFmt string) (usedFmt string, err error) {
	timeFmt = NormTimeFormat(timeFmt)
	if !n.IsTime() {
		return timeFmt, errors.New("cannot set value because it is not of type time.Time")
	}
	t, err := time.Parse(timeFmt, tv)
	if err != nil {
		return timeFmt, err
	}
	n.SetStruct(t)
	return timeFmt, nil
}
func MakeAllNodes(o Options, vs ...interface{}) []*Nodes {
	allNodes := make([]*Nodes, 0)
	for _, v := range vs {
		nodes := MakeNodes(o, v)
		allNodes = append(allNodes, nodes)
	}
	return allNodes
}
func MakeNodes(o Options, v interface{}) *Nodes {
	_, err := util.IsStructPointer(v)
	if err != nil {
		panic(err.Error())
	}
	return makeNodes("", v, o)
}
type Options struct {
	NoFollow []string
	IgnoreTypes []string
}
func makeNodes(prefix string, v interface{}, options Options) (nodes *Nodes) {
	nodes = &Nodes{
		nodesMap:   make(map[string]*Node),
		nodesSlice: make([]*Node, 0),
		v:          v,
	}
	vStruct := reflect.ValueOf(v).Elem()
	for i := 0; i < vStruct.NumField(); i++ {
		rawField := vStruct.Field(i)
		if !rawField.CanSet() {  
			continue
		}
		field := rawField
		if rawField.Kind() == reflect.Ptr {
			if rawField.IsNil() {
				z := reflect.New(rawField.Type().Elem())
				rawField.Set(z)
			}
			field = reflect.Indirect(rawField)
		}
		if isIgnoredKind(field.Kind()) {
			continue
		}
		if isIgnoredType(field.Type().String(), options.IgnoreTypes) {
			continue
		}
		if field.Kind() == reflect.Slice {
			baseType := reflect.TypeOf(field.Interface()).Elem()
			if baseType.Kind() == reflect.Ptr {
				baseType = baseType.Elem()
			}
			if !isBasicType(baseType.Kind()) {
				continue
			}
		}
		node := &Node{
			Prefix:     prefix,
			FieldValue: field,
			Field:      vStruct.Type().Field(i),
			Index:      i,
			tag:        make(map[string]string),
			meta:       make(map[string]string),
		}
		addNode(nodes, node)
		if field.Kind() == reflect.Struct && followStruct(node.ValueType(), options.NoFollow) {
			mergeNodes(nodes, makeNodes(node.FullName(), field.Addr().Interface(), options))
		}
	}
	return nodes
}
func addNode(nodes *Nodes, n *Node) *Nodes {
	nodes.nodesMap[n.FullName()] = n
	nodes.nodesSlice = append(nodes.nodesSlice, n)
	return nodes
}
var ignoreList = []reflect.Kind{
	reflect.Array,
	reflect.Func,
	reflect.Chan,
	reflect.Complex64,
	reflect.Complex128,
	reflect.Interface,
	reflect.Map,
}
func isIgnoredKind(fieldKind reflect.Kind) bool {
	for _, v := range ignoreList {
		if fieldKind == v {
			return true
		}
	}
	return false
}
func isIgnoredType(fieldType string, ignoredTypes []string) bool {
	for _, v := range ignoredTypes {
		if fieldType == v {
			return true
		}
	}
	return false
}
var basicTypes = []reflect.Kind{
	reflect.String,
	reflect.Bool,
	reflect.Int,
	reflect.Int8,
	reflect.Int16,
	reflect.Int32,
	reflect.Int64,
	reflect.Uint,
	reflect.Uint8,
	reflect.Uint16,
	reflect.Uint32,
	reflect.Uint64,
	reflect.Float32,
	reflect.Float64,
}
func isBasicType(t reflect.Kind) bool {
	for _, bt := range basicTypes {
		if t == bt {
			return true
		}
	}
	return false
}
func mergeNodes(nodesA, nodesB *Nodes) *Nodes {
	for k, v := range nodesB.nodesMap {
		nodesA.nodesMap[k] = v
	}
	nodesA.nodesSlice = append(nodesA.nodesSlice, nodesB.nodesSlice...)
	return nodesA
}
func followStruct(typeName string, noFollow []string) bool {
	for _, fv := range noFollow {
		if typeName == fv {
			return false
		}
	}
	return true
}
func Parent(child *Node, nodes map[string]*Node) (parent *Node) {
	parent, _ = nodes[child.ParentName()]
	return parent
}
func Parents(child *Node, nodes map[string]*Node) (parents []*Node) {
	for _, name := range child.ParentsNames() {
		ancestor, ok := nodes[name]
		if !ok {
			panic(fmt.Sprintf("ancestor '%v' of '%v' should exist", name, child.FullName()))
		}
		parents = append(parents, ancestor)
	}
	return parents
}
type FieldString func(n *Node, heritage []*Node) (name string)