package widgets

import (
	"context"
	"image/color"
	"sync/atomic"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"github.com/dweymouth/supersonic/ui/layouts"
)

type LoadingDots struct {
	widget.BaseWidget

	running    atomic.Bool
	animCancel context.CancelFunc

	dots      [3]minSizeCircle
	animPos   int
	container *fyne.Container
}

func NewLoadingDots() *LoadingDots {
	l := &LoadingDots{}
	for i := range l.dots {
		l.dots[i] = minSizeCircle{Circle: canvas.Circle{
			FillColor: theme.DisabledColor()},
		}
	}
	l.ExtendBaseWidget(l)
	l.Hide()
	return l
}

func (l *LoadingDots) Start() {
	if !l.running.CompareAndSwap(false, true) {
		return // already started
	}
	for i := range l.dots {
		l.dots[i].Circle.FillColor = theme.DisabledColor()
	}
	l.animPos = 0
	l.Show()
	var ctx context.Context
	ctx, l.animCancel = context.WithCancel(context.Background())
	go l.animate(ctx)
}

func (l *LoadingDots) Stop() {
	if !l.running.Load() {
		return
	}
	l.Hide()
	l.animCancel()
}

func (l *LoadingDots) animate(ctx context.Context) {
	foreground := theme.ForegroundColor()
	disabled := theme.DisabledColor()
	l.doTick(foreground, disabled)
	ticker := time.NewTicker(333 * time.Millisecond)
	for {
		select {
		case <-ctx.Done():
			ticker.Stop()
			l.running.Store(false)
			return
		case <-ticker.C:
			l.doTick(foreground, disabled)
		}
	}
}

func (l *LoadingDots) doTick(foreground, disabled color.Color) {
	oldDot := l.animPos - 1
	if oldDot == -1 {
		oldDot = len(l.dots) - 1
	}
	l.dots[l.animPos].Circle.FillColor = foreground
	l.dots[oldDot].Circle.FillColor = disabled
	l.Refresh()
	l.animPos += 1
	if l.animPos >= len(l.dots) {
		l.animPos = 0
	}
}

func (l *LoadingDots) CreateRenderer() fyne.WidgetRenderer {
	if l.container == nil {
		layout := &layouts.CenterPadLayout{
			PadLeftRight: 3,
		}
		l.container = container.NewHBox(
			container.New(layout, &l.dots[0]),
			container.New(layout, &l.dots[1]),
			container.New(layout, &l.dots[2]),
		)
	}
	return widget.NewSimpleRenderer(l.container)
}

type minSizeCircle struct {
	widget.BaseWidget
	Circle canvas.Circle
}

func (m *minSizeCircle) CreateRenderer() fyne.WidgetRenderer {
	return widget.NewSimpleRenderer(&m.Circle)
}

func (m *minSizeCircle) MinSize() fyne.Size {
	return fyne.NewSquareSize(theme.IconInlineSize() / 2)
}
