//go:build linux

package linux

import (
	"bufio"
	"context"
	"fmt"
	"os/exec"
	"sync"
)

const playerWindowTitle = "videotools-player"

type Controller struct {
	mu     sync.Mutex
	cmd    *exec.Cmd
	stdin  *bufio.Writer
	ctx    context.Context
	cancel context.CancelFunc
	path   string
}

func New() *Controller {
	return &Controller{}
}

func (c *Controller) Load(path string, offset float64) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.stopLocked()

	ctx, cancel := context.WithCancel(context.Background())
	args := []string{
		"-hide_banner", "-loglevel", "error",
		"-autoexit",
		"-window_title", playerWindowTitle,
		"-noborder",
		"-x", "0",
		"-y", "0",
	}
	if offset > 0 {
		args = append(args, "-ss", fmt.Sprintf("%.4f", offset))
	}
	args = append(args, path)

	cmd := exec.CommandContext(ctx, "ffplay", args...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		cancel()
		return err
	}
	if err := cmd.Start(); err != nil {
		cancel()
		return err
	}

	c.cmd = cmd
	c.stdin = bufio.NewWriter(stdin)
	c.ctx = ctx
	c.cancel = cancel
	c.path = path

	go cmd.Wait()
	return nil
}

func (c *Controller) Play() error {
	return c.send('p')
}

func (c *Controller) Pause() error {
	return c.send('p')
}

func (c *Controller) Seek(offset float64) error {
	if c.path == "" {
		return fmt.Errorf("no source loaded")
	}
	return c.Load(c.path, offset)
}

func (c *Controller) FullScreen() error {
	return c.send('f')
}

func (c *Controller) Stop() error {
	return c.send('q')
}

func (c *Controller) Close() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.stopLocked()
}

func (c *Controller) send(ch byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.stdin == nil {
		return fmt.Errorf("player stdin unavailable")
	}
	if _, err := c.stdin.Write([]byte{ch}); err != nil {
		return err
	}
	return c.stdin.Flush()
}

func (c *Controller) stopLocked() {
	if c.stdin != nil {
		c.stdin.Write([]byte{'q'})
		c.stdin.Flush()
	}
	if c.cancel != nil {
		c.cancel()
	}
	c.cmd = nil
	c.stdin = nil
	c.cancel = nil
	c.path = ""
}
