package interp

import (
	
	
	
	
	
	
)

// importSrc calls gta on the source code for the package identified by
// importPath. rPath is the relative path to the directory containing the source
// code for the package. It can also be "main" as a special value.
func ( *Interpreter) (,  string,  bool) (string, error) {
	var  string
	var  error

	if .srcPkg[] != nil {
		,  := .pkgNames[]
		if ! {
			return "", fmt.Errorf("inconsistent knowledge about %s", )
		}
		return , nil
	}

	// For relative import paths in the form "./xxx" or "../xxx", the initial
	// base path is the directory of the interpreter input file, or "." if no file
	// was provided.
	// In all other cases, absolute import paths are resolved from the GOPATH
	// and the nested "vendor" directories.
	if isPathRelative() {
		if  == mainID {
			 = "."
		}
		 = filepath.Join(filepath.Dir(.name), , )
	} else if , ,  = .pkgDir(.context.GOPATH, , );  != nil {
		// Try again, assuming a root dir at the source location.
		if ,  = .rootFromSourceLocation();  != nil {
			return "", 
		}
		if , ,  = .pkgDir(.context.GOPATH, , );  != nil {
			return "", 
		}
	}

	if .rdir[] {
		return "", fmt.Errorf("import cycle not allowed\n\timports %s", )
	}
	.rdir[] = true

	,  := fs.ReadDir(.opt.filesystem, )
	if  != nil {
		return "", 
	}

	var  []*node
	var  []*node
	 := make(map[string][]*node)

	var  *node
	var  string

	// Parse source files.
	for ,  := range  {
		 := .Name()
		if skipFile(&.context, , ) {
			continue
		}

		 = filepath.Join(, )
		var  []byte
		if ,  = fs.ReadFile(.opt.filesystem, );  != nil {
			return "", 
		}

		,  := .parse(string(), , false)
		if  != nil {
			return "", 
		}
		if  == nil {
			continue
		}

		var  string
		if , ,  = .ast();  != nil {
			return "", 
		}
		if  == nil {
			continue
		}

		if .astDot {
			 := .dotCmd
			if  == "" {
				 = defaultDotCmd(, "yaegi-ast-")
			}
			.astDot(dotWriter(), )
		}
		if  == "" {
			 = 
		} else if  !=  &&  {
			return "", fmt.Errorf("found packages %s and %s in %s", , , )
		}
		 = append(, )

		 := effectivePkg(, )
		var  []*node
		,  = .gta(, , , )
		if  != nil {
			return "", 
		}
		[] = append([], ...)
	}

	// Revisit incomplete nodes where GTA could not complete.
	for ,  := range  {
		if  = .gtaRetry(, , );  != nil {
			return "", 
		}
	}

	// Generate control flow graphs.
	for ,  := range  {
		var  []*node
		if ,  = .cfg(, nil, , );  != nil {
			return "", 
		}
		 = append(, ...)
	}

	// Register source package in the interpreter. The package contains only
	// the global symbols in the package scope.
	.mutex.Lock()
	 := .scopes[]
	if  == nil {
		.mutex.Unlock()
		// A nil scope means that no even an empty package is created from source.
		return "", fmt.Errorf("no Go files in %s", )
	}
	.srcPkg[] = .sym
	.pkgNames[] = 

	.frame.mutex.Lock()
	.resizeFrame()
	.frame.mutex.Unlock()
	.mutex.Unlock()

	// Once all package sources have been parsed, execute entry points then init functions.
	for ,  := range  {
		if  = genRun();  != nil {
			return "", 
		}
		.run(, nil)
	}

	// Wire and execute global vars in global scope gs.
	,  := genGlobalVars(, )
	if  != nil {
		return "", 
	}
	.run(, nil)

	// Add main to list of functions to run, after all inits.
	if  := .sym[mainID];  == mainID &&  != nil &&  {
		 = append(, .node)
	}

	for ,  := range  {
		.run(, .frame)
	}

	return , nil
}

// rootFromSourceLocation returns the path to the directory containing the input
// Go file given to the interpreter, relative to $GOPATH/src.
// It is meant to be called in the case when the initial input is a main package.
func ( *Interpreter) () (string, error) {
	 := .name
	if  == DefaultSourceName {
		return "", nil
	}
	,  := os.Getwd()
	if  != nil {
		return "", 
	}
	 := filepath.Join(, filepath.Dir())
	 := strings.TrimPrefix(, filepath.Join(.context.GOPATH, "src")+"/")
	if  ==  {
		return "", fmt.Errorf("package location %s not in GOPATH", )
	}
	return , nil
}

// pkgDir returns the absolute path in filesystem for a package given its import path
// and the root of the subtree dependencies.
func ( *Interpreter) ( string, ,  string) (string, string, error) {
	 := filepath.Join(, "vendor")
	 := filepath.Join(, "src", , )

	if ,  := fs.Stat(.opt.filesystem, );  == nil {
		return , , nil // found!
	}

	 = filepath.Join(, "src", effectivePkg(, ))

	if ,  := fs.Stat(.opt.filesystem, );  == nil {
		return , , nil // found!
	}

	if  == "" {
		if .context.GOPATH == "" {
			return "", "", fmt.Errorf("unable to find source related to: %q. Either the GOPATH environment variable, or the Interpreter.Options.GoPath needs to be set", )
		}
		return "", "", fmt.Errorf("unable to find source related to: %q", )
	}

	 := filepath.Join(, "src", )
	,  := previousRoot(.opt.filesystem, , )
	if  != nil {
		return "", "", 
	}

	return .(, , )
}

const vendor = "vendor"

// Find the previous source root (vendor > vendor > ... > GOPATH).
func previousRoot( fs.FS, ,  string) (string, error) {
	 = filepath.Clean()
	,  := filepath.Split()
	 = filepath.Clean()

	// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
	if  != mainID &&  != vendor {
		 = strings.TrimSuffix(, string(filepath.Separator))
		 := strings.TrimSuffix(strings.TrimSuffix(, ), string(filepath.Separator))

		// look for the closest vendor in one of our direct ancestors, as it takes priority.
		var  string
		for {
			,  := fs.Stat(, filepath.Join(, vendor))
			if  == nil && .IsDir() {
				 = strings.TrimPrefix(strings.TrimPrefix(, ), string(filepath.Separator))
				break
			}
			if !errors.Is(, fs.ErrNotExist) {
				return "", 
			}
			// stop when we reach GOPATH/src
			if  ==  {
				break
			}

			// stop when we reach GOPATH/src/blah
			 = filepath.Dir()
			if  ==  {
				break
			}

			// just an additional failsafe, stop if we reach the filesystem root, or dot (if
			// we are dealing with relative paths).
			// TODO(mpl): It should probably be a critical error actually,
			// as we shouldn't have gone that high up in the tree.
			// TODO(dennwc): This partially fails on Windows, since it cannot recognize drive letters as "root".
			if  == string(filepath.Separator) ||  == "." ||  == "" {
				break
			}
		}

		if  != "" {
			return , nil
		}
	}

	// TODO(mpl): the algorithm below might be redundant with the one above,
	// but keeping it for now. Investigate/simplify/remove later.
	 := strings.Split(, string(filepath.Separator))
	var  int
	for  := len() - 1;  >= 0; -- {
		if [] == "vendor" {
			 = 
			break
		}
	}

	if  == 0 {
		return "", nil
	}

	return filepath.Join([:]...), nil
}

func effectivePkg(,  string) string {
	 := strings.Split(, string(filepath.Separator))
	 := strings.Split(, string(filepath.Separator))

	var  []string

	 := 0
	 := 0
	for  := 0;  < len(); ++ {
		 := [len()-1-]

		 := len() - 1 - 
		if  > 0 &&  == [] &&  != 0 {
			 = 
			++
		} else if  ==  {
			 = append(, )
		}
	}

	var  string
	for  := len() - 1;  >= 0; -- {
		 = filepath.Join(, [])
	}

	return filepath.Join(, )
}

// isPathRelative returns true if path starts with "./" or "../".
// It is intended for use on import paths, where "/" is always the directory separator.
func isPathRelative( string) bool {
	return strings.HasPrefix(, "./") || strings.HasPrefix(, "../")
}