package daemon

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
)

const (
	envVarName  = "_GO_DAEMON"
	envVarValue = "1"
)

// func Reborn daemonize process. Function Reborn calls ForkExec
// in the parent process and terminates him. In the child process,
// function sets umask, work dir and calls Setsid. Function sets
// for child process environment variable _GO_DAEMON=1 - the mark,
// might used for debug.
func Reborn(umask uint32, workDir string) (err error) {

	if !WasReborn() {
		// parent process - fork and exec
		var path string
		if path, err = filepath.Abs(os.Args[0]); err != nil {
			return
		}

		cmd := prepareCommand(path)

		if err = cmd.Start(); err != nil {
			return
		}

		os.Exit(0)
	}

	// child process - daemon
	syscall.Umask(int(umask))

	if len(workDir) != 0 {
		if err = os.Chdir(workDir); err != nil {
			return
		}
	}

	_, err = syscall.Setsid()

	// Do not required redirect std
	// to /dev/null, this work was
	// done function ForkExec

	return
}

// func WasReborn, return true if the process has environment
// variable _GO_DAEMON=1 (child process).
func WasReborn() bool {
	return os.Getenv(envVarName) == envVarValue
}

func prepareCommand(path string) (cmd *exec.Cmd) {

	// prepare command-line arguments
	cmd = exec.Command(path, os.Args[1:]...)

	// prepare environment variables
	envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue)
	cmd.Env = append(os.Environ(), envVar)

	return
}

// func RedirectStream redirects file s to file target.
func RedirectStream(s, target *os.File) (err error) {

	stdoutFd := int(s.Fd())
	if err = syscall.Close(stdoutFd); err != nil {
		return
	}

	err = syscall.Dup2(int(target.Fd()), stdoutFd)

	return
}
