package vals

import (
	
	

	
)

// StructMap may be implemented by a struct to mark the struct as a "struct
// map", which causes Elvish to treat it like a read-only map. Each exported,
// named field and getter method (a method taking no argument and returning one
// value) becomes a field of the map, with the name mapped to dash-case.
//
// The following operations are derived for structmaps: Kind, Repr, Hash, Len,
// Index, HasKey and IterateKeys.
//
// Example:
//
//   type someStruct struct {
//       FooBar int
//       lorem  string
//   }
//
//   func (someStruct) IsStructMap() { }
//
//   func (s SomeStruct) Ipsum() string { return s.lorem }
//
//   func (s SomeStruct) OtherMethod(int) { }
//
// An instance of someStruct behaves like a read-only map with 3 fields:
// foo-bar, lorem and ipsum.
type StructMap interface{ IsStructMap() }

// PseudoStructMap may be implemented by a type to derive the Repr, Index,
// HasKey and IterateKeys operations from the struct map returned by the Fields
// method.
type PseudoStructMap interface{ Fields() StructMap }

// Keeps cached information about a structMap.
type structMapInfo struct {
	filledFields int
	plainFields  int
	// Dash-case names for all fields. The first plainFields elements
	// corresponds to all the plain fields, while the rest corresponds to getter
	// fields. May contain empty strings if the corresponding field is not
	// reflected onto the structMap (i.e. unexported fields, unexported methods
	// and non-getter methods).
	fieldNames []string
}

var structMapInfos sync.Map

// Gets the structMapInfo associated with a type, caching the result.
func ( reflect.Type) structMapInfo {
	if ,  := structMapInfos.Load();  {
		return .(structMapInfo)
	}
	 := makeStructMapInfo()
	structMapInfos.Store(, )
	return 
}

func ( reflect.Type) structMapInfo {
	 := .NumField()
	 := .NumMethod()
	 := make([]string, +)
	 := 0

	for  := 0;  < ; ++ {
		 := .Field()
		if .PkgPath == "" && !.Anonymous {
			[] = strutil.CamelToDashed(.Name)
			++
		}
	}

	for  := 0;  < ; ++ {
		 := .Method()
		if .PkgPath == "" && .Type.NumIn() == 1 && .Type.NumOut() == 1 {
			[+] = strutil.CamelToDashed(.Name)
			++
		}
	}

	return structMapInfo{, , }
}

type structMapIterator struct {
	info structMapInfo
	i    int
}

func ( reflect.Type) *structMapIterator {
	return &structMapIterator{getStructMapInfo(), -1}
}

func ( *structMapIterator) () bool {
	 := .info.fieldNames
	if .i >= len() {
		return false
	}

	.i++
	for .i < len() && [.i] == "" {
		.i++
	}
	return .i < len()
}

func ( *structMapIterator) ( reflect.Value) (string, interface{}) {
	 := .info.fieldNames[.i]
	if .i < .info.plainFields {
		return , .Field(.i).Interface()
	}
	 := .Method(.i - .info.plainFields)
	return , .Call(nil)[0].Interface()
}