// Copyright (c) 2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package dig

import (
	
	
	

	
	
)

const (
	_optionalTag         = "optional"
	_nameTag             = "name"
	_ignoreUnexportedTag = "ignore-unexported"
)

// Unique identification of an object in the graph.
type key struct {
	t reflect.Type

	// Only one of name or group will be set.
	name  string
	group string
}

func ( key) () string {
	if .name != "" {
		return fmt.Sprintf("%v[name=%q]", .t, .name)
	}
	if .group != "" {
		return fmt.Sprintf("%v[group=%q]", .t, .group)
	}
	return .t.String()
}

// Option configures a Container.
type Option interface {
	applyOption(*Container)
}

// Container is a directed acyclic graph of types and their dependencies.
// A Container is the root Scope that represents the top-level scoped
// directed acyclic graph of the dependencies.
type Container struct {
	// this is the "root" Scope that represents the
	// root of the scope tree.
	scope *Scope
}

// containerWriter provides write access to the Container's underlying data
// store.
type containerWriter interface {
	// setValue sets the value with the given name and type in the container.
	// If a value with the same name and type already exists, it will be
	// overwritten.
	setValue(name string, t reflect.Type, v reflect.Value)

	// setDecoratedValue sets a decorated value with the given name and type
	// in the container. If a decorated value with the same name and type already
	// exists, it will be overwritten.
	setDecoratedValue(name string, t reflect.Type, v reflect.Value)

	// submitGroupedValue submits a value to the value group with the provided
	// name.
	submitGroupedValue(name string, t reflect.Type, v reflect.Value)

	// submitDecoratedGroupedValue submits a decorated value to the value group
	// with the provided name.
	submitDecoratedGroupedValue(name string, t reflect.Type, v reflect.Value)
}

// containerStore provides access to the Container's underlying data store.
type containerStore interface {
	containerWriter

	// Adds a new graph node to the Container
	newGraphNode(w interface{}, orders map[*Scope]int)

	// Returns a slice containing all known types.
	knownTypes() []reflect.Type

	// Retrieves the value with the provided name and type, if any.
	getValue(name string, t reflect.Type) (v reflect.Value, ok bool)

	// Retrieves a decorated value with the provided name and type, if any.
	getDecoratedValue(name string, t reflect.Type) (v reflect.Value, ok bool)

	// Retrieves all values for the provided group and type.
	//
	// The order in which the values are returned is undefined.
	getValueGroup(name string, t reflect.Type) []reflect.Value

	// Retrieves all decorated values for the provided group and type, if any.
	getDecoratedValueGroup(name string, t reflect.Type) (reflect.Value, bool)

	// Returns the providers that can produce a value with the given name and
	// type.
	getValueProviders(name string, t reflect.Type) []provider

	// Returns the providers that can produce values for the given group and
	// type.
	getGroupProviders(name string, t reflect.Type) []provider

	// Returns the providers that can produce a value with the given name and
	// type across all the Scopes that are in effect of this containerStore.
	getAllValueProviders(name string, t reflect.Type) []provider

	// Returns the decorator that can decorate values for the given name and
	// type.
	getValueDecorator(name string, t reflect.Type) (decorator, bool)

	// Reutrns the decorator that can decorate values for the given group and
	// type.
	getGroupDecorator(name string, t reflect.Type) (decorator, bool)

	// Reports a list of stores (starting at this store) up to the root
	// store.
	storesToRoot() []containerStore

	createGraph() *dot.Graph

	// Returns invokerFn function to use when calling arguments.
	invoker() invokerFn

	// Returns a clock to use
	clock() digclock.Clock
}

// New constructs a Container.
func ( ...Option) *Container {
	 := newScope()
	 := &Container{scope: }

	for ,  := range  {
		.applyOption()
	}
	return 
}

// DeferAcyclicVerification is an Option to override the default behavior
// of container.Provide, deferring the dependency graph validation to no longer
// run after each call to container.Provide. The container will instead verify
// the graph on first `Invoke`.
//
// Applications adding providers to a container in a tight loop may experience
// performance improvements by initializing the container with this option.
func () Option {
	return deferAcyclicVerificationOption{}
}

type deferAcyclicVerificationOption struct{}

func (deferAcyclicVerificationOption) () string {
	return "DeferAcyclicVerification()"
}

func (deferAcyclicVerificationOption) ( *Container) {
	.scope.deferAcyclicVerification = true
}

// RecoverFromPanics is an [Option] to recover from panics that occur while
// running functions given to the container. When set, recovered panics
// will be placed into a [PanicError], and returned at the invoke callsite.
// See [PanicError] for an example on how to handle panics with this option
// enabled, and distinguish them from errors.
func () Option {
	return recoverFromPanicsOption{}
}

type recoverFromPanicsOption struct{}

func (recoverFromPanicsOption) () string {
	return "RecoverFromPanics()"
}

func (recoverFromPanicsOption) ( *Container) {
	.scope.recoverFromPanics = true
}

// Changes the source of randomness for the container.
//
// This will help provide determinism during tests.
func setRand( *rand.Rand) Option {
	return setRandOption{r: }
}

type setRandOption struct{ r *rand.Rand }

func ( setRandOption) () string {
	return fmt.Sprintf("setRand(%p)", .r)
}

func ( setRandOption) ( *Container) {
	.scope.rand = .r
}

// Changes the source of time for the container.
func setClock( digclock.Clock) Option {
	return setClockOption{c: }
}

type setClockOption struct{ c digclock.Clock }

func ( setClockOption) () string {
	return fmt.Sprintf("setClock(%v)", .c)
}

func ( setClockOption) ( *Container) {
	.scope.clockSrc = .c
}

// DryRun is an Option which, when set to true, disables invocation of functions supplied to
// Provide and Invoke. Use this to build no-op containers.
func ( bool) Option {
	return dryRunOption()
}

type dryRunOption bool

func ( dryRunOption) () string {
	return fmt.Sprintf("DryRun(%v)", bool())
}

func ( dryRunOption) ( *Container) {
	if  {
		.scope.invokerFn = dryInvoker
	} else {
		.scope.invokerFn = defaultInvoker
	}
}

// invokerFn specifies how the container calls user-supplied functions.
type invokerFn func(fn reflect.Value, args []reflect.Value) (results []reflect.Value)

func defaultInvoker( reflect.Value,  []reflect.Value) []reflect.Value {
	return .Call()
}

// Generates zero values for results without calling the supplied function.
func dryInvoker( reflect.Value,  []reflect.Value) []reflect.Value {
	 := .Type()
	 := make([]reflect.Value, .NumOut())
	for  := 0;  < .NumOut(); ++ {
		[] = reflect.Zero(.Type().Out())
	}

	return 
}

// String representation of the entire Container
func ( *Container) () string {
	return .scope.String()
}

// Scope creates a child scope of the Container with the given name.
func ( *Container) ( string,  ...ScopeOption) *Scope {
	return .scope.Scope(, ...)
}

type byTypeName []reflect.Type

func ( byTypeName) () int {
	return len()
}

func ( byTypeName) ( int,  int) bool {
	return fmt.Sprint([]) < fmt.Sprint([])
}

func ( byTypeName) ( int,  int) {
	[], [] = [], []
}

func shuffledCopy( *rand.Rand,  []reflect.Value) []reflect.Value {
	 := make([]reflect.Value, len())
	for ,  := range .Perm(len()) {
		[] = []
	}
	return 
}