package main

import (
	"bytes"
	"errors"
	"os/exec"
	"path/filepath"
	"strings"
)

// Tool info
type Tool struct {
	Args   []string `yaml:"args,omitempty" json:"args,omitempty"`
	Method string   `yaml:"method,omitempty" json:"method,omitempty"`
	Status bool     `yaml:"status,omitempty" json:"status,omitempty"`
	dir    bool
	isTool bool
	method []string
	cmd    []string
	name   string
}

// Tools go
type Tools struct {
	Fix      Tool `yaml:"fix,omitempty" json:"fix,omitempty"`
	Clean    Tool `yaml:"clean,omitempty" json:"clean,omitempty"`
	Vet      Tool `yaml:"vet,omitempty" json:"vet,omitempty"`
	Fmt      Tool `yaml:"fmt,omitempty" json:"fmt,omitempty"`
	Test     Tool `yaml:"test,omitempty" json:"test,omitempty"`
	Generate Tool `yaml:"generate,omitempty" json:"generate,omitempty"`
	Install  Tool `yaml:"install,omitempty" json:"install,omitempty"`
	Build    Tool `yaml:"build,omitempty" json:"build,omitempty"`
	Run      bool `yaml:"run,omitempty" json:"run,omitempty"`
}

// Setup go tools
func (t *Tools) Setup() {
	// go clean
	if t.Clean.Status {
		t.Clean.name = "Clean"
		t.Clean.isTool = true
		t.Clean.cmd = replace([]string{"go clean"}, t.Clean.Method)
		t.Clean.Args = split([]string{}, t.Clean.Args)
	}
	// go generate
	if t.Generate.Status {
		t.Generate.dir = true
		t.Generate.isTool = true
		t.Generate.name = "Generate"
		t.Generate.cmd = replace([]string{"go", "generate"}, t.Generate.Method)
		t.Generate.Args = split([]string{}, t.Generate.Args)
	}
	// go fix
	if t.Fix.Status {
		t.Fix.name = "Fix"
		t.Fix.isTool = true
		t.Fix.cmd = replace([]string{"go fix"}, t.Fix.Method)
		t.Fix.Args = split([]string{}, t.Fix.Args)
	}
	// go fmt
	if t.Fmt.Status {
		if len(t.Fmt.Args) == 0 {
			t.Fmt.Args = []string{"-s", "-w", "-e", "./"}
		}
		t.Fmt.name = "Fmt"
		t.Fmt.isTool = true
		t.Fmt.cmd = replace([]string{"gofmt"}, t.Fmt.Method)
		t.Fmt.Args = split([]string{}, t.Fmt.Args)
	}
	// go vet
	if t.Vet.Status {
		t.Vet.dir = true
		t.Vet.name = "Vet"
		t.Vet.isTool = true
		t.Vet.cmd = replace([]string{"go", "vet"}, t.Vet.Method)
		t.Vet.Args = split([]string{}, t.Vet.Args)
	}
	// go test
	if t.Test.Status {
		t.Test.dir = true
		t.Test.isTool = true
		t.Test.name = "Test"
		t.Test.cmd = replace([]string{"go", "test"}, t.Test.Method)
		t.Test.Args = split([]string{}, t.Test.Args)
	}
	// go install
	t.Install.name = "Install"
	t.Install.cmd = replace([]string{"go", "install"}, t.Install.Method)
	t.Install.Args = split([]string{}, t.Install.Args)
	// go build
	if t.Build.Status {
		t.Build.name = "Build"
		t.Build.cmd = replace([]string{"go", "build"}, t.Build.Method)
		t.Build.Args = split([]string{}, t.Build.Args)
	}
}

// Exec a go tool
func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
	if t.dir && filepath.Ext(path) != "" {
		path = filepath.Dir(path)
	}
	if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "") {
		args := []string{}
		if strings.HasSuffix(path, ".go") {
			args = append(t.Args, path)
			path = filepath.Dir(path)
		}
		if s := ext(path); s == "" || s == "go" {
			var out, stderr bytes.Buffer
			done := make(chan error)
			args = append(t.cmd, t.Args...)
			cmd := exec.Command(args[0], args[1:]...)
			cmd.Dir = path
			cmd.Stdout = &out
			cmd.Stderr = &stderr
			// Start command
			cmd.Start()
			go func() { done <- cmd.Wait() }()
			// Wait a result
			select {
			case <-stop:
				// Stop running command
				cmd.Process.Kill()
			case err := <-done:
				// Command completed
				response.Name = t.name
				if err != nil {
					response.Err = errors.New(stderr.String() + out.String())
				} else {
					response.Out = out.String()
				}
			}
		}
	}
	return
}

// Compile is used for build and install
func (t *Tool) Compile(path string, stop <-chan bool) (response Response) {
	var out bytes.Buffer
	var stderr bytes.Buffer
	done := make(chan error)
	args := append(t.cmd, t.Args...)
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = filepath.Dir(path)
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	// Start command
	cmd.Start()
	go func() { done <- cmd.Wait() }()
	// Wait a result
	select {
	case <-stop:
		// Stop running command
		cmd.Process.Kill()
	case err := <-done:
		// Command completed
		response.Name = t.name
		if err != nil {
			response.Err = errors.New(stderr.String() + err.Error())
		}
	}
	return
}