package jen

import (
	
	
	
	
	
	
)

// NewFile Creates a new file, with the specified package name.
func ( string) *File {
	return &File{
		Group: &Group{
			multi: true,
		},
		name:    ,
		imports: map[string]importdef{},
		hints:   map[string]importdef{},
	}
}

// NewFilePath creates a new file while specifying the package path - the
// package name is inferred from the path.
func ( string) *File {
	return &File{
		Group: &Group{
			multi: true,
		},
		name:    guessAlias(),
		path:    ,
		imports: map[string]importdef{},
		hints:   map[string]importdef{},
	}
}

// NewFilePathName creates a new file with the specified package path and name.
func (,  string) *File {
	return &File{
		Group: &Group{
			multi: true,
		},
		name:    ,
		path:    ,
		imports: map[string]importdef{},
		hints:   map[string]importdef{},
	}
}

// File represents a single source file. Package imports are managed
// automatically by File.
type File struct {
	*Group
	name        string
	path        string
	imports     map[string]importdef
	hints       map[string]importdef
	comments    []string
	headers     []string
	cgoPreamble []string
	// NoFormat can be set to true to disable formatting of the generated source. This may be useful
	// when performance is critical, and readable code is not required.
	NoFormat bool
	// If you're worried about generated package aliases conflicting with local variable names, you
	// can set a prefix here. Package foo becomes {prefix}_foo.
	PackagePrefix string
	// CanonicalPath adds a canonical import path annotation to the package clause.
	CanonicalPath string
}

// importdef is used to differentiate packages where we know the package name from packages where the
// import is aliased. If alias == false, then name is the actual package name, and the import will be
// rendered without an alias. If used == false, the import has not been used in code yet and should be
// excluded from the import block.
type importdef struct {
	name  string
	alias bool
}

// HeaderComment adds a comment to the top of the file, above any package
// comments. A blank line is rendered below the header comments, ensuring
// header comments are not included in the package doc.
func ( *File) ( string) {
	.headers = append(.headers, )
}

// PackageComment adds a comment to the top of the file, above the package
// keyword.
func ( *File) ( string) {
	.comments = append(.comments, )
}

// CgoPreamble adds a cgo preamble comment that is rendered directly before the "C" pseudo-package
// import.
func ( *File) ( string) {
	.cgoPreamble = append(.cgoPreamble, )
}

// Anon adds an anonymous import.
func ( *File) ( ...string) {
	for ,  := range  {
		.imports[] = importdef{name: "_", alias: true}
	}
}

// ImportName provides the package name for a path. If specified, the alias will be omitted from the
// import block. This is optional. If not specified, a sensible package name is used based on the path
// and this is added as an alias in the import block.
func ( *File) (,  string) {
	.hints[] = importdef{name: , alias: false}
}

// ImportNames allows multiple names to be imported as a map. Use the [gennames](gennames) command to
// automatically generate a go file containing a map of a selection of package names.
func ( *File) ( map[string]string) {
	for ,  := range  {
		.hints[] = importdef{name: , alias: false}
	}
}

// ImportAlias provides the alias for a package path that should be used in the import block. A
// period can be used to force a dot-import.
func ( *File) (,  string) {
	.hints[] = importdef{name: , alias: true}
}

func ( *File) ( string) bool {
	return .path == 
}

func ( *File) ( string) bool {
	// multiple dot-imports are ok
	if  == "." {
		return true
	}
	// the import alias is invalid if it's a reserved word
	if IsReservedWord() {
		return false
	}
	// the import alias is invalid if it's already been registered
	for ,  := range .imports {
		if  == .name {
			return false
		}
	}
	return true
}

func ( *File) ( string) bool {
	if ,  := .hints[];  {
		return .name == "." && .alias
	}
	return false
}

func ( *File) ( string) string {
	if .isLocal() {
		// notest
		// should never get here because in Qual the packageToken will be null,
		// so render will never be called.
		return ""
	}

	// if the path has been registered previously, simply return the name
	 := .imports[]
	if .name != "" && .name != "_" {
		return .name
	}

	// special case for "C" pseudo-package
	if  == "C" {
		.imports["C"] = importdef{name: "C", alias: false}
		return "C"
	}

	var  string
	var  bool

	if  := .hints[]; .name != "" {
		// look up the path in the list of provided package names and aliases by ImportName / ImportAlias
		 = .name
		 = .alias
	} else if standardLibraryHints[] != "" {
		// look up the path in the list of standard library packages
		 = standardLibraryHints[]
		 = false
	} else {
		// if a hint is not found for the package, guess the alias from the package path
		 = guessAlias()
		 = true
	}

	// If the name is invalid or has been registered already, make it unique by appending a number
	 := 
	 := 0
	for !.isValidAlias() {
		++
		 = fmt.Sprintf("%s%d", , )
	}

	// If we've changed the name to make it unique, it should definitely be an alias
	if  !=  {
		 = true
	}

	// Only add a prefix if the name is an alias
	if .PackagePrefix != "" &&  {
		 = .PackagePrefix + "_" + 
	}

	// Register the eventual name
	.imports[] = importdef{name: , alias: }

	return 
}

// GoString renders the File for testing. Any error will cause a panic.
func ( *File) () string {
	 := &bytes.Buffer{}
	if  := .Render();  != nil {
		panic()
	}
	return .String()
}

func guessAlias( string) string {
	 := 

	if strings.HasSuffix(, "/") {
		// training slashes are usually tolerated, so we can get rid of one if
		// it exists
		 = [:len()-1]
	}

	if strings.Contains(, "/") {
		// if the path contains a "/", use the last part
		 = [strings.LastIndex(, "/")+1:]
	}

	// alias should be lower case
	 = strings.ToLower()

	// alias should now only contain alphanumerics
	 := regexp.MustCompile(`[^a-z0-9]`)
	 = .ReplaceAllString(, "")

	// can't have a first digit, per Go identifier rules, so just skip them
	for ,  := utf8.DecodeRuneInString(); unicode.IsDigit(); ,  = utf8.DecodeRuneInString() {
		 = [:]
	}

	// If path part was all digits, we may be left with an empty string. In this case use "pkg" as the alias.
	if  == "" {
		 = "pkg"
	}

	return 
}