package main

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

const defaultWindowName = "smug_def"

func ExpandPath(path string) string {
	if strings.HasPrefix(path, "~/") {
		userHome, err := os.UserHomeDir()
		if err != nil {
			return path
		}

		return strings.Replace(path, "~", userHome, 1)
	}

	return path
}

func Contains(slice []string, s string) bool {
	for _, e := range slice {
		if e == s {
			return true
		}
	}

	return false
}

type Smug struct {
	tmux      Tmux
	commander Commander
}

func (smug Smug) execShellCommands(commands []string, path string) error {
	for _, c := range commands {
		cmd := exec.Command("/bin/sh", "-c", c)
		cmd.Dir = path

		_, err := smug.commander.Exec(cmd)
		if err != nil {
			return err
		}
	}
	return nil
}

func (smug Smug) setEnvVariables(target string, env map[string]string) error {
	for key, value := range env {
		_, err := smug.tmux.SetEnv(target, key, value)
		if err != nil {
			return err
		}
	}

	return nil
}

func (smug Smug) switchOrAttach(target string, attach bool, insideTmuxSession bool) error {
	if insideTmuxSession && attach {
		return smug.tmux.SwitchClient(target)
	} else if !insideTmuxSession {
		return smug.tmux.Attach(target, os.Stdin, os.Stdout, os.Stderr)
	}

	return nil
}

func (smug Smug) Stop(config *Config, options *Options, context Context) error {
	windows := options.Windows
	if len(windows) == 0 {
		sessionRoot := ExpandPath(config.Root)

		err := smug.execShellCommands(config.Stop, sessionRoot)
		if err != nil {
			return err
		}
		_, err = smug.tmux.StopSession(config.Session)
		return err
	}

	for _, w := range windows {
		err := smug.tmux.KillWindow(config.Session + ":" + w)
		if err != nil {
			return err
		}
	}

	return nil
}

func (smug Smug) Start(config *Config, options *Options, context Context) error {
	var sessionName string
	var err error

	createWindowsInsideCurrSession := options.InsideCurrentSession
	if createWindowsInsideCurrSession && !context.InsideTmuxSession {
		return errors.New("cannot use -i flag outside of a tmux session")
	}

	sessionName = config.Session
	if createWindowsInsideCurrSession {
		sessionName, err = smug.tmux.SessionName()
		if err != nil {
			return err
		}
	}
	sessionName = sessionName + ":"

	sessionExists := smug.tmux.SessionExists(sessionName)
	sessionRoot := ExpandPath(config.Root)
	windows := options.Windows
	attach := options.Attach

	if !sessionExists && !createWindowsInsideCurrSession {
		err := smug.execShellCommands(config.BeforeStart, sessionRoot)
		if err != nil {
			return err
		}

		_, err = smug.tmux.NewSession(config.Session, sessionRoot, defaultWindowName)
		if err != nil {
			return err
		}

		err = smug.setEnvVariables(config.Session, config.Env)
		if err != nil {
			return err
		}
	} else if len(windows) == 0 && !createWindowsInsideCurrSession {
		return smug.switchOrAttach(sessionName, attach, context.InsideTmuxSession)
	}

	for _, w := range config.Windows {
		if (len(windows) == 0 && w.Manual) || (len(windows) > 0 && !Contains(windows, w.Name)) {
			continue
		}

		windowRoot := ExpandPath(w.Root)
		if windowRoot == "" || !filepath.IsAbs(windowRoot) {
			windowRoot = filepath.Join(sessionRoot, w.Root)
		}

		window, err := smug.tmux.NewWindow(sessionName, w.Name, windowRoot)
		if err != nil {
			return err
		}

		for _, c := range w.Commands {
			time.Sleep(time.Millisecond * time.Duration(config.SendKeysTimeout))
			err := smug.tmux.SendKeys(window, c)
			if err != nil {
				return err
			}
		}

		for i, p := range w.Panes {
			paneRoot := ExpandPath(p.Root)
			if paneRoot == "" || !filepath.IsAbs(p.Root) {
				paneRoot = filepath.Join(windowRoot, p.Root)
			}

			newPane, err := smug.tmux.SplitWindow(window, p.Type, paneRoot)
			if err != nil {
				return err
			}

			if i%2 == 0 {
				_, err = smug.tmux.SelectLayout(window, Tiled)
				if err != nil {
					return err
				}

			}

			for _, c := range p.Commands {
				time.Sleep(time.Millisecond * time.Duration(config.SendKeysTimeout))
				err = smug.tmux.SendKeys(window+"."+newPane, c)
				if err != nil {
					return err
				}
			}
		}

		layout := w.Layout
		if layout == "" {
			layout = EvenHorizontal
		}

		_, err = smug.tmux.SelectLayout(window, layout)
		if err != nil {
			return err
		}
	}

	if !options.InsideCurrentSession && !sessionExists {
		err := smug.tmux.KillWindow(sessionName + defaultWindowName)
		if err != nil {
			return err
		}
		err = smug.tmux.RenumberWindows(sessionName)
		if err != nil {
			return err
		}
	}

	if len(config.Windows) > 0 && !options.Detach {
		w := config.Windows[0].Name
		if len(options.Windows) > 0 {
			w = options.Windows[0]
		}

		return smug.switchOrAttach(sessionName+w, attach, context.InsideTmuxSession)
	}

	return nil
}

func (smug Smug) GetConfigFromSession(options *Options, context Context) (Config, error) {
	config := Config{}

	tmuxSession, err := smug.tmux.SessionName()
	if err != nil {
		return Config{}, err
	}
	config.Session = tmuxSession

	tmuxWindows, err := smug.tmux.ListWindows(options.Project)
	if err != nil {
		return Config{}, err
	}

	for _, w := range tmuxWindows {
		tmuxPanes, err := smug.tmux.ListPanes(options.Project + ":" + w.ID)
		if err != nil {
			return Config{}, err
		}

		panes := []Pane{}
		for _, p := range tmuxPanes {
			root := p.Root
			if root == w.Root {
				root = ""
			}
			panes = append(panes, Pane{
				Root: root,
			})
		}

		config.Windows = append(config.Windows, Window{
			Name:   w.Name,
			Layout: w.Layout,
			Root:   w.Root,
			Panes:  panes,
		})
	}

	return config, nil
}
