package types

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	
	
	
	
	am 
	
	ssdbg 
	
)

var ss = ssdbg.DebuggerStates

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

// ///// PARAMS

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

// Params are CLI params for am-dbg, also used as internal state via Debugger.Params.
//
//nolint:lll
type Params struct {

	// line args

	MachUrl string `arg:"positional" help:"Machine URL to connect to"`

	// main params

	ListenAddr      string   `arg:"-l,--listen-addr" default:"localhost:6831" help:"Host and port for the debugger to listen on"`
	OutputDir       string   `arg:"-d,--dir" default:"." help:"Output directory for generated files"`
	CleanOnConnect  bool     `arg:"--clean-on-connect" default:"true" help:"Clean up disconnected clients on the 1st connection"`
	ImportData      string   `arg:"-i,--import-data" help:"Import an exported gob.br file"`
	FwdData         []string `arg:"-f,--fwd-data,separate" help:"Forward incoming data to other instances (repeatable)"`
	SelectConnected bool     `arg:"-c,--select-connected" help:"Select the newly connected machine, if no other is connected"`

	// minor params

	EnableClipboard      bool        `arg:"--enable-clipboard" default:"true" help:"Enable clipboard support"`
	EnableMouse          bool        `arg:"--enable-mouse" default:"true" help:"Enable mouse support"`
	FilterAutoTx         bool        `arg:"--filter-auto" help:"Filter automatic transitions"`
	FilterAutoCanceledTx bool        `arg:"--filter-auto-canceled" help:"Filter automatic canceled transitions"`
	FilterCanceledTx     bool        `arg:"--filter-canceled" help:"Filter canceled transitions"`
	FilterChecks         bool        `arg:"--filter-checks" help:"Filter check (read-only) transitions"`
	FilterDisconn        bool        `arg:"--filter-disconn" help:"Filter disconnected machines"`
	FilterEmptyTx        bool        `arg:"--filter-empty" help:"Filter empty transitions"`
	FilterGroup          bool        `arg:"--filter-group" default:"true" help:"Filter transitions by a selected group"`
	FilterHealthTx       bool        `arg:"--filter-health" help:"Filter health-check transitions"`
	FilterLogLevel       am.LogLevel `arg:"--filter-log-level" default:"2" help:"Filter transitions up to this log level, 0-5 (silent-everything)"`
	FilterRpcMachs       bool        `arg:"--filter-rpc-machs" help:"Filter RPC machines"`
	FilterQueuedTx       bool        `arg:"--filter-queued" help:"Filter queued transitions"`

	OutputCallLog   bool                 `arg:"--output-call-log" help:"Write called handlers as Go code into call-log/{mach-id}/{mtime}.go inside --dir (EXPERIMENTAL)"`
	OutputClients   bool                 `arg:"--output-clients" help:"Write a detailed client list into clients.txt inside --dir"`
	OutputDiagrams  ParamsOutputDiagrams `arg:"--output-diagrams" help:"Enable graph diagrams and set the level of detail for machine diagrams in --dir (0 off, 1-3 on) (EXPERIMENTAL)"`
	OutputDiagGroup ParamsOutDiagGroup   `arg:"--output-diag-group" help:"Only show states from the selected group (valid: hide, skip)" default:"hide"`
	OutputDiagTx    ParamsOutDiagTx      `arg:"--output-diag-tx" help:"Dim states and rels unrelated to a transition (valid: called, changed, touched, relations)" default:"relations"`
	OutputGraph     bool                 `arg:"--output-graph" help:"Write the current network graph as graph.(md|mgml) inside --dir (EXPERIMENTAL)"`
	OutputLog       bool                 `arg:"--output-log" help:"Write the current log buffer to log.md inside --dir"`
	OutputTx        bool                 `arg:"--output-tx" default:"true" help:"Write the current transition with steps into tx.md / d2 / mermaid / txt inside --dir"`

	// ui

	UiMcp bool `arg:"--ui-mcp" help:"Enable MCP server on port --listen-addr +1 (requires --ui-web) (EXPERIMENTAL)" default:"true"`
	UiSsh bool `arg:"--ui-ssh" help:"Enable SSH headless mode on port --listen-addr +2 (EXPERIMENTAL)"`
	UiWeb bool `arg:"--ui-web" default:"true" help:"Start a web server for --dir and diagrams on --listen-addr +1"`

	// view

	PrintVersion    bool                `arg:"--version" help:"Print version and exit"`
	StartupView     string              `arg:"-v,--view" default:"tree-log" help:"Initial view (tree-log, tree-matrix, matrix)"`
	ViewExpandLinks bool                `arg:"--view-expand-links" help:"Expand all tree links" default:"true"`
	ViewLogWrap     bool                `arg:"--view-log-wrap" help:"Wrap log lines"`
	ViewNarrow      bool                `arg:"--view-narrow" help:"Force a narrow view, independently of the viewport size"`
	ViewRain        bool                `arg:"--view-rain" help:"Show the rain view"`
	ViewReader      bool                `arg:"-r,--view-reader" help:"Show the log reader view" default:"true"`
	TailMode        bool                `arg:"--view-tail" default:"true" help:"Show the most recent transition"`
	ViewTheme       string              `arg:"--view-theme" default:"dark" help:"Color theme (dark, light)"`
	ViewTimelines   ParamsViewTimelines `arg:"--view-timelines" default:"1" help:"Number of timelines to show (0-2)"`

	// optimization

	LogOpsTtl time.Duration `arg:"--log-ops-ttl" default:"1h" help:"Max time to live for logs level LogOps"`
	MaxMemMb  int           `arg:"--max-mem" default:"1000" help:"Max memory usage (in MB) to flush old transitions"`

	// self-dbg

	DebugAddr    string `arg:"--dbg-am-dbg-addr" help:"Debug this instance of am-dbg with another one"`
	RaceDetector bool   `arg:"--dbg-go-race" help:"Go race detector is enabled"`
	// Id is the ID of this asyncmachine (for debugging).
	Id string `arg:"--dbg-id" default:"am-dbg" help:"ID of this instance"`
	// LogLevel is the log level of this instance (for debugging).
	LogLevel am.LogLevel `arg:"--dbg-log-level" default:"0" help:"Log level produced by this instance, 0-5 (silent-everything)"`
	DbgOtel  bool        `arg:"--dbg-otel" help:"Enable OpenTelemetry tracing for this instance"`
	ProfSrv  string      `arg:"--dbg-prof-srv" help:"Start pprof server"`
	Repl     bool        `arg:"--dbg-repl" help:"Start a REPL server in --dir"`

	// internal params

	// AddrHttp is the computed HTTP server address (ListenAddr port+1).
	AddrHttp string `arg:"-"`
	// AddrRpc is the RPC listen address (same as ListenAddr).
	AddrRpc string `arg:"-"`
	// AddrSsh is the computed SSH server address (ListenAddr port+2).
	AddrSsh string `arg:"-"`
	// DbgLogger is the file logger for this instance.
	DbgLogger *log.Logger `arg:"-"`
	// Filters is the active set of transition filters.
	Filters *Filters `arg:"-"`
	// Print is the output function for status messages
	Print   func(txt string, args ...any) `arg:"-"`
	ProfCpu bool                          `arg:"-"`
	ProfMem bool                          `arg:"-"`
	// Screen is an optional tcell screen override (tests, SSH).
	Screen tcell.Screen `arg:"-"`
	// Version is the computed build version string.
	Version string `arg:"-"`
}

// -----

// ParamsOutputDiagrams is an enum for --output-diagrams
type ParamsOutputDiagrams enum.Member[int]

// 0, 1, 2, 3
var (
	ParamsOutputDiagramsNone  = ParamsOutputDiagrams{0}
	ParamsOutputDiagramsOne   = ParamsOutputDiagrams{1}
	ParamsOutputDiagramsTwo   = ParamsOutputDiagrams{2}
	ParamsOutputDiagramsThree = ParamsOutputDiagrams{3}

	ParamsOutputDiagramsEnum = enum.New(
		ParamsOutputDiagramsNone,
		ParamsOutputDiagramsOne,
		ParamsOutputDiagramsTwo,
		ParamsOutputDiagramsThree,
	)
)

func ( *ParamsOutputDiagrams) ( []byte) error {
	,  := strconv.Atoi(string())
	if  != nil {
		return fmt.Errorf("invalid value: %s", )
	}
	 := ParamsOutputDiagramsEnum.Parse()
	if  == nil {
		return fmt.Errorf("invalid value: %s", )
	}
	* = *
	return nil
}

// -----

// ParamsOutDiagTx is an enum for --output-diagrams-group
type ParamsOutDiagTx enum.Member[string]

// "", "called", "changed", "touched"
var (
	ParamsOutDiagTxNone      = ParamsOutDiagTx{""}
	ParamsOutDiagTxCalled    = ParamsOutDiagTx{"called"}
	ParamsOutDiagTxMutated   = ParamsOutDiagTx{"mutated"}
	ParamsOutDiagTxTouched   = ParamsOutDiagTx{"touched"}
	ParamsOutDiagTxRelations = ParamsOutDiagTx{"relations"}

	ParamsOutDiagTxEnum = enum.New(ParamsOutDiagTxNone, ParamsOutDiagTxCalled,
		ParamsOutDiagTxMutated, ParamsOutDiagTxTouched, ParamsOutDiagTxRelations)
)

func ( *ParamsOutDiagTx) ( []byte) error {
	 := ParamsOutDiagTxEnum.Parse(string())
	if  == nil {
		return fmt.Errorf("invalid value: %s", )
	}
	* = *
	return nil
}

// -----

// ParamsOutDiagGroup is an enum for --output-diagrams-group
type ParamsOutDiagGroup enum.Member[string]

// "", "hide", "skip"
var (
	ParamsOutDiagGroupNone = ParamsOutDiagGroup{""}
	ParamsOutDiagGroupHide = ParamsOutDiagGroup{"hide"}
	ParamsOutDiagGroupSkip = ParamsOutDiagGroup{"skip"}

	ParamsOutDiagGroupEnum = enum.New(ParamsOutDiagGroupNone,
		ParamsOutDiagGroupHide, ParamsOutDiagGroupSkip)
)

func ( *ParamsOutDiagGroup) ( []byte) error {
	 := ParamsOutDiagGroupEnum.Parse(string())
	if  == nil {
		return fmt.Errorf("invalid value: %s", )
	}
	* = *
	return nil
}

// -----

// ParamsViewTimelines is an enum for --view-timelines
type ParamsViewTimelines enum.Member[int]

// 0, 1, 2
var (
	ParamsViewTimelinesNone = ParamsViewTimelines{0}
	ParamsViewTimelinesOne  = ParamsViewTimelines{1}
	ParamsViewTimelinesTwo  = ParamsViewTimelines{2}

	ParamsViewTimelinesEnum = enum.New(
		ParamsViewTimelinesNone,
		ParamsViewTimelinesOne,
		ParamsViewTimelinesTwo,
	)
)

func ( *ParamsViewTimelines) ( []byte) error {
	,  := strconv.Atoi(string())
	if  != nil {
		return fmt.Errorf("invalid value: %s", )
	}
	 := ParamsViewTimelinesEnum.Parse()
	if  == nil {
		return fmt.Errorf("invalid value: %s", )
	}
	* = *
	return nil
}

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

// ///// FILTERS

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

// Filters controls which transitions and log entries are shown.
type Filters struct {
	SkipCanceledTx     bool
	SkipAutoTx         bool
	SkipAutoCanceledTx bool
	SkipEmptyTx        bool
	SkipHealthTx       bool
	SkipQueuedTx       bool
	SkipOutGroup       bool
	SkipChecks         bool
	LogLevel           am.LogLevel
	SkipRpcMach        bool
}

func ( *Filters) ( *Filters) bool {
	if  == nil ||  == nil {
		return false
	}

	return .SkipCanceledTx == .SkipCanceledTx &&
		.SkipAutoTx == .SkipAutoTx &&
		.SkipAutoCanceledTx == .SkipAutoCanceledTx &&
		.SkipEmptyTx == .SkipEmptyTx &&
		.SkipHealthTx == .SkipHealthTx &&
		.SkipQueuedTx == .SkipQueuedTx &&
		.SkipOutGroup == .SkipOutGroup &&
		.SkipChecks == .SkipChecks &&
		.LogLevel == .LogLevel &&
		.SkipRpcMach == .SkipRpcMach
}

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

// ///// PROFILING

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

// GetLogger returns a file logger, according to params.
func ( *Params,  string) *log.Logger {
	// TODO slog

	if .LogLevel <= 0 {
		return log.Default()
	}
	 := filepath.Join(, "am-dbg.log")

	// file logging
	_ = os.Remove()
	,  := os.OpenFile(, os.O_CREATE|os.O_WRONLY, 0o666)
	if  != nil {
		panic()
	}

	return log.New(, "", log.LstdFlags)
}

func ( *log.Logger,  *Params) {
	if !.ProfMem {
		return
	}

	,  := os.Create("mem.prof")
	if  != nil {
		.Fatal("could not create memory profile: ", )
	}
	defer .Close() // error handling omitted for example

	runtime.GC() // get up-to-date statistics
	if  := pprof.WriteHeapProfile();  != nil {
		.Fatal("could not write memory profile: ", )
	}
}

func ( *log.Logger,  *Params) func() {
	if !.ProfCpu {
		return nil
	}

	,  := os.Create("cpu.prof")
	if  != nil {
		.Fatal("could not create CPU profile: ", )
	}
	if  := pprof.StartCPUProfile();  != nil {
		.Fatal("could not start CPU profile: ", )
	}

	return func() {
		pprof.StopCPUProfile()
		.Close()
	}
}

func ( context.Context,  *log.Logger,  *Params) {
	if .ProfSrv == "" {
		return
	}
	go func() {
		.Println("Starting pprof server on " + .ProfSrv)
		// TODO support ctx cancel
		if  := http.ListenAndServe(.ProfSrv, nil);  != nil {
			.Fatalf("could not start pprof server: %v", )
		}
	}()
}

type MachAddress struct {
	MachId string
	TxId   string
	// TODO remove
	Step int

	// GET param

	MachTime  uint64
	QueueTick uint64
	HumanTime time.Time

	// selected group
	Group string
	// selected state
	State string
}

type MachTime struct {
	Id   string
	Time uint64
}

func ( *MachAddress) () *MachAddress {
	return &MachAddress{
		MachId:   .MachId,
		TxId:     .TxId,
		Step:     .Step,
		MachTime: .MachTime,
	}
}

// StringBase returns a mach URL without GET params (unless no tx ID). Empty
// string ret if no link.
func ( *MachAddress) () string {
	if  == nil || .MachId == "" {
		return ""
	}
	 := "mach://" + .MachId

	if .TxId != "" {
		 += "/" + .TxId
		if .Step != 0 {
			 += fmt.Sprintf("/%d", .Step)
		}
	} else if .MachTime != 0 {
		return  + fmt.Sprintf("/?t=%d", .MachTime)
	} else if .QueueTick != 0 {
		return  + fmt.Sprintf("/?q=%d", .QueueTick)
	} else if !.HumanTime.IsZero() {
		return  + fmt.Sprintf("/?q=%s", .HumanTime)
	} else {
		return ""
	}

	return 
}

// String returns a full mach URL.
func ( *MachAddress) () string {
	 := .StringBase()
	 := []string{}
	if .MachTime != 0 {
		 = append(, fmt.Sprintf("t=%d", .MachTime))
	}
	if .QueueTick != 0 {
		 = append(, fmt.Sprintf("q=%d", .QueueTick))
	}
	if !.HumanTime.IsZero() {
		 = append(, fmt.Sprintf("ht=%s", .HumanTime))
	}
	if .Group != "" {
		 = append(, fmt.Sprintf("group=%s", NormalizeGroupName(.Group)))
	}
	if .State != "" {
		 = append(, fmt.Sprintf("state=%s", .State))
	}

	if len() == 0 {
		return 
	}

	slices.Sort()
	return  + "?" + strings.Join(, "&")
}

func ( string) (*MachAddress, error) {
	// TODO merge parsing with addr bar

	,  := url.Parse()
	if  != nil {
		return nil, 
	} else if .Host == "" {
		return nil, fmt.Errorf("mach ID missing in: %s", )
	}

	 := &MachAddress{
		MachId: .Host,
	}
	 := strings.Split(.Path, "/")
	if len() > 1 {
		.TxId = [1]
	}
	if len() > 2 {
		if ,  := strconv.Atoi([2]);  == nil {
			.Step = 
		}
	}

	// get params
	,  := url.ParseQuery(.RawQuery)
	if  != nil {
		return nil, 
	}
	if  := .Get("t");  != "" {
		if ,  := strconv.ParseUint(, 10, 64);  == nil {
			.MachTime = 
		}
	}
	if  := .Get("q");  != "" {
		if ,  := strconv.ParseUint(, 10, 64);  == nil {
			.QueueTick = 
		}
	}
	if  := .Get("ht");  != "" {
		if ,  := time.Parse(time.RFC3339, );  == nil {
			.HumanTime = 
		}
	}
	.State = .Get("state")
	.Group = .Get("group")

	return , nil
}

type MsgTxParsed struct {
	StatesAdded   []int
	StatesRemoved []int
	StatesTouched []int
	// TimeSum is machine time after this transition.
	TimeSum       uint64
	TimeDiff      uint64
	ReaderEntries []*LogReaderEntryPtr
	// Transitions which reported this one as their source
	Forks       []MachAddress
	ForksLabels []string
	// QueueTick when this tx should be executed
	ResultTick uint64
}

type MsgSchemaParsed struct {
	// TODO split to group ID, labels
	Groups      map[string]am.S
	GroupsOrder []string
	Hash      string
}

type LogReaderEntry struct {
	Kind LogReaderKind
	// states are empty for logReaderWhenQueue
	States []int
	// CreatedAt is machine time when this entry was created
	CreatedAt uint64
	// ClosedAt is human time when this entry was closed, so it can be disposed.
	ClosedAt time.Time

	// per-type fields

	// Pipe is for logReaderPipeIn, logReaderPipeOut
	Pipe am.MutationType
	// Mach is for logReaderPipeIn, logReaderPipeOut, logReaderMention
	Mach string
	// Ticks is for logReaderWhenTime only
	Ticks am.Time
	// Args is for logReaderWhenArgs only
	Args string
	// QueueTick is for logReaderWhenQueue only
	QueueTick int
}

type LogReaderEntryPtr struct {
	TxId     string
	EntryIdx int
}

type LogReaderKind int

const (
	LogReaderCtx LogReaderKind = iota + 1
	LogReaderWhen
	LogReaderWhenNot
	LogReaderWhenTime
	LogReaderWhenArgs
	LogReaderWhenQueue
	LogReaderPipeIn
	LogReaderPipeOut
	// TODO mentions of machine IDs
	// logReaderMention
)

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

// ///// TOOLS

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

// ToolName is a debugger's toolbar button.
type ToolName enum.Member[string]

var (
	ToolFilterCanceledTx = ToolName{"skip-canceled"}
	ToolFilterQueuedTx   = ToolName{"skip-queued"}
	ToolFilterAutoTx     = ToolName{"skip-auto"}
	ToolFilterEmptyTx    = ToolName{"skip-empty"}
	ToolFilterHealth     = ToolName{"skip-health"}
	ToolFilterOutGroup   = ToolName{"skip-outgroup"}
	ToolFilterChecks     = ToolName{"skip-checks"}
	ToolFilterRpcMachs   = ToolName{"skip-rpc-machs"}
	ToolFilterDisconn    = ToolName{"skip-disconn"}
	ToolLogTimestamps    = ToolName{"hide-timestamps"}
	ToolFilterTraces     = ToolName{"hide-traces"}
	ToolNarrowLayout     = ToolName{"narrow-layout"}
	ToolLog              = ToolName{"log"}
	ToolDiagrams         = ToolName{"diagrams"}
	ToolDiagramsTx       = ToolName{"diag-tx"}
	ToolDiagramsGroup    = ToolName{"diag-group"}
	ToolOutputTx         = ToolName{"out-tx"}
	ToolOutputLog        = ToolName{"out-log"}
	ToolCallLog          = ToolName{"call-log"}
	ToolTimelines        = ToolName{"timelines"}
	ToolReader           = ToolName{"reader"}
	ToolRain             = ToolName{"rain"}
	ToolLogWrap          = ToolName{"log-wrap"}
	ToolWeb              = ToolName{"web"}
	ToolHelp             = ToolName{"help"}
	ToolPlay             = ToolName{"play"}
	ToolTail             = ToolName{"tail"}
	ToolPrev             = ToolName{"prev"}
	ToolNext             = ToolName{"next"}
	ToolJumpNext         = ToolName{"jump-next"}
	ToolJumpPrev         = ToolName{"jump-prev"}
	ToolFirst            = ToolName{"first"}
	ToolLast             = ToolName{"last"}
	ToolExpand           = ToolName{"expand"}
	ToolMatrix           = ToolName{"matrix"}
	ToolExport           = ToolName{"export"}
	ToolNextStep         = ToolName{"next-step"}
	ToolPrevStep         = ToolName{"prev-step"}
	ToolPrevClient       = ToolName{"prev-client"}
	ToolNextClient       = ToolName{"next-client"}

	ToolNames = enum.New(
		ToolFilterCanceledTx,
		ToolFilterQueuedTx,
		ToolFilterAutoTx,
		ToolFilterEmptyTx,
		ToolFilterHealth,
		ToolFilterOutGroup,
		ToolFilterChecks,
		ToolFilterDisconn,
		ToolLogTimestamps,
		ToolFilterTraces,
		ToolNarrowLayout,
		ToolLog,
		ToolDiagrams,
		ToolDiagramsTx,
		ToolDiagramsGroup,
		ToolCallLog,
		ToolTimelines,
		ToolReader,
		ToolRain,
		ToolLogWrap,
		ToolWeb,
		ToolHelp,
		ToolPlay,
		ToolTail,
		ToolPrev,
		ToolNext,
		ToolJumpNext,
		ToolJumpPrev,
		ToolFirst,
		ToolLast,
		ToolExpand,
		ToolMatrix,
		ToolExport,
		ToolNextStep,
		ToolPrevStep,
		ToolPrevClient,
		ToolNextClient,
	)
)

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

// ///// ARGS TODO split per state

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

const APrefix = "dbg"

type Args struct {
	am.ArgsBase `json:"-"`
}

func (Args) () string {
	return APrefix
}

// A is a struct for debugger arguments. It's a typesafe alternative to [am.A].
type A struct {
	Args `json:"-"`

	// client identification

	ClientId string `log:"client_id"`
	TxId     string `log:"tx_id"`
	ConnId   string `log:"conn_id"`
	ConnIds  []string

	// cursor positioning (1-based)

	Cursor1     int `log:"cursor1" json:",string"`
	CursorStep1 int `log:"cursor_step1" json:",string"`
	// TODO merge with Cursor1
	CursorTx1 int `log:"cursor_tx1" json:",string"`

	// navigation

	Amount int  `log:"amount" json:",string"`
	Fwd    bool `log:"fwd" json:",string"`

	// state / group selection

	State string `log:"state"`
	Group string

	// tool toggling

	ToolName ToolName `log:"tool_name"`

	// client init

	Id string `log:"id"`

	// log building

	// 1-based index
	LogRebuildEnd int `json:",string"`
	LogBuffer     string
	LogLevel      am.LogLevel `json:",string"`

	// cursor history / filtering

	SkipHistory bool
	TrimHistory bool
	FilterBack  bool

	// filter / update flags

	FilterTxs       bool
	BuildClientList bool
	Immediate       bool
	FromConnected   bool
	FromPlaying     bool
	MouseFocus      bool

	// display / text

	Text string `log:"text"`
	Uri  string `log:"uri"`
	Addr string `log:"addr"`

	// matrix rain

	Row       int `json:",string"`
	Column    int `json:",string"`
	CurrTxRow int `json:",string"`

	// FocusPrimitive is the UI element receiving focus.
	FocusPrimitive cview.Primitive
	// HttpRequest is for web server route handlers.
	HttpRequest *http.Request
	// HttpResponseWriter is for web server route handlers.
	HttpResponseWriter http.ResponseWriter
	// DoneChan signals completion of a web handler.
	DoneChan chan struct{}
	// WebSocketConn is for WebSocket handlers.
	WebSocketConn *websocket.Conn
	// MsgStruct is the schema message from am-dbg protocol.
	MsgStruct *dbg.DbgMsgStruct
	// MsgsTx is a batch of transaction messages.
	MsgsTx []*dbg.DbgMsgTx

	// DiagDom is parsed HTML for diagram generation.
	DiagDom *goquery.Document
	// DiagName is the name of the diagram being generated.
	DiagName string
	DiagType DiagramType `log:"diag_type"`
}

// ARpc is a subset of A, that can be passed over RPC.
type ARpc struct {
	Args `json:"-"`

	// client identification

	ClientId string `log:"client_id"`
	TxId     string `log:"tx_id"`
	ConnId   string `log:"conn_id"`
	ConnIds  []string

	// cursor positioning (1-based)

	Cursor1     int `log:"cursor1" json:",string"`
	CursorStep1 int `log:"cursor_step1" json:",string"`
	// TODO merge with Cursor1
	CursorTx1 int `log:"cursor_tx1" json:",string"`

	// navigation

	Amount int  `log:"amount" json:",string"`
	Fwd    bool `log:"fwd" json:",string"`

	// state / group selection

	State string `log:"state"`
	Group string

	// tool toggling

	ToolName ToolName `log:"tool_name"`

	// client init

	Id string `log:"id"`

	// log building

	LogRebuildEnd int `json:",string"`
	LogBuffer     string
	LogLevel      am.LogLevel `json:",string"`

	// cursor history / filtering

	SkipHistory bool
	TrimHistory bool
	FilterBack  bool

	// filter / update flags

	FilterTxs       bool
	BuildClientList bool
	Immediate       bool
	FromConnected   bool
	FromPlaying     bool
	MouseFocus      bool

	// display / text

	Text string `log:"text"`
	Uri  string `log:"uri"`
	Addr string `log:"addr"`

	// matrix rain

	Row       int `json:",string"`
	Column    int `json:",string"`
	CurrTxRow int `json:",string"`
}

func init() {
	for ,  := range ArgsRpc {
		gob.Register()
	}
}

// ArgsRpc will be available in the REPL.
var ArgsRpc = []am.ArgsApi{ARpc{}}

// StateCalls is a list binding args to state names.
var StateCalls = []am.CallSignature{
	{States: am.S{ss.StateNameSelected}, Needed: []string{"State"}},
	{States: am.S{ss.Redraw}, Optional: []string{"Immediate"}},
	{States: am.S{ss.Fwd}, Optional: []string{"Amount"}},
	{States: am.S{ss.Back}, Optional: []string{"Amount"}},
	{States: am.S{ss.ConnectEvent}, Needed: []string{"MsgStruct", "ConnId"}},
	{States: am.S{ss.DisconnectEvent}, Needed: []string{"ConnId"}},
	{
		States: am.S{ss.ClientMsg},
		Needed: []string{"MsgsTx", "ConnIds"},
	},
	{
		States: am.S{ss.RemoveClient},
		Needed: []string{"ClientId"},
	},
	{
		States: am.S{ss.SetGroup},
		Needed: []string{"Group"},
	},
	{
		States:   am.S{ss.SelectingClient},
		Needed:   []string{"ClientId"},
		Optional: []string{"Group", "FromConnected"},
	},
	{
		States:   am.S{ss.ClientSelected},
		Optional: []string{"FromConnected", "FromPlaying"},
	},
	{
		States:   am.S{ss.ScrollToTx},
		Optional: []string{"CursorTx1", "TxId", "CursorStep1", "TrimHistory"},
	},
	{
		States: am.S{ss.ScrollToStep},
		Needed: []string{"CursorStep1"},
	},
	{
		States: am.S{ss.ToggleTool},
		Needed: []string{"ToolName"},
		Values: map[string][]string{
			"ToolName": ToolNames.Values(),
		},
	},
	{
		States:   am.S{ss.ToolToggled},
		Optional: []string{"FilterTxs", "BuildClientList"},
	},
	{
		States: am.S{ss.SwitchingClientTx},
		Desc:   "Go to N-th transition of a client.",
		Needed: []string{"ClientId", "CursorTx1"},
	},
	{
		States: am.S{ss.ScrollToMutTx},
		Desc: "ScrollToMutTxState scrolls to a transition which mutated the " +
			"passed state, If fwd is true, it scrolls forward, otherwise backwards.",
		Needed:   []string{"State"},
		Optional: []string{"Fwd"},
	},
	{
		States:   am.S{ss.AfterFocus},
		Needed:   []string{"FocusPrimitive"},
		Optional: []string{"MouseFocus"},
	},
	{
		States:   am.S{ss.Resized},
		Optional: []string{"LogRebuildEnd"},
	},
	{
		States:   am.S{ss.WebReqDiag},
		Needed:   []string{"HttpRequest", "HttpResponseWriter", "DoneChan"},
		Optional: []string{"Uri", "Addr"},
	},
	{
		States: am.S{ss.WebSocketDiag},
		Needed: []string{"WebSocketConn", "HttpRequest", "HttpResponseWriter",
			"DoneChan"},
		Optional: []string{"Addr"},
	},
	{
		States: am.S{ss.MatrixRainSelected},
		Needed: []string{"Row", "Column", "CurrTxRow"},
	},
}

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

// ///// UTILS

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

// TODO
func ( string) string {
	, _, _ = strings.Cut(, ":")
	return strings.TrimSuffix(strings.ReplaceAll(strings.ReplaceAll(,
		"-", ""),
		" ", ""),
		"StatesDef")
}

type StateTraceItem struct {
	Label      string
	Source     *MachAddress
	StateNames am.S
}

// DiagramType is a type of a diagram.
type DiagramType enum.Member[string]

var (
	DiagramTypeState = DiagramType{"state"}
	DiagramTypeMach  = DiagramType{"mach"}
	DiagramTypeSteps = DiagramType{"steps"}
	DiagramTypeGraph = DiagramType{"graph"}
)

func ( DiagramType) () string {
	return .Value
}

type WsDiagMsg struct {
	// ID of this event (eg refresh)
	Event string
	// ID of this diagram (eg mach ID, state name...)
	Id    string
	Group string
}