package main

import "os"
import "fmt"
import "time"
import "flag"
import "os/exec"
import "hnakra/cli"
import "path/filepath"
import "hnakra/cmd/hnctl/spawn"

func main () {
	flag.Usage = func () {
		out := flag.CommandLine.Output()
		fmt.Fprintf(out, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(out, "  start\n")
		fmt.Fprintf(out, "    Start a service\n")
		fmt.Fprintf(out, "  stop\n")
		fmt.Fprintf(out, "    Stop a service\n")
		fmt.Fprintf(out, "  restart\n")
		fmt.Fprintf(out, "    Start and then stop a service\n")
		os.Exit(1)
	}

	// define commands
	startCommand := flag.NewFlagSet("start", flag.ExitOnError)
	startService := startCommand.String("s", "router", "Service to start")
	
	stopCommand := flag.NewFlagSet("stop", flag.ExitOnError)
	stopService := stopCommand.String("s", "router", "Service to stop")
	
	restartCommand := flag.NewFlagSet("restart", flag.ExitOnError)
	restartService := stopCommand.String("s", "router", "Service to restart")
	
	flag.Parse()

	// execute correct command
	if len(os.Args) < 2 {
		flag.Usage()
		os.Exit(1)
	}
	subCommandArgs := os.Args[2:]
	switch os.Args[1] {
	case "start":
		startCommand.Parse(subCommandArgs)
		execStart(*startService)
	case "stop":
		stopCommand.Parse(subCommandArgs)
		execStop(*stopService)
	case "restart":
		restartCommand.Parse(subCommandArgs)
		execStop(*restartService)
		execStart(*restartService)
	}
}

func execStart (service string) {
	fullName := cli.ServiceUser(service)
	cli.NeedRoot()
	
	pid, err := spawn.PidOf(fullName)
	if err == nil && spawn.Running(pid) {
		cli.Sayf("service is already running")
		return
	}
	
	uid, gid, err := spawn.LookupUID(fullName)
	if err != nil {
		cli.Sayf("cannot start service: %v", err)
		os.Exit(1)
	}
	
	path, err := exec.LookPath(fullName)
	if err != nil {
		cli.Sayf("cannot start service: %v", err)
		os.Exit(1)
	}

	logDir := filepath.Join("/var/log/", fullName)
	env := append(os.Environ(), "HNAKRA_LOG_DIR=" + logDir)
	err = ensureLogDir(logDir, int(uid), int(gid))
	if err != nil {
		cli.Sayf("cannot start service: %v", err)
		os.Exit(1)
	}

	// prepare pidfile. the service will be responsible for actually writing
	// to it
	err = ensurePidFile(spawn.PidFile(fullName), int(uid), int(gid))
	if err != nil {
		cli.Sayf("cannot start service: %v", err)
		os.Exit(1)
	}

	// spawn the service
	pid, err = spawn.Spawn(path, uid, gid, env)
	if err != nil {
		cli.Sayf("cannot start service: %v", err)
		os.Exit(1)
	}
	
	fmt.Println(pid)
}

func execStop (service string) {
	fullName := cli.ServiceUser(service)
	cli.NeedRoot()
	
	pid, err := spawn.PidOf(fullName)
	if err != nil || !spawn.Running(pid) {
		cli.Sayf("service is not running")
		return
	}

	process, err := os.FindProcess(pid)
	if err != nil {
		cli.Sayf("service is not running")
		return
	}

	err = spawn.KillAndWait(process, 16 * time.Second)
	if err != nil {
		cli.Sayf("could not stop service: %v", err)
		os.Exit(1)
	}
}

func ensureLogDir (directory string, uid, gid int) error {
	err := os.MkdirAll(directory, 0755)
	if err != nil { return err }
	err = os.Chmod(directory, 0770)
	if err != nil { return err }
	err = os.Chown(directory, uid, gid)
	if err != nil { return err }

	return nil
}

func ensurePidFile (file string, uid, gid int) error {
	pidFile, err := os.Create(file)
	if err != nil { return err }
	err = pidFile.Close()
	if err != nil { return err }
	
	err = os.Chmod(file, 0660)
	if err != nil { return err }
	err = os.Chown(file, uid, gid)
	if err != nil { return err }

	return nil
}
