package machine

import (
	
	
	
)

// ///// ///// /////

// ///// STATES & SCHEMA

// ///// ///// /////

type StatesBase struct {
	// Exception is the only built-in state and mean a global error. All errors
	// have to [State.Require] the Exception state. If [Machine.PanicToErr] is
	// true, Exception will receive it.
	Exception   string
	names       S
	groups      map[string][]int
	groupsOrder []string
}

var _ States = &StatesBase{}

func ( *StatesBase) () S {
	return .names
}

func ( *StatesBase) () (map[string][]int, []string) {
	return .groups, .groupsOrder
}

func ( *StatesBase) ( S) {
	.names = slicesUniq()
}

func ( *StatesBase) ( map[string][]int,  []string) {
	.groups = 
	.groupsOrder = 
}

type States interface {
	// Names returns the state names of the state machine.
	Names() S
	StateGroups() (map[string][]int, []string)
	SetNames(S)
	SetStateGroups(map[string][]int, []string)
}

func [ States]( )  {
	// read and assign names of all the embedded structs
	 := S{}
	 := map[string][]int{}
	 := reflect.ValueOf(&).Elem()
	 := []string{}
	parseStateNames(, &, "self", , &)
	.SetNames()
	.SetStateGroups(, )

	return 
}

func parseStateNames(
	 reflect.Value,  *S,  string,  map[string][]int,
	 *[]string,
) {
	if  != "StatesBase" {
		[] = []int{}
		* = append(*, )
	}
	 := .Type()
	for  := 0;  < .NumField(); ++ {

		 := .Field()
		 := .Field()
		 := .Type.Kind()

		if .Anonymous &&  == reflect.Ptr &&
			// embedded struct (inherit states)
			.Type.Elem().Kind() == reflect.Struct {

			if .IsNil() {
				 := reflect.New(.Type.Elem())
				.Set()
			}
			(.Elem(), , .Name, , )

		} else if .CanSet() &&  == reflect.String {
			// local state name
			.SetString(.Name)
			if !slices.Contains(*, .Name) {
				if  != "StatesBase" {
					[] = append([], len(*))
				}
				* = append(*, .Name)
			}
		}
	}
}

func [ any]( ,  ...any)  {
	// init nil embeds
	 := reflect.ValueOf(&).Elem()
	initNilEmbeds()

	// assign values from parent mixins into the local instance
	for  := range  {
		copyFields([], &)
	}

	return 
}

func initNilEmbeds( reflect.Value) {
	 := .Type()
	for  := 0;  < .NumField(); ++ {

		 := .Field()
		 := .Field()
		 := .Type.Kind()

		if .Anonymous &&  == reflect.Ptr &&
			.Type.Elem().Kind() == reflect.Struct {

			if .IsNil() {
				 := reflect.New(.Type.Elem())
				.Set()
			}
			(.Elem())
		}
	}
}

func copyFields(,  interface{}) {
	 := reflect.ValueOf()
	 := reflect.ValueOf()

	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}

	for  := 0;  < .NumField(); ++ {
		 := .Type().Field().Name
		 := .Field()
		 := .FieldByName()

		if .Kind() == reflect.Struct {
			(.Addr().Interface(), .Addr().Interface())
		} else {
			if .CanSet() {
				.Set()
			}
		}
	}
}

// ///// ///// /////

// ///// ARGS

// ///// ///// /////

// AT represents typed arguments of pkg/machine, extracted from Event.Args
// via ParseArgs, or created manually to for Pass.
type AT struct {
	Err          error
	ErrTrace     string
	Panic        *ExceptionArgsPanic
	TargetStates S
	CalledStates S
	TimeBefore   Time
	TimeAfter    Time
	Event        *Event
	// MutDone chan gets closed by the machine once it's processed. Can cause chan
	// leaks when misused. Only for Can* checks.
	CheckDone *CheckDone
}

type CheckDone struct {
	// TODO close these on dispose and deadline
	Ch chan struct{}
	// Was the mutation canceled?
	Canceled bool
}

const (
	argErr       = "_am_err"
	argErrTrace  = "_am_errTrace"
	argPanic     = "_am_panic"
	argCheckDone = "_am_checkDone"
)

// ParseArgs extracts AT from A.
func ( A) *AT {
	 := &AT{}

	if ,  := [argErr];  {
		.Err = .(error)
	}
	if ,  := [argErrTrace];  {
		.ErrTrace = .(string)
	}
	if ,  := [argPanic];  {
		.Panic = .(*ExceptionArgsPanic)
	}
	if ,  := [argCheckDone];  {
		.CheckDone = .(*CheckDone)
	}

	return 
}

// Pass prepares A from AT, to pass to further mutations.
func ( *AT) A {
	 := A{}

	if .Err != nil {
		[argErr] = .Err
	}
	if .ErrTrace != "" {
		[argErrTrace] = .ErrTrace
	}
	if .Panic != nil {
		[argPanic] = .Panic
	}
	if .CheckDone != nil {
		[argCheckDone] = .CheckDone
	}

	return 
}

// PassMerge prepares A from AT and existing A, to pass to further
// mutations.
func ( A,  *AT) A {
	var  A
	if  == nil {
		 = A{}
	} else {
		 = maps.Clone()
	}

	// unmarshal
	for ,  := range Pass() {
		[] = 
	}

	return 
}