package cmdext

import (
	"fmt"
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
	"time"
)

type CommandRunner struct {
	program          string
	args             []string
	timeout          *time.Duration
	env              []string
	listener         []CommandListener
	enforceExitCodes *[]int
	enforceNoTimeout bool
}

func Runner(program string) *CommandRunner {
	return &CommandRunner{
		program:          program,
		args:             make([]string, 0),
		timeout:          nil,
		env:              make([]string, 0),
		listener:         make([]CommandListener, 0),
		enforceExitCodes: nil,
		enforceNoTimeout: false,
	}
}

func (r *CommandRunner) Arg(arg string) *CommandRunner {
	r.args = append(r.args, arg)
	return r
}

func (r *CommandRunner) Args(arg []string) *CommandRunner {
	r.args = append(r.args, arg...)
	return r
}

func (r *CommandRunner) Timeout(timeout time.Duration) *CommandRunner {
	r.timeout = &timeout
	return r
}

func (r *CommandRunner) Env(key, value string) *CommandRunner {
	r.env = append(r.env, fmt.Sprintf("%s=%s", key, value))
	return r
}

func (r *CommandRunner) RawEnv(env string) *CommandRunner {
	r.env = append(r.env, env)
	return r
}

func (r *CommandRunner) Envs(env []string) *CommandRunner {
	r.env = append(r.env, env...)
	return r
}

func (r *CommandRunner) EnsureExitcode(arg ...int) *CommandRunner {
	r.enforceExitCodes = langext.Ptr(langext.ForceArray(arg))
	return r
}

func (r *CommandRunner) FailOnExitCode() *CommandRunner {
	r.enforceExitCodes = langext.Ptr([]int{0})
	return r
}

func (r *CommandRunner) FailOnTimeout() *CommandRunner {
	r.enforceNoTimeout = true
	return r
}

func (r *CommandRunner) Listen(lstr CommandListener) *CommandRunner {
	r.listener = append(r.listener, lstr)
	return r
}

func (r *CommandRunner) ListenStdout(lstr func(string)) *CommandRunner {
	r.listener = append(r.listener, genericCommandListener{_readStdoutLine: &lstr})
	return r
}

func (r *CommandRunner) ListenStderr(lstr func(string)) *CommandRunner {
	r.listener = append(r.listener, genericCommandListener{_readStderrLine: &lstr})
	return r
}

func (r *CommandRunner) Run() (CommandResult, error) {
	return run(*r)
}
