package ifelse

import (
	"fmt"
	"go/ast"
)

// Call contains the name of a function that deviates control flow.
type Call struct {
	Pkg  string // The package qualifier of the function, if not built-in.
	Name string // The function name.
}

// DeviatingFuncs lists known control flow deviating function calls.
var DeviatingFuncs = map[Call]BranchKind{
	{"os", "Exit"}:     Exit,
	{"log", "Fatal"}:   Exit,
	{"log", "Fatalf"}:  Exit,
	{"log", "Fatalln"}: Exit,
	{"", "panic"}:      Panic,
	{"log", "Panic"}:   Panic,
	{"log", "Panicf"}:  Panic,
	{"log", "Panicln"}: Panic,
}

// ExprCall gets the Call of an ExprStmt, if any.
func ExprCall(expr *ast.ExprStmt) (Call, bool) {
	call, ok := expr.X.(*ast.CallExpr)
	if !ok {
		return Call{}, false
	}
	switch v := call.Fun.(type) {
	case *ast.Ident:
		return Call{Name: v.Name}, true
	case *ast.SelectorExpr:
		if ident, ok := v.X.(*ast.Ident); ok {
			return Call{Name: v.Sel.Name, Pkg: ident.Name}, true
		}
	}
	return Call{}, false
}

// String returns the function name with package qualifier (if any)
func (f Call) String() string {
	switch {
	case f.Pkg != "":
		return fmt.Sprintf("%s.%s", f.Pkg, f.Name)
	default:
		return f.Name
	}
}
