package main

import (
	"context"
	"image/color"
	"log"
	"os"
	"os/exec"
	"sync"

	"gioui.org/app"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

func main() {
	ui := NewUi()
	go func() {
		err := ui.Run()
		if err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()
	app.Main()
}

type State struct {
	sync.Mutex
	Theme               *material.Theme
	Title               string
	UrlEditor           widget.Editor
	PlayPauseButton     widget.Clickable
	PlayPauseButtonText string
	PlayerContext       context.Context
	PlayerCancel        context.CancelFunc
}

type Ui struct {
	Window *app.Window
	State  State
}

func NewUi() *Ui {
	ui := Ui{}

	ui.State.Theme = material.NewTheme()

	ui.Window = new(app.Window)
	ui.Window.Option(app.Title("soundbox app"))

	ui.State.Title = "soundbox"
	ui.State.PlayPauseButtonText = "Play"

	return &ui
}

func (ui *Ui) Cleanup() {
	ui.State.Lock()
	defer ui.State.Unlock()

	if ui.State.PlayerCancel != nil {
		ui.State.PlayerCancel()
	}
}

func (ui *Ui) Run() error {
	defer ui.Cleanup()
	var ops op.Ops
	for {
		switch e := ui.Window.Event().(type) {
		case app.DestroyEvent:
			return e.Err
		case app.FrameEvent:
			gtx := app.NewContext(&ops, e)
			ui.HandleInputs(gtx)
			ui.Layout(gtx)
			e.Frame(gtx.Ops)
		}
	}
}

func (ui *Ui) HandleInputs(gtx layout.Context) {
	ui.State.Lock()
	defer ui.State.Unlock()

	if ui.State.PlayPauseButton.Clicked(gtx) {
		if ui.State.UrlEditor.ReadOnly {
			ui.State.PlayerCancel()
		} else {
			ui.State.PlayerContext, ui.State.PlayerCancel = context.WithCancel(context.Background())
			go mpv(ui.State.PlayerContext, ui.State.UrlEditor.Text(), ui)
		}
	}
}

func (ui *Ui) Layout(gtx layout.Context) layout.Dimensions {
	ui.State.Lock()
	defer ui.State.Unlock()

	inset := layout.UniformInset(unit.Dp(10))

	flex := layout.Flex{
		Axis:    layout.Vertical,
		Spacing: layout.SpaceEnd,
	}

	h1 := material.H1(ui.State.Theme, ui.State.Title)
	h1.Color = color.NRGBA{R: 88, G: 88, B: 88, A: 255}
	h1.Alignment = text.Middle

	editor := material.Editor(ui.State.Theme, &ui.State.UrlEditor, "Audio stream URL")
	editor.Editor.Alignment = text.Middle

	button := material.Button(ui.State.Theme, &ui.State.PlayPauseButton, ui.State.PlayPauseButtonText)

	return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return flex.Layout(gtx,
			layout.Rigid(h1.Layout),
			layout.Rigid(layout.Spacer{Height: unit.Dp(25)}.Layout),
			layout.Rigid(editor.Layout),
			layout.Rigid(layout.Spacer{Height: unit.Dp(25)}.Layout),
			layout.Rigid(button.Layout),
		)
	})
}

func mpv(ctx context.Context, url string, ui *Ui) {
	setPlayingState := func(isPlaying bool) {
		ui.State.Lock()
		defer ui.Window.Invalidate()
		defer ui.State.Unlock()

		ui.State.UrlEditor.ReadOnly = isPlaying
		if isPlaying {
			ui.State.PlayPauseButtonText = "Stop"
		} else {
			ui.State.PlayPauseButtonText = "Play"
		}
	}

	setPlayingState(true)

	cmd := exec.CommandContext(
		ctx,
		"mpv",
		"--no-video",
		url,
	)
	_ = cmd.Run()

	setPlayingState(false)
}