package mode

import (
	
	
	
	
	
	

	
	
	
	
	
)

// Location is a mode for viewing location history and changing to a selected
// directory. It is based on the ComboBox widget.
type Location interface {
	tk.ComboBox
}

// LocationSpec is the configuration to start the location history feature.
type LocationSpec struct {
	// Key bindings.
	Bindings tk.Bindings
	// Store provides the directory history and the function to change directory.
	Store LocationStore
	// IteratePinned specifies pinned directories by calling the given function
	// with all pinned directories.
	IteratePinned func(func(string))
	// IterateHidden specifies hidden directories by calling the given function
	// with all hidden directories.
	IterateHidden func(func(string))
	// IterateWorksapce specifies workspace configuration.
	IterateWorkspaces LocationWSIterator
	// Configuration for the filter.
	Filter FilterSpec
}

// LocationStore defines the interface for interacting with the directory history.
type LocationStore interface {
	Dirs(blacklist map[string]struct{}) ([]store.Dir, error)
	Chdir(dir string) error
	Getwd() (string, error)
}

// A special score for pinned directories.
var pinnedScore = math.Inf(1)

var errNoDirectoryHistoryStore = errors.New("no directory history store")

// NewLocation creates a new location mode.
func ( cli.App,  LocationSpec) (Location, error) {
	if .Store == nil {
		return nil, errNoDirectoryHistoryStore
	}

	 := []store.Dir{}
	 := map[string]struct{}{}
	,  := "", ""

	if .IteratePinned != nil {
		.IteratePinned(func( string) {
			[] = struct{}{}
			 = append(, store.Dir{Score: pinnedScore, Path: })
		})
	}
	if .IterateHidden != nil {
		.IterateHidden(func( string) { [] = struct{}{} })
	}
	,  := .Store.Getwd()
	if  == nil {
		[] = struct{}{}
		if .IterateWorkspaces != nil {
			,  = .IterateWorkspaces.Parse()
		}
	}
	,  := .Store.Dirs()
	if  != nil {
		return nil, fmt.Errorf("db error: %v", )
	}
	for ,  := range  {
		if filepath.IsAbs(.Path) {
			 = append(, )
		} else if  != "" && hasPathPrefix(.Path, ) {
			 = append(, )
		}
	}

	 := locationList{}

	 := tk.NewComboBox(tk.ComboBoxSpec{
		CodeArea: tk.CodeAreaSpec{
			Prompt:      modePrompt(" LOCATION ", true),
			Highlighter: .Filter.Highlighter,
		},
		ListBox: tk.ListBoxSpec{
			Bindings: .Bindings,
			OnAccept: func( tk.Items,  int) {
				 := .(locationList).dirs[].Path
				if strings.HasPrefix(, ) {
					 =  + [len():]
				}
				 := .Store.Chdir()
				if  != nil {
					.Notify(.Error())
				}
				.SetAddon(nil, false)
			},
		},
		OnFilter: func( tk.ComboBox,  string) {
			.ListBox().Reset(.filter(.Filter.makePredicate()), 0)
		},
	})
	return , nil
}

func (,  string) bool {
	return  ==  ||
		strings.HasPrefix(, +string(filepath.Separator))
}

// LocationWSIterator is a function that iterates all workspaces by calling
// the passed function with the name and pattern of each kind of workspace.
// Iteration should stop when the called function returns false.
type LocationWSIterator func(func(kind, pattern string) bool)

// Parse returns whether the path matches any kind of workspace. If there is
// a match, it returns the kind of the workspace and the root. It there is no
// match, it returns "", "".
func ( LocationWSIterator) ( string) (,  string) {
	var ,  string
	(func(,  string) bool {
		if !strings.HasPrefix(, "^") {
			 = "^" + 
		}
		,  := regexp.Compile()
		if  != nil {
			// TODO(xiaq): Surface the error.
			return true
		}
		if  := .FindString();  != "" {
			,  = , 
			return false
		}
		return true
	})
	return , 
}

type locationList struct {
	dirs []store.Dir
}

func ( locationList) ( func(string) bool) locationList {
	var  []store.Dir
	for ,  := range .dirs {
		if (fsutil.TildeAbbr(.Path)) {
			 = append(, )
		}
	}
	return locationList{}
}

func ( locationList) ( int) ui.Text {
	return ui.T(fmt.Sprintf("%s %s",
		showScore(.dirs[].Score), fsutil.TildeAbbr(.dirs[].Path)))
}

func ( locationList) () int { return len(.dirs) }

func ( float64) string {
	if  == pinnedScore {
		return "  *"
	}
	return fmt.Sprintf("%3.0f", )
}