package debugger

import (
	
	
	
	
	
	
	

	
	
	
	

	
	

	am 
)

const (
	// TODO light mode
	colorActive     = tcell.ColorOlive
	colorActive2    = tcell.ColorGreenYellow
	colorInactive   = tcell.ColorLimeGreen
	colorHighlight  = tcell.ColorDarkSlateGray
	colorHighlight2 = tcell.ColorDimGray
	colorHighlight3 = tcell.Color233
	colorErr        = tcell.ColorRed
	// TODO customize
	playInterval = 500 * time.Millisecond
	// TODO add param --max-clients
	maxClients            = 5000
	timeFormat            = "15:04:05.000000000"
	fastJumpAmount        = 50
	arrowThrottleMs       = 200
	logUpdateDebounce     = 300 * time.Millisecond
	sidebarUpdateDebounce = time.Second
	searchAsTypeWindow    = 1500 * time.Millisecond
	heartbeatInterval     = 1 * time.Minute
	// heartbeatInterval = 5 * time.Second
	// msgMaxAge             = 0

	// maxMemMb = 100
	maxMemMb        = 50
	msgMaxThreshold = 300
	// max txs queued for scrolling the timelines
	scrollTxThrottle = 3
)

var colorDefault = cview.Styles.PrimaryTextColor

const (
	// row 1
	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"
	toolFilterDisconn    ToolName = "skip-disconn"
	ToolLogTimestamps    ToolName = "hide-timestamps"
	ToolFilterTraces     ToolName = "hide-traces"
	toolLog              ToolName = "log"
	toolDiagrams         ToolName = "diagrams"
	toolTimelines        ToolName = "timelines"
	// toolLog0              ToolName = "log-0"
	// toolLog1              ToolName = "log-1"
	// toolLog2              ToolName = "log-2"
	// toolLog3              ToolName = "log-3"
	// toolLog4              ToolName = "log-4"
	toolReader ToolName = "reader"
	toolRain   ToolName = "rain"
	toolWeb    ToolName = "web"

	// row 2

	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"
)

type Opts struct {
	MachUrl         string
	SelectConnected bool
	CleanOnConnect  bool
	EnableMouse     bool
	ShowReader      bool
	// MachAddress to listen on
	AddrRpc  string
	AddrHttp string
	AddrSsh  string
	// Log level of the debugger's machine
	DbgLogLevel am.LogLevel
	// Go race detector is enabled
	DbgRace   bool
	DbgLogger *log.Logger
	// Filters for the transitions and logging
	Filters *OptsFilters
	// Timelines is the number of timelines to show (0-2).
	Timelines int
	// File path to import (brotli)
	ImportData string
	// Dump client list into a txt file.
	OutputClients bool
	// Root dir for output files
	OutputDir string
	// OutputDiagrams is the details level of the current machine's diagram (0-3).
	// 0 - off, 3 - most detailed
	OutputDiagrams int
	// Screen overload for tests & ssh
	Screen tcell.Screen
	// Debugger's ID
	Id string
	// version of this instance
	Version         string
	MaxMemMb        int
	Log2Ttl         time.Duration
	ViewNarrow      bool
	ViewRain        bool
	TailMode        bool
	OutputTx        bool
	EnableClipboard bool
	UiSsh           bool
	UiWeb           bool
	Print           func(txt string, args ...any)
	OutputLog       bool
}

type OptsFilters struct {
	SkipCanceledTx     bool
	SkipAutoTx         bool
	SkipAutoCanceledTx bool
	SkipEmptyTx        bool
	SkipHealthTx       bool
	SkipQueuedTx       bool
	SkipOutGroup       bool
	SkipChecks         bool
	LogLevel           am.LogLevel
}

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

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

type ToolName string

type Client struct {
	*server.Client

	CursorTx1           int
	ReaderCollapsed     bool
	CursorStep1         int
	SelectedState       string
	SelectedReaderEntry *types.LogReaderEntryPtr
	// extracted log entries per tx ID
	// TODO GC when all entries are closedAt and the first client's tx is later
	//  than the latest closedAt; whole tx needs to be disposed at the same time
	LogReader map[string][]*types.LogReaderEntry

	logRenderedCursor1    int
	logRenderedLevel      am.LogLevel
	logRenderedFilters    *OptsFilters
	logRenderedTimestamps bool
	logReaderMx           sync.Mutex
}

func newClient(, ,  string,  *server.Exportable) *Client {
	 := &Client{
		Client: &server.Client{
			Id:         ,
			ConnId:     ,
			SchemaHash: ,
			Exportable: ,
		},
		LogReader: make(map[string][]*types.LogReaderEntry),
	}
	.ParseSchema()

	return 
}

func ( *Client) ( string,  *types.LogReaderEntry) int {
	.logReaderMx.Lock()
	defer .logReaderMx.Unlock()

	.LogReader[] = append(.LogReader[], )

	return len(.LogReader[]) - 1
}

func ( *Client) ( string,  int) *types.LogReaderEntry {
	.logReaderMx.Lock()
	defer .logReaderMx.Unlock()

	,  := .LogReader[]
	if ! ||  >= len() {
		return nil
	}

	return []
}

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

// ///// SSH

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

func ( ssh.Session) (tcell.Screen, error) {
	, ,  := .Pty()
	if ! {
		return nil, errors.New("no pty requested")
	}
	,  := terminfo.LookupTerminfo(.Term)
	if  != nil {
		return nil, 
	}
	,  := tcell.NewTerminfoScreenFromTtyTerminfo(&tty{
		Session: ,
		size:    .Window,
		ch:      ,
	}, )
	if  != nil {
		return nil, 
	}
	return , nil
}

type tty struct {
	ssh.Session
	size     ssh.Window
	ch       <-chan ssh.Window
	resizecb func()
	mu       sync.Mutex
}

func ( *tty) () error {
	go func() {
		for  := range .ch {
			.mu.Lock()
			.size = 
			.notifyResize()
			.mu.Unlock()
		}
	}()
	return nil
}

func ( *tty) () error {
	return nil
}

func ( *tty) () error {
	return nil
}

func ( *tty) () ( tcell.WindowSize,  error) {
	.mu.Lock()
	defer .mu.Unlock()

	return tcell.WindowSize{
		Width:  .size.Width,
		Height: .size.Height,
	}, nil
}

func ( *tty) ( func()) {
	.mu.Lock()
	defer .mu.Unlock()

	.resizecb = 
}

func ( *tty) () {
	if .resizecb != nil {
		.resizecb()
	}
}

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

// ///// MISC

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

// openURL opens the specified URL in the default browser of the user.
// https://gist.github.com/sevkin/9798d67b2cb9d07cb05f89f14ba682f8
func openURL( string) error {
	var  string
	var  []string

	switch runtime.GOOS {
	case "windows":
		 = "cmd.exe"
		 = []string{
			"/c", "rundll32", "url.dll,FileProtocolHandler",
			strings.ReplaceAll(, "&", "^&"),
		}
	case "darwin":
		 = "open"
		 = []string{}
	default:
		if isWSL() {
			 = "cmd.exe"
			 = []string{"start", }
		} else {
			 = "xdg-open"
			 = []string{}
		}
	}

	 := exec.Command(, ...)
	 := .Start()
	if  != nil {
		return 
	}
	 = .Wait()
	if  != nil {
		return 
	}

	return nil
}

// isWSL checks if the Go program is running inside Windows Subsystem for Linux
func isWSL() bool {
	,  := exec.Command("uname", "-r").Output()
	if  != nil {
		return false
	}
	return strings.Contains(strings.ToLower(string()), "microsoft")
}