package gcfg

import (
	
	
	
	
	
	
	
	

	
	
)

type tag struct {
	ident   string
	intMode string
}

func ( string) tag {
	 := tag{}
	 := strings.Split(, ",")
	.ident = [0]
	for ,  := range [1:] {
		if strings.HasPrefix(, "int=") {
			.intMode = [len("int="):]
		}
	}
	return 
}

func ( reflect.Value,  string) (reflect.Value, tag) {
	var  string
	,  := utf8.DecodeRuneInString()
	if unicode.IsLetter() && !unicode.IsLower() && !unicode.IsUpper() {
		 = "X"
	}
	 += strings.Replace(, "-", "_", -1)
	,  := .Type().FieldByNameFunc(func( string) bool {
		if !.FieldByName().CanSet() {
			return false
		}
		,  := .Type().FieldByName()
		 := newTag(.Tag.Get("gcfg"))
		if .ident != "" {
			return strings.EqualFold(.ident, )
		}
		return strings.EqualFold(, )
	})
	if ! {
		return reflect.Value{}, tag{}
	}
	return .FieldByName(.Name), newTag(.Tag.Get("gcfg"))
}

type setter func(destp interface{}, blank bool, val string, t tag) error

var errUnsupportedType = fmt.Errorf("unsupported type")
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")

var setters = []setter{
	typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
}

func ( interface{},  bool,  string,  tag) error {
	,  := .(textUnmarshaler)
	if ! {
		return errUnsupportedType
	}
	if  {
		return errBlankUnsupported
	}
	return .UnmarshalText([]byte())
}

func ( interface{},  bool,  string,  tag) error {
	if  {
		reflect.ValueOf().Elem().Set(reflect.ValueOf(true))
		return nil
	}
	,  := types.ParseBool()
	if  == nil {
		reflect.ValueOf().Elem().Set(reflect.ValueOf())
	}
	return 
}

func ( string) types.IntMode {
	var  types.IntMode
	if strings.ContainsAny(, "dD") {
		 |= types.Dec
	}
	if strings.ContainsAny(, "hH") {
		 |= types.Hex
	}
	if strings.ContainsAny(, "oO") {
		 |= types.Oct
	}
	return 
}

var typeModes = map[reflect.Type]types.IntMode{
	reflect.TypeOf(int(0)):    types.Dec | types.Hex,
	reflect.TypeOf(int8(0)):   types.Dec | types.Hex,
	reflect.TypeOf(int16(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int32(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int64(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint(0)):   types.Dec | types.Hex,
	reflect.TypeOf(uint8(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
use default mode (allow dec/hex/oct) for uintptr type
	reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
}

func ( reflect.Type) types.IntMode {
	,  := typeModes[]
	if ! {
		 = types.Dec | types.Hex | types.Oct
	}
	return 
}

func ( interface{},  bool,  string,  tag) error {
	if  {
		return errBlankUnsupported
	}
	 := intMode(.intMode)
	if  == 0 {
		 = intModeDefault(reflect.TypeOf().Elem())
	}
	return types.ParseInt(, , )
}

func ( interface{},  bool,  string,  tag) error {
	if  {
		return errBlankUnsupported
	}
	,  := .(*string)
	if ! {
		return errUnsupportedType
	}
	* = 
	return nil
}

var kindSetters = map[reflect.Kind]setter{
	reflect.String:  stringSetter,
	reflect.Bool:    boolSetter,
	reflect.Int:     intSetter,
	reflect.Int8:    intSetter,
	reflect.Int16:   intSetter,
	reflect.Int32:   intSetter,
	reflect.Int64:   intSetter,
	reflect.Uint:    intSetter,
	reflect.Uint8:   intSetter,
	reflect.Uint16:  intSetter,
	reflect.Uint32:  intSetter,
	reflect.Uint64:  intSetter,
	reflect.Uintptr: intSetter,
}

var typeSetters = map[reflect.Type]setter{
	reflect.TypeOf(big.Int{}): intSetter,
}

func ( interface{},  bool,  string,  tag) error {
	 := reflect.ValueOf().Type().Elem()
	,  := typeSetters[]
	if ! {
		return errUnsupportedType
	}
	return (, , , )
}

func ( interface{},  bool,  string,  tag) error {
	 := reflect.ValueOf().Type().Elem().Kind()
	,  := kindSetters[]
	if ! {
		return errUnsupportedType
	}
	return (, , , )
}

func ( interface{},  bool,  string,  tag) error {
	if  {
		return errBlankUnsupported
	}
	return types.ScanFully(, , 'v')
}

func ( *warnings.Collector,  string,  reflect.Value,
	 := reflect.New()
	 := "default-" + 
	,  := fieldFold(, )
	var  error
	if .IsValid() {
		 := bytes.NewBuffer(nil)
		 := gob.NewEncoder()
		if  = .Collect(.EncodeValue());  != nil {
			return , 
		}
		 := gob.NewDecoder(bytes.NewReader(.Bytes()))
		if  = .Collect(.DecodeValue(.Elem()));  != nil {
			return , 
		}
	}
	return , nil
}

func ( *warnings.Collector,  interface{}, , ,  string,
	 := reflect.ValueOf()
	if .Kind() != reflect.Ptr || .Elem().Kind() != reflect.Struct {
		panic(fmt.Errorf("config must be a pointer to a struct"))
	}
	 := .Elem()
	,  := fieldFold(, )
	if !.IsValid() {
		 := extraData{section: }
		return .Collect()
	}
	 := .Kind() == reflect.Map
	if  !=  {
		return nil
	}
	if  {
		 := .Type()
		if .Key().Kind() != reflect.String ||
			.Elem().Kind() != reflect.Ptr ||
			.Elem().Elem().Kind() != reflect.Struct {
			panic(fmt.Errorf("map field for section must have string keys and "+
				" pointer-to-struct values: section %q", ))
		}
		if .IsNil() {
			.Set(reflect.MakeMap())
		}
		 := reflect.ValueOf()
		 := .MapIndex()
		if !.IsValid() {
			 := .Type().Elem().Elem()
			var  error
			if ,  = newValue(, , , );  != nil {
				return 
			}
			.SetMapIndex(, )
		}
		 = .Elem()
	} else if .Kind() != reflect.Struct {
		panic(fmt.Errorf("field for section must be a map or a struct: "+
			"section %q", ))
	} else if  != "" {
		 := extraData{section: , subsection: &}
		return .Collect()
Empty name is a special value, meaning that only the section/subsection object is to be created, with no values set.
	if  == "" {
		return nil
	}
	,  := fieldFold(, )
	if !.IsValid() {
		var  error
		if  {
			 = extraData{section: , subsection: &, variable: &}
		} else {
			 = extraData{section: , variable: &}
		}
		return .Collect()
vVal is either single-valued var, or newly allocated value within multi-valued var
multi-value if unnamed slice type
	 := .Type().Name() == "" && .Kind() == reflect.Slice ||
		.Type().Name() == "" && .Kind() == reflect.Ptr && .Type().Elem().Name() == "" && .Type().Elem().Kind() == reflect.Slice
	if  && .Kind() == reflect.Ptr {
		if .IsNil() {
			.Set(reflect.New(.Type().Elem()))
		}
		 = .Elem()
	}
	if  &&  {
		.Set(reflect.Zero(.Type()))
		return nil
	}
	if  {
		 = reflect.New(.Type().Elem()).Elem()
	} else {
		 = 
	}
	 := .Type().Name() == "" && .Type().Kind() == reflect.Ptr
vAddr is address of value to set (dereferenced & allocated as needed)
	var  reflect.Value
	switch {
	case :
		 = reflect.New(.Type().Elem())
	case  && !:
		 = 
	default:
		 = .Addr()
	}
	 := .Interface()
	,  := error(nil), false
	for ,  := range setters {
		 = (, , , )
		if  == nil {
			 = true
			break
		}
		if  != errUnsupportedType {
			return 
		}
	}
in case all setters returned errUnsupportedType
		return 
	}
	if  { // set reference if it was dereferenced and newly allocated
		.Set()
	}
	if  { // append if multi-valued
		.Set(reflect.Append(, ))
	}
	return nil