package cview

import (
	
	
	

	
)

const (
	// The size of the event/update/redraw channels.
	queueSize = 100

	// The minimum duration between resize event callbacks.
	resizeEventThrottle = 50 * time.Millisecond
)

// Application represents the top node of an application.
//
// It is not strictly required to use this class as none of the other classes
// depend on it. However, it provides useful tools to set up an application and
// plays nicely with all widgets.
//
// The following command displays a primitive p on the screen until Ctrl-C is
// pressed:
//
//	if err := cview.NewApplication().SetRoot(p, true).Run(); err != nil {
//	    panic(err)
//	}
type Application struct {
	// The application's screen. Apart from Run(), this variable should never be
	// set directly. Always use the screenReplacement channel after calling
	// Fini(), to set a new screen (or nil to stop the application).
	screen tcell.Screen

	// The size of the application's screen.
	width, height int

	// The primitive which currently has the keyboard focus.
	focus Primitive

	// The root primitive to be seen on the screen.
	root Primitive

	// Whether or not the application resizes the root primitive.
	rootFullscreen bool

	// Whether or not to enable bracketed paste mode.
	enableBracketedPaste bool

	// Whether or not to enable mouse events.
	enableMouse bool

	// An optional capture function which receives a key event and returns the
	// event to be forwarded to the default input handler (nil if nothing should
	// be forwarded).
	inputCapture func(event *tcell.EventKey) *tcell.EventKey

	// Time a resize event was last processed.
	lastResize time.Time

	// Timer limiting how quickly resize events are processed.
	throttleResize *time.Timer

	// An optional callback function which is invoked when the application's
	// window is initialized, and when the application's window size changes.
	// After invoking this callback the screen is cleared and the application
	// is drawn.
	afterResize func(width int, height int)

	// An optional callback function which is invoked before the application's
	// focus changes.
	beforeFocus func(p Primitive) bool

	// An optional callback function which is invoked after the application's
	// focus changes.
	afterFocus func(p Primitive)

	// An optional callback function which is invoked just before the root
	// primitive is drawn.
	beforeDraw func(screen tcell.Screen) bool

	// An optional callback function which is invoked after the root primitive
	// was drawn.
	afterDraw func(screen tcell.Screen)

	// Used to send screen events from separate goroutine to main event loop
	events chan tcell.Event

	// Functions queued from goroutines, used to serialize updates to primitives.
	updates chan func()

	// An object that the screen variable will be set to after Fini() was called.
	// Use this channel to set a new screen object for the application
	// (screen.Init() and draw() will be called implicitly). A value of nil will
	// stop the application.
	screenReplacement chan tcell.Screen

	// An optional capture function which receives a mouse event and returns the
	// event to be forwarded to the default mouse handler (nil if nothing should
	// be forwarded).
	mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)

	// doubleClickInterval specifies the maximum time between clicks to register a
	// double click rather than a single click.
	doubleClickInterval time.Duration

	mouseCapturingPrimitive Primitive        // A Primitive returned by a MouseHandler which will capture future mouse events.
	lastMouseX, lastMouseY  int              // The last position of the mouse.
	mouseDownX, mouseDownY  int              // The position of the mouse when its button was last pressed.
	lastMouseClick          time.Time        // The time when a mouse button was last clicked.
	lastMouseButtons        tcell.ButtonMask // The last mouse button state.

	sync.RWMutex
}

// NewApplication creates and returns a new application.
func () *Application {
	return &Application{
		enableBracketedPaste: true,
		events:               make(chan tcell.Event, queueSize),
		updates:              make(chan func(), queueSize),
		screenReplacement:    make(chan tcell.Screen, 1),
	}
}

// HandlePanic (when deferred at the start of a goroutine) handles panics
// gracefully. The terminal is returned to its original state before the panic
// message is printed.
//
// Panics may only be handled by the panicking goroutine. Because of this,
// HandlePanic must be deferred at the start of each goroutine (including main).
func ( *Application) () {
	 := recover()
	if  == nil {
		return
	}

	.Lock()
	defer .Unlock()
	.finalizeScreen()

	panic()
}

// SetInputCapture sets a function which captures all key events before they are
// forwarded to the key event handler of the primitive which currently has
// focus. This function can then choose to forward that key event (or a
// different one) by returning it or stop the key event processing by returning
// nil.
//
// Note that this also affects the default event handling of the application
// itself: Such a handler can intercept the Ctrl-C event which closes the
// application.
func ( *Application) ( func( *tcell.EventKey) *tcell.EventKey) {
	.Lock()
	defer .Unlock()

	.inputCapture = 

}

// GetInputCapture returns the function installed with SetInputCapture() or nil
// if no such function has been installed.
func ( *Application) () func( *tcell.EventKey) *tcell.EventKey {
	.RLock()
	defer .RUnlock()

	return .inputCapture
}

// SetMouseCapture sets a function which captures mouse events (consisting of
// the original tcell mouse event and the semantic mouse action) before they are
// forwarded to the appropriate mouse event handler. This function can then
// choose to forward that event (or a different one) by returning it or stop
// the event processing by returning a nil mouse event.
func ( *Application) ( func( *tcell.EventMouse,  MouseAction) (*tcell.EventMouse, MouseAction)) {
	.mouseCapture = 

}

// GetMouseCapture returns the function installed with SetMouseCapture() or nil
// if no such function has been installed.
func ( *Application) () func( *tcell.EventMouse,  MouseAction) (*tcell.EventMouse, MouseAction) {
	return .mouseCapture
}

// SetDoubleClickInterval sets the maximum time between clicks to register a
// double click rather than a single click. A standard duration is provided as
// StandardDoubleClick. No interval is set by default, disabling double clicks.
func ( *Application) ( time.Duration) {
	.doubleClickInterval = 
}

// SetScreen allows you to provide your own tcell.Screen object. For most
// applications, this is not needed and you should be familiar with
// tcell.Screen when using this function.
//
// This function is typically called before the first call to Run(). Init() need
// not be called on the screen.
func ( *Application) ( tcell.Screen) {
	if  == nil {
		return // Invalid input. Do nothing.
	}

	.Lock()
	if .screen == nil {
		// Run() has not been called yet.
		.screen = 
		.Unlock()
		return
	}

	// Run() is already in progress. Exchange screen.
	 := .screen
	.Unlock()
	.Fini()
	.screenReplacement <- 
}

// GetScreen returns the current tcell.Screen of the application. Lock the
// application when manipulating the screen to prevent race conditions. This
// value is only available after calling Init or Run.
func ( *Application) () tcell.Screen {
	.RLock()
	defer .RUnlock()
	return .screen
}

// GetScreenSize returns the size of the application's screen. These values are
// only available after calling Init or Run.
func ( *Application) () (,  int) {
	.RLock()
	defer .RUnlock()
	return .width, .height
}

// Init initializes the application screen. Calling Init before running is not
// required. Its primary use is to populate screen dimensions before running an
// application.
func ( *Application) () error {
	.Lock()
	defer .Unlock()
	return .init()
}

func ( *Application) () error {
	if .screen != nil {
		return nil
	}

	var  error
	.screen,  = tcell.NewScreen()
	if  != nil {
		return 
	}
	if  = .screen.Init();  != nil {
		return 
	}
	.width, .height = .screen.Size()
	if .enableBracketedPaste {
		.screen.EnablePaste()
	}
	if .enableMouse {
		.screen.EnableMouse()
	}
	return nil
}

// EnableBracketedPaste enables bracketed paste mode, which is enabled by default.
func ( *Application) ( bool) {
	.Lock()
	defer .Unlock()
	if  != .enableBracketedPaste && .screen != nil {
		if  {
			.screen.EnablePaste()
		} else {
			.screen.DisablePaste()
		}
	}
	.enableBracketedPaste = 
}

// EnableMouse enables mouse events.
func ( *Application) ( bool) {
	.Lock()
	defer .Unlock()
	if  != .enableMouse && .screen != nil {
		if  {
			.screen.EnableMouse()
		} else {
			.screen.DisableMouse()
		}
	}
	.enableMouse = 
}

// Run starts the application and thus the event loop. This function returns
// when Stop() was called.
func ( *Application) () error {
	.Lock()

	// Initialize screen
	 := .init()
	if  != nil {
		.Unlock()
		return 
	}

	// defer a.HandlePanic()

	// Draw the screen for the first time.
	.Unlock()
	.draw()

	// Separate loop to wait for screen replacement events.
	var  sync.WaitGroup
	.Add(1)
	go func() {
		defer .HandlePanic()

		defer .Done()
		for {
			.RLock()
			 := .screen
			.RUnlock()
			if  == nil {
				// We have no screen. Let's stop.
				.QueueEvent(nil)
				break
			}

			// A screen was finalized (event is nil). Wait for a new screen.
			 = <-.screenReplacement
			if  == nil {
				// No new screen. We're done.
				.QueueEvent(nil)
				return
			}

			// We have a new screen. Keep going.
			.Lock()
			.screen = 
			.Unlock()

			// Initialize and draw this screen.
			if  := .Init();  != nil {
				panic()
			}
			if .enableBracketedPaste {
				.EnablePaste()
			}
			if .enableMouse {
				.EnableMouse()
			}

			.draw()
		}
	}()

	 := func( interface{}) {
		.RLock()
		 := .focus
		 := .inputCapture
		 := .screen
		.RUnlock()

		switch event := .(type) {
		case *tcell.EventKey:
			// Intercept keys.
			if  != nil {
				 = ()
				if  == nil {
					.draw()
					return // Don't forward event.
				}
			}

			// Ctrl-C closes the application.
			if .Key() == tcell.KeyCtrlC {
				.Stop()
				return
			}

			// Pass other key events to the currently focused primitive.
			if  != nil {
				if  := .InputHandler();  != nil {
					(, func( Primitive) {
						.SetFocus()
					})
					.draw()
				}
			}
		case *tcell.EventResize:
			// Throttle resize events.
			if time.Since(.lastResize) < resizeEventThrottle {
				// Stop timer
				if .throttleResize != nil && !.throttleResize.Stop() {
					select {
					case <-.throttleResize.C:
					default:
					}
				}

				 :=  // Capture

				// Start timer
				.throttleResize = time.AfterFunc(resizeEventThrottle, func() {
					.events <- 
				})

				return
			}

			.lastResize = time.Now()

			if  == nil {
				return
			}

			.Clear()
			.width, .height = .Size()

			// Call afterResize handler if there is one.
			if .afterResize != nil {
				.afterResize(.width, .height)
			}

			.draw()
		case *tcell.EventMouse:
			,  := .fireMouseActions()
			if  {
				.draw()
			}
			.lastMouseButtons = .Buttons()
			if  {
				.mouseDownX, .mouseDownY = .Position()
			}
		}
	}

	 := &sync.Mutex{}

	go func() {
		defer .HandlePanic()

		for  := range .updates {
			.Lock()
			()
			.Unlock()
		}
	}()

	go func() {
		defer .HandlePanic()

		for  := range .events {
			.Lock()
			()
			.Unlock()
		}
	}()

	// Start screen event loop.
	for {
		.Lock()
		 := .screen
		.Unlock()

		if  == nil {
			break
		}

		// Wait for next event.
		 := .PollEvent()
		if  == nil {
			break
		}

		.Lock()
		()
		.Unlock()
	}

	// Wait for the screen replacement event loop to finish.
	.Wait()
	.Lock()
	defer .Unlock()
	.screen = nil

	return nil
}

// fireMouseActions analyzes the provided mouse event, derives mouse actions
// from it and then forwards them to the corresponding primitives.
func ( *Application) ( *tcell.EventMouse) (,  bool) {
	// We want to relay follow-up events to the same target primitive.
	var  Primitive

	// Helper function to fire a mouse action.
	 := func( MouseAction) {
		switch  {
		case MouseLeftDown, MouseMiddleDown, MouseRightDown:
			 = true
		}

		// Intercept event.
		if .mouseCapture != nil {
			,  = .mouseCapture(, )
			if  == nil {
				 = true
				return // Don't forward event.
			}
		}

		// Determine the target primitive.
		var ,  Primitive
		if .mouseCapturingPrimitive != nil {
			 = .mouseCapturingPrimitive
			 = .mouseCapturingPrimitive
		} else if  != nil {
			 = 
		} else {
			 = .root
		}
		if  != nil {
			if  := .MouseHandler();  != nil {
				var  bool
				,  = (, , func( Primitive) {
					.SetFocus()
				})
				if  {
					 = true
				}
			}
		}
		.mouseCapturingPrimitive = 
	}

	,  := .Position()
	 := .Buttons()
	 :=  != .mouseDownX ||  != .mouseDownY
	 :=  ^ .lastMouseButtons

	if  != .lastMouseX ||  != .lastMouseY {
		(MouseMove)
		.lastMouseX = 
		.lastMouseY = 
	}

	for ,  := range []struct {
		                  tcell.ButtonMask
		, , ,  MouseAction
	}{
		{tcell.ButtonPrimary, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick},
		{tcell.ButtonMiddle, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick},
		{tcell.ButtonSecondary, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick},
	} {
		if &. != 0 {
			if &. != 0 {
				(.)
			} else {
				(.)
				if ! {
					if .doubleClickInterval == 0 || .lastMouseClick.Add(.doubleClickInterval).Before(time.Now()) {
						(.)
						.lastMouseClick = time.Now()
					} else {
						(.)
						.lastMouseClick = time.Time{} // reset
					}
				}
			}
		}
	}

	for ,  := range []struct {
		 tcell.ButtonMask
		 MouseAction
	}{
		{tcell.WheelUp, MouseScrollUp},
		{tcell.WheelDown, MouseScrollDown},
		{tcell.WheelLeft, MouseScrollLeft},
		{tcell.WheelRight, MouseScrollRight}} {
		if &. != 0 {
			(.)
		}
	}

	return , 
}

// Stop stops the application, causing Run() to return.
func ( *Application) () {
	.Lock()
	defer .Unlock()

	.finalizeScreen()
	.screenReplacement <- nil
}

func ( *Application) () {
	 := .screen
	if  == nil {
		return
	}

	.screen = nil
	.Fini()
}

// Suspend temporarily suspends the application by exiting terminal UI mode and
// invoking the provided function "f". When "f" returns, terminal UI mode is
// entered again and the application resumes.
//
// A return value of true indicates that the application was suspended and "f"
// was called. If false is returned, the application was already suspended,
// terminal UI mode was not exited, and "f" was not called.
func ( *Application) ( func()) bool {
	.Lock()
	if .screen == nil {
		.Unlock()
		return false // Screen has not yet been initialized.
	}
	 := .screen.Suspend()
	.Unlock()
	if  != nil {
		panic()
	}

	// Wait for "f" to return.
	()

	.Lock()
	 = .screen.Resume()
	.Unlock()
	if  != nil {
		panic()
	}

	return true
}

// Draw draws the provided primitives on the screen, or when no primitives are
// provided, draws the application's root primitive (i.e. the entire screen).
//
// When one or more primitives are supplied, the Draw functions of the
// primitives are called. Handlers set via BeforeDrawFunc and AfterDrawFunc are
// not called.
//
// When no primitives are provided, the Draw function of the application's root
// primitive is called. This results in drawing the entire screen. Handlers set
// via BeforeDrawFunc and AfterDrawFunc are also called.
func ( *Application) ( ...Primitive) {
	.QueueUpdate(func() {
		if len() == 0 {
			.draw()
			return
		}

		.Lock()
		if .screen != nil {
			for ,  := range  {
				.Draw(.screen)
			}
			.screen.Show()
		}
		.Unlock()
	})
}

// draw actually does what Draw() promises to do.
func ( *Application) () {
	.Lock()

	 := .screen
	 := .root
	 := .rootFullscreen
	 := .beforeDraw
	 := .afterDraw

	// Maybe we're not ready yet or not anymore.
	if  == nil ||  == nil {
		.Unlock()
		return
	}

	// Resize if requested.
	if  {
		.SetRect(0, 0, .width, .height)
	}

	// Call before handler if there is one.
	if  != nil {
		.Unlock()
		if () {
			.Show()
			return
		}
	} else {
		.Unlock()
	}

	// Draw all primitives.
	.Draw()

	// Call after handler if there is one.
	if  != nil {
		()
	}

	// Sync screen.
	.Show()
}

// SetBeforeDrawFunc installs a callback function which is invoked just before
// the root primitive is drawn during screen updates. If the function returns
// true, drawing will not continue, i.e. the root primitive will not be drawn
// (and an after-draw-handler will not be called).
//
// Note that the screen is not cleared by the application. To clear the screen,
// you may call screen.Clear().
//
// Provide nil to uninstall the callback function.
func ( *Application) ( func( tcell.Screen) bool) {
	.Lock()
	defer .Unlock()

	.beforeDraw = 
}

// GetBeforeDrawFunc returns the callback function installed with
// SetBeforeDrawFunc() or nil if none has been installed.
func ( *Application) () func( tcell.Screen) bool {
	.RLock()
	defer .RUnlock()

	return .beforeDraw
}

// SetAfterDrawFunc installs a callback function which is invoked after the root
// primitive was drawn during screen updates.
//
// Provide nil to uninstall the callback function.
func ( *Application) ( func( tcell.Screen)) {
	.Lock()
	defer .Unlock()

	.afterDraw = 
}

// GetAfterDrawFunc returns the callback function installed with
// SetAfterDrawFunc() or nil if none has been installed.
func ( *Application) () func( tcell.Screen) {
	.RLock()
	defer .RUnlock()

	return .afterDraw
}

// SetRoot sets the root primitive for this application. If "fullscreen" is set
// to true, the root primitive's position will be changed to fill the screen.
//
// This function must be called at least once or nothing will be displayed when
// the application starts.
//
// It also calls SetFocus() on the primitive and draws the application.
func ( *Application) ( Primitive,  bool) {
	.Lock()
	.root = 
	.rootFullscreen = 
	if .screen != nil {
		.screen.Clear()
	}
	.Unlock()

	.SetFocus()

	.Draw()
}

// ResizeToFullScreen resizes the given primitive such that it fills the entire
// screen.
func ( *Application) ( Primitive) {
	.RLock()
	,  := .width, .height
	.RUnlock()
	.SetRect(0, 0, , )
}

// SetAfterResizeFunc installs a callback function which is invoked when the
// application's window is initialized, and when the application's window size
// changes. After invoking this callback the screen is cleared and the
// application is drawn.
//
// Provide nil to uninstall the callback function.
func ( *Application) ( func( int,  int)) {
	.Lock()
	defer .Unlock()

	.afterResize = 
}

// GetAfterResizeFunc returns the callback function installed with
// SetAfterResizeFunc() or nil if none has been installed.
func ( *Application) () func( int,  int) {
	.RLock()
	defer .RUnlock()

	return .afterResize
}

// SetFocus sets the focus on a new primitive. All key events will be redirected
// to that primitive. Callers must ensure that the primitive will handle key
// events.
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func ( *Application) ( Primitive) {
	.Lock()

	if .beforeFocus != nil {
		.Unlock()
		 := .beforeFocus()
		if ! {
			return
		}
		.Lock()
	}

	if .focus != nil {
		.focus.Blur()
	}

	.focus = 

	if .screen != nil {
		.screen.HideCursor()
	}
	.Unlock()

	if .afterFocus != nil {
		.afterFocus()
	}

	if  != nil {
		.Focus(func( Primitive) {
			.()
		})
	}
}

// GetFocus returns the primitive which has the current focus. If none has it,
// nil is returned.
func ( *Application) () Primitive {
	.RLock()
	defer .RUnlock()

	return .focus
}

// SetBeforeFocusFunc installs a callback function which is invoked before the
// application's focus changes. Return false to maintain the current focus.
//
// Provide nil to uninstall the callback function.
func ( *Application) ( func( Primitive) bool) {
	.Lock()
	defer .Unlock()

	.beforeFocus = 
}

// SetAfterFocusFunc installs a callback function which is invoked after the
// application's focus changes.
//
// Provide nil to uninstall the callback function.
func ( *Application) ( func( Primitive)) {
	.Lock()
	defer .Unlock()

	.afterFocus = 
}

// QueueUpdate queues a function to be executed as part of the event loop.
//
// Note that Draw() is not implicitly called after the execution of f as that
// may not be desirable. You can call Draw() from f if the screen should be
// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
// up with an immediate refresh of the screen.
func ( *Application) ( func()) {
	.updates <- 
}

// QueueUpdateDraw works like QueueUpdate() except, when one or more primitives
// are provided, the primitives are drawn after the provided function returns.
// When no primitives are provided, the entire screen is drawn after the
// provided function returns.
func ( *Application) ( func(),  ...Primitive) {
	.QueueUpdate(func() {
		()

		if len() == 0 {
			.draw()
			return
		}
		.Lock()
		if .screen != nil {
			for ,  := range  {
				.Draw(.screen)
			}
			.screen.Show()
		}
		.Unlock()
	})
}

// QueueEvent sends an event to the Application event loop.
//
// It is not recommended for event to be nil.
func ( *Application) ( tcell.Event) {
	.events <- 
}

// RingBell sends a bell code to the terminal.
func ( *Application) () {
	.QueueUpdate(func() {
		fmt.Print(string(byte(7)))
	})
}