package source

import (
	
	
	
	
	
	
	
	
	
	
	
)

// Update is set by the -update flag. It indicates the user running the tests
// would like to update any golden values.
var Update bool

func () {
	flag.BoolVar(&Update, "update", false, "update golden values")
}

// ErrNotFound indicates that UpdateExpectedValue failed to find the
// variable to update, likely because it is not a package level variable.
var ErrNotFound = fmt.Errorf("failed to find variable for update of golden value")

// UpdateExpectedValue looks for a package-level variable with a name that
// starts with expected in the arguments to the caller. If the variable is
// found, the value of the variable will be updated to value of the other
// argument to the caller.
func ( int, ,  interface{}) error {
	, , ,  := runtime.Caller( + 1)
	if ! {
		return errors.New("failed to get call stack")
	}
	debug("call stack position: %s:%d", , )

	 := token.NewFileSet()
	,  := parser.ParseFile(, , nil, parser.AllErrors|parser.ParseComments)
	if  != nil {
		return fmt.Errorf("failed to parse source file %s: %w", , )
	}

	,  := getCallExprArgs(, , )
	if  != nil {
		return fmt.Errorf("call from %s:%d: %w", , , )
	}

	if len() < 3 {
		debug("not enough arguments %d: %v",
			len(), debugFormatNode{Node: &ast.CallExpr{Args: }})
		return ErrNotFound
	}

	,  := getIdentForExpectedValueArg()
	if  < 0 ||  == nil {
		debug("no arguments started with the word 'expected': %v",
			debugFormatNode{Node: &ast.CallExpr{Args: }})
		return ErrNotFound
	}

	 := 
	if  == 1 {
		 = 
	}

	,  := .(string)
	if ! {
		debug("value must be type string, got %T", )
		return ErrNotFound
	}
	return UpdateVariable(, , , , )
}

// UpdateVariable writes to filename the contents of astFile with the value of
// the variable updated to value.
func (
	 string,
	 *token.FileSet,
	 *ast.File,
	 *ast.Ident,
	 string,
) error {
	 := .Obj
	if  == nil {
		return ErrNotFound
	}
	if .Kind != ast.Con && .Kind != ast.Var {
		debug("can only update var and const, found %v", .Kind)
		return ErrNotFound
	}

	switch decl := .Decl.(type) {
	case *ast.ValueSpec:
		if len(.Names) != 1 {
			debug("more than one name in ast.ValueSpec")
			return ErrNotFound
		}

		.Values[0] = &ast.BasicLit{
			Kind:  token.STRING,
			Value: "`" +  + "`",
		}

	case *ast.AssignStmt:
		if len(.Lhs) != 1 {
			debug("more than one name in ast.AssignStmt")
			return ErrNotFound
		}

		.Rhs[0] = &ast.BasicLit{
			Kind:  token.STRING,
			Value: "`" +  + "`",
		}

	default:
		debug("can only update *ast.ValueSpec, found %T", .Decl)
		return ErrNotFound
	}

	var  bytes.Buffer
	if  := format.Node(&, , );  != nil {
		return fmt.Errorf("failed to format file after update: %w", )
	}

	,  := os.Create()
	if  != nil {
		return fmt.Errorf("failed to open file %v: %w", , )
	}
	if _,  = .Write(.Bytes());  != nil {
		return fmt.Errorf("failed to write file %v: %w", , )
	}
	if  := .Sync();  != nil {
		return fmt.Errorf("failed to sync file %v: %w", , )
	}
	return nil
}

func ( []ast.Expr) (int, *ast.Ident) {
	for  := 1;  < 3; ++ {
		switch e := [].(type) {
		case *ast.Ident:
			if strings.HasPrefix(strings.ToLower(.Name), "expected") {
				return , 
			}
		}
	}
	return -1, nil
}