package mode

import (
	
	
	
	

	
	
	
	
)

type Navigation interface {
	tk.Widget
	// SelectedName returns the currently selected name. It returns an empty
	// string if there is no selected name, which can happen if the current
	// directory is empty.
	SelectedName() string
	// Select changes the selection.
	Select(f func(tk.ListBoxState) int)
	// ScrollPreview scrolls the preview.
	ScrollPreview(delta int)
	// Ascend ascends to the parent directory.
	Ascend()
	// Descend descends into the currently selected child directory.
	Descend()
	// MutateFiltering changes the filtering status.
	MutateFiltering(f func(bool) bool)
	// MutateShowHidden changes whether hidden files - files whose names start
	// with ".", should be shown.
	MutateShowHidden(f func(bool) bool)
}

// NavigationSpec specifieis the configuration for the navigation mode.
type NavigationSpec struct {
	// Key bindings.
	Bindings tk.Bindings
	// Underlying filesystem.
	Cursor NavigationCursor
	// A function that returns the relative weights of the widths of the 3
	// columns. If unspecified, the ratio is 1:3:4.
	WidthRatio func() [3]int
	// Configuration for the filter.
	Filter FilterSpec
}

type navigationState struct {
	Filtering  bool
	ShowHidden bool
}

type navigation struct {
	NavigationSpec
	app        cli.App
	codeArea   tk.CodeArea
	colView    tk.ColView
	lastFilter string
	stateMutex sync.RWMutex
	state      navigationState
}

func ( *navigation) ( func(*navigationState)) {
	.stateMutex.Lock()
	defer .stateMutex.Unlock()
	(&.state)
}

func ( *navigation) () navigationState {
	.stateMutex.RLock()
	defer .stateMutex.RUnlock()
	return .state
}

func ( *navigation) ( term.Event) bool {
	if .colView.Handle() {
		return true
	}
	if .CopyState().Filtering {
		if .codeArea.Handle() {
			 := .codeArea.CopyState().Buffer.Content
			if  != .lastFilter {
				.lastFilter = 
				updateState(, "")
			}
			return true
		}
		return false
	}
	return .app.CodeArea().Handle()
}

func ( *navigation) (,  int) *term.Buffer {
	 := .codeArea.Render(, )
	 := .colView.Render(, -len(.Lines))
	.Extend(, false)
	return 
}

func ( *navigation) () bool {
	return .CopyState().Filtering
}

func ( *navigation) () {
	// Remember the name of the current directory before ascending.
	 := ""
	,  := .Cursor.Current()
	if  == nil {
		 = .Name()
	}

	 = .Cursor.Ascend()
	if  != nil {
		.app.Notify(.Error())
	} else {
		.codeArea.MutateState(func( *tk.CodeAreaState) {
			.Buffer = tk.CodeBuffer{}
		})
		updateState(, )
	}
}

func ( *navigation) () {
	,  := .colView.CopyState().Columns[1].(tk.ListBox)
	if ! {
		return
	}
	 := .CopyState()
	if .Items.Len() == 0 {
		return
	}
	 := .Items.(fileItems)[.Selected]
	if !.IsDirDeep() {
		return
	}
	 := .Cursor.Descend(.Name())
	if  != nil {
		.app.Notify(.Error())
	} else {
		.codeArea.MutateState(func( *tk.CodeAreaState) {
			.Buffer = tk.CodeBuffer{}
		})
		updateState(, "")
	}
}

// NewNavigation creates a new navigation mode.
func ( cli.App,  NavigationSpec) Navigation {
	if .Cursor == nil {
		.Cursor = NewOSNavigationCursor()
	}
	if .WidthRatio == nil {
		.WidthRatio = func() [3]int { return [3]int{1, 3, 4} }
	}

	var  *navigation
	 = &navigation{
		NavigationSpec: ,
		app:            ,
		codeArea: tk.NewCodeArea(tk.CodeAreaSpec{
			Prompt: func() ui.Text {
				if .CopyState().ShowHidden {
					return modeLine(" NAVIGATING (show hidden) ", true)
				}
				return modeLine(" NAVIGATING ", true)
			},
			Highlighter: .Filter.Highlighter,
		}),
		colView: tk.NewColView(tk.ColViewSpec{
			Bindings: .Bindings,
			Weights: func(int) []int {
				 := .WidthRatio()
				return [:]
			},
			OnLeft:  func(tk.ColView) { .ascend() },
			OnRight: func(tk.ColView) { .descend() },
		}),
	}
	updateState(, "")
	return 
}

func ( *navigation) () string {
	,  := .colView.CopyState().Columns[1].(tk.ListBox)
	if ! {
		return ""
	}
	 := .CopyState()
	if 0 <= .Selected && .Selected < .Items.Len() {
		return .Items.(fileItems)[.Selected].Name()
	}
	return ""
}

func ( *navigation,  string) {
	 := .colView
	 := .Cursor
	 := .lastFilter
	 := .CopyState().ShowHidden

	var ,  tk.Widget

	.MutateState(func( *tk.ColViewState) {
		* = tk.ColViewState{
			Columns: []tk.Widget{
				tk.Empty{}, tk.Empty{}, tk.Empty{}},
			FocusColumn: 1,
		}
	})

	,  := .Parent()
	if  == nil {
		 = makeCol(, )
	} else {
		 = makeErrCol()
	}

	,  := .Current()
	if  == nil {
		 = makeColInner(
			,
			.Filter.makePredicate(),
			,
			func( tk.Items,  int) {
				 := makeCol(.(fileItems)[], )
				.MutateState(func( *tk.ColViewState) {
					.Columns[2] = 
				})
			})
		tryToSelectName(, .Name())
		if  != "" {
			tryToSelectName(, )
		}
	} else {
		 = makeErrCol()
		tryToSelectNothing()
	}

	.MutateState(func( *tk.ColViewState) {
		.Columns[0] = 
		.Columns[1] = 
	})
}

// Selects nothing if the widget is a listbox.
func ( tk.Widget) {
	,  := .(tk.ListBox)
	if ! {
		return
	}
	.Select(func(tk.ListBoxState) int { return -1 })
}

// Selects the item with the given name, if the widget is a listbox with
// fileItems and has such an item.
func ( tk.Widget,  string) {
	,  := .(tk.ListBox)
	if ! {
		// Do nothing
		return
	}
	.Select(func( tk.ListBoxState) int {
		,  := .Items.(fileItems)
		if ! {
			return 0
		}
		for ,  := range  {
			if .Name() ==  {
				return 
			}
		}
		return 0
	})
}

func ( NavigationFile,  bool) tk.Widget {
	return makeColInner(, func(string) bool { return true }, , nil)
}

func ( NavigationFile,  func(string) bool,  bool,  func(tk.Items, int)) tk.Widget {
	, ,  := .Read()
	if  != nil {
		return makeErrCol()
	}

	if  != nil {
		var  []NavigationFile
		for ,  := range  {
			 := .Name()
			 := len() > 0 && [0] == '.'
			if () && ( || !) {
				 = append(, )
			}
		}
		 = 
		sort.Slice(, func(,  int) bool {
			return [].Name() < [].Name()
		})
		return tk.NewListBox(tk.ListBoxSpec{
			Padding: 1, ExtendStyle: true, OnSelect: ,
			State: tk.ListBoxState{Items: fileItems()},
		})
	}

	 := strings.Split(sanitize(string()), "\n")
	return tk.NewTextView(tk.TextViewSpec{
		State:      tk.TextViewState{Lines: },
		Scrollable: true,
	})
}

func ( error) tk.Widget {
	return tk.Label{Content: ui.T(.Error(), ui.FgRed)}
}

type fileItems []NavigationFile

func ( fileItems) ( int) ui.Text {
	return [].ShowName()
}

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

func ( string) string {
	// Remove unprintable characters, and replace tabs with 4 spaces.
	var  strings.Builder
	for ,  := range  {
		if  == '\t' {
			.WriteString("    ")
		} else if  == '\n' || unicode.IsGraphic() {
			.WriteRune()
		}
	}
	return .String()
}

func ( *navigation) ( func(tk.ListBoxState) int) {
	if ,  := .colView.CopyState().Columns[1].(tk.ListBox);  {
		.Select()
	}
}

func ( *navigation) ( int) {
	if ,  := .colView.CopyState().Columns[2].(tk.TextView);  {
		.ScrollBy()
	}
}

func ( *navigation) () {
	.colView.Left()
}

func ( *navigation) () {
	.colView.Right()
}

func ( *navigation) ( func(bool) bool) {
	.MutateState(func( *navigationState) { .Filtering = (.Filtering) })
}

func ( *navigation) ( func(bool) bool) {
	.MutateState(func( *navigationState) { .ShowHidden = (.ShowHidden) })
	updateState(, .SelectedName())
}