package pkgpath

import (
	"path"
	"runtime"
)

const strMain = "main"

var (
	// mainPath is the import path of package main, or "main" if the import
	// path could not be determined.
	mainPath = strMain
)

// Main returns the import path of package main, the currently running
// command.  If the import path could not be determined, it returns "main" and
// false.
//
// It does not work on binaries compiled on filesystems that use a separator
// other than '/'.  It does not correctly detect the import path if a segment
// of the path is "src".
//
// This works by looking at the file path where the main.init function is
// defined, searching for the rightmost "src" directory if the recorded file
// path is absolute.
//
// If the command has been compiled with a trimpath argument in gcflags, the
// available file path is relative. In this case, the directory of the file is
// assumed to be exactly the import path. When using the trimpath compiler
// flag, be sure to trim off the relevant GOPATH segment and the "src" path
// segment that follows it, like so (in the case of a single-element GOPATH):
//
//     go build -gcflags "-trimpath $(go env GOPATH)/src" ./...
func Main() (string, bool) {
	return mainPath, (mainPath != strMain)
}

// Caller returns the import path of functions on the calling goroutine's
// stack.  The skip parameter indicates how many stack frames should be
// ascended, with 0 referring to the caller of Caller.  The return values are
// the import path of the caller and whether the import path could be
// determined.
func Caller(skip int) (string, bool) {
	return caller(skip + 1)
}

func init() {
	// When using gc version go1.4.1:
	//
	// skip = 0 gives us this func, ending with something like
	// "pkgpath.init·1".
	//
	// skip = 1 gives us this package's primary init func, which calls the
	// individual "func init" bodies.
	//
	// Larger skip values take us up the import chain, eventually leading to
	// package main's primary init func, which calls the primary init funcs of
	// its imported packages.  Its name is "main.init".

	var filename string
	for i := 0; ; i++ {
		pc, file, _, ok := runtime.Caller(i)
		if !ok {
			break
		}

		name := runtime.FuncForPC(pc).Name()
		if name == "main.init" {
			filename = file
		}
	}

	mainPath = fileImportPath(filename)
}

func fileImportPath(filename string) string {
	if !path.IsAbs(filename) {
		// This is a synthetic file path, as is generated by go test.
		return path.Dir(filename)
	}

	prefix, suffix := filename, ""
	var revparts, parts []string
	for {
		prefix, suffix = path.Split(prefix)
		prefix = path.Clean(prefix)

		if len(suffix) == 0 {
			// We got to the root without finding a "src" segment - maybe this
			// is "go run" of a file that's not in GOPATH?
			return strMain
		}
		if suffix == "src" {
			break
		}

		revparts = append(revparts, suffix)
	}

	for i := len(revparts) - 1; i >= 0; i-- {
		parts = append(parts, revparts[i])
	}
	return path.Dir(path.Join(parts...))
}
