package mode

import (
	
	
	
	
	

	
	
)

// NavigationCursor represents a cursor for navigating in a potentially virtual
// filesystem.
type NavigationCursor interface {
	// Current returns a File that represents the current directory.
	Current() (NavigationFile, error)
	// Parent returns a File that represents the parent directory. It may return
	// nil if the current directory is the root of the filesystem.
	Parent() (NavigationFile, error)
	// Ascend navigates to the parent directory.
	Ascend() error
	// Descend navigates to the named child directory.
	Descend(name string) error
}

// NavigationFile represents a potentially virtual file.
type NavigationFile interface {
	// Name returns the name of the file.
	Name() string
	// ShowName returns a styled filename.
	ShowName() ui.Text
	// IsDirDeep returns whether the file is itself a directory or a symlink to
	// a directory.
	IsDirDeep() bool
	// Read returns either a list of File's if the File represents a directory,
	// a (possibly incomplete) slice of bytes if the File represents a normal
	// file, or an error if the File cannot be read.
	Read() ([]NavigationFile, []byte, error)
}

// NewOSNavigationCursor returns a NavigationCursor backed by the OS.
func () NavigationCursor {
	return osCursor{lscolors.GetColorist()}
}

type osCursor struct{ colorist lscolors.Colorist }

func ( osCursor) () (NavigationFile, error) {
	,  := filepath.Abs(".")
	if  != nil {
		return nil, 
	}
	return file{filepath.Base(), , os.ModeDir, .colorist}, nil
}

func ( osCursor) () (NavigationFile, error) {
	if ,  := filepath.Abs(".");  == "/" {
		return emptyDir{}, nil
	}
	,  := filepath.Abs("..")
	if  != nil {
		return nil, 
	}
	return file{filepath.Base(), , os.ModeDir, .colorist}, nil
}

func ( osCursor) () error { return os.Chdir("..") }

func ( osCursor) ( string) error { return os.Chdir() }

type emptyDir struct{}

func (emptyDir) () string                            { return "" }
func (emptyDir) () ui.Text                       { return nil }
func (emptyDir) () bool                         { return true }
func (emptyDir) () ([]NavigationFile, []byte, error) { return []NavigationFile{}, nil, nil }

type file struct {
	name     string
	path     string
	mode     os.FileMode
	colorist lscolors.Colorist
}

func ( file) () string { return .name }

func ( file) () ui.Text {
	 := .colorist.GetStyle(.path)
	return ui.Text{&ui.Segment{
		Style: ui.StyleFromSGR(), Text: .name}}
}

func ( file) () bool {
	if .mode.IsDir() {
		// File itself is a directory; return true and save a stat call.
		return true
	}
	,  := os.Stat(.path)
	return  == nil && .IsDir()
}

const previewBytes = 64 * 1024

var (
	errDevice     = errors.New("no preview for device file")
	errNamedPipe  = errors.New("no preview for named pipe")
	errSocket     = errors.New("no preview for socket file")
	errCharDevice = errors.New("no preview for char device")
	errNonUTF8    = errors.New("no preview for non-utf8 file")
)

var specialFileModes = []struct {
	mode os.FileMode
	err  error
}{
	{os.ModeDevice, errDevice},
	{os.ModeNamedPipe, errNamedPipe},
	{os.ModeSocket, errSocket},
	{os.ModeCharDevice, errCharDevice},
}

func ( file) () ([]NavigationFile, []byte, error) {
	,  := os.Open(.path)
	if  != nil {
		return nil, nil, 
	}
	defer .Close()

	,  := .Stat()
	if  != nil {
		return nil, nil, 
	}

	if .IsDir() {
		,  := .Readdir(0)
		if  != nil {
			return nil, nil, 
		}
		 := make([]NavigationFile, len())
		for ,  := range  {
			[] = file{
				.Name(),
				filepath.Join(.path, .Name()),
				.Mode(),
				.colorist,
			}
		}
		return , nil, 
	}

	for ,  := range specialFileModes {
		if .Mode()&.mode != 0 {
			return nil, nil, .err
		}
	}

	var  [previewBytes]byte
	,  := .Read([:])
	if  != nil &&  != io.EOF {
		return nil, nil, 
	}

	 := [:]
	if !utf8.Valid() {
		return nil, nil, errNonUTF8
	}

	return nil, , nil
}