package terminal

import (
	"fmt"
	"github.com/ActiveState/termtest/conpty"
	"github.com/gliderlabs/ssh"
	"io"
	"log"
	"os"
	"syscall"
)

var PtySpawner = Spawner(func(sshSession ssh.Session, env []string, name string, args ...string) (Process, error) {
	_, winCh, isPty := sshSession.Pty()
	if !isPty {
		return nil, fmt.Errorf("ssh session is not a pty")
	}

	cpty, err := conpty.New(80, 25)
	if err != nil {
		return nil, err
	}
	pid, _, err := cpty.Spawn(
		name,
		args,
		&syscall.ProcAttr{
			Env: env,
		},
	)
	if err != nil {
		cpty.Close()
		return nil, err
	}
	log.Printf("New process with pid %d spawned\n", pid)
	process, err := os.FindProcess(pid)
	if err != nil {
		cpty.Close()
		return nil, fmt.Errorf("Failed to find process: %v", err)
	}

	go func() {
		for win := range winCh {
			err = cpty.Resize(uint16(win.Width), uint16(win.Height))
			if err != nil {
				log.Printf("Failed to resize terminal to %d x %d", win.Width, win.Height)
			}
		}
	}()
	return ptyProcess{
		cpty:    cpty,
		process: process,
	}, nil
})

type ptyProcess struct {
	cpty    *conpty.ConPty
	process *os.Process
}

func (proc ptyProcess) Read(p []byte) (n int, err error) {
	return proc.cpty.OutPipe().Read(p)
}
func (proc ptyProcess) Write(p []byte) (n int, err error) {
	uintn, err := proc.cpty.Write(p)
	return int(uintn), err
}

func (p ptyProcess) Pipe() io.ReadWriter {
	return p
}

func (p ptyProcess) Kill() error {
	return p.process.Kill()
}

func (p ptyProcess) Wait() error {
	defer p.cpty.Close()
	ps, err := p.process.Wait()
	if err != nil {
		return fmt.Errorf("Error waiting for process: %v", err)
	}
	if ps.ExitCode() != 0 {
		return fmt.Errorf("exit code was: %d\n", ps.ExitCode())
	}
	return nil
}