// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

//go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package pinentry

import (
	"fmt"
	"os"
	"path/filepath"

	"os/exec"

	"github.com/keybase/client/go/logger"
)

//
// some borrowed from here:
//
//  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
//
// Under the Apache 2.0 license
//

func canExec(s string) error {
	fi, err := os.Stat(s)
	if err != nil {
		return err
	}
	mode := fi.Mode()

	//
	// Only consider non-directories that have at least one +x
	//  bit set.
	//
	// TODO: Recheck this on Windows!
	//   See here for lookpath: http://golang.org/src/pkg/os/exec/lp_windows.go
	//
	// Similar to check from exec.LookPath below
	//   See here: http://golang.org/src/pkg/os/exec/lp_unix.go
	//
	switch {
	case mode.IsDir():
		return fmt.Errorf("Program '%s' is a directory", s)
	case int(mode)&0111 == 0:
		return fmt.Errorf("Program '%s' isn't executable", s)
	default:
		return nil
	}
}

func FindPinentry(log logger.Logger) (string, error) {
	if !HasWindows() {
		return "", fmt.Errorf("Can't spawn gui window, not using pinentry")
	}
	bins := []string{
		// If you install MacTools you'll wind up with this pinentry
		"/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac",
	}

	extraPaths := []string{}

	log.Debug("+ FindPinentry()")

	cmds := []string{
		"pinentry-gtk-2",
		"pinentry-qt4",
		"pinentry",
	}

	checkFull := func(s string) bool {
		log.Debug("| Check fullpath %s", s)
		found := (canExec(s) == nil)
		if found {
			log.Debug("- Found: %s", s)
		}
		return found
	}

	for _, b := range bins {
		if checkFull(b) {
			return b, nil
		}
	}

	path := os.Getenv("PATH")
	for _, c := range cmds {
		log.Debug("| Looking for %s in standard PATH %s", c, path)
		fullc, err := exec.LookPath(c)
		if err == nil {
			log.Debug("- Found %s", fullc)
			return fullc, nil
		}
	}

	for _, ep := range extraPaths {
		for _, c := range cmds {
			full := filepath.Join(ep, c)
			if checkFull(full) {
				return full, nil
			}
		}
	}

	log.Debug("- FindPinentry: none found")
	return "", fmt.Errorf("No pinentry found, checked a bunch of different places")
}

func (pe *Pinentry) GetTerminalName() {
	// Noop on all platforms but Windows
}
