package gocvwebcam

import (
	"image"
	"image/color"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/widget"
	"gocv.io/x/gocv"
)

var _ fyne.WidgetRenderer = (*gocvGocvWebcamRenderer)(nil)

type GocvWebcam struct {
	widget.BaseWidget

	GocvWebcamID int64
	UpdateFPS    int64
	fpsTimer     *time.Ticker
}

func NewGocvWebcam() *GocvWebcam {
	w := &GocvWebcam{}
	w.ExtendBaseWidget(w)
	w.GocvWebcamID = 0
	w.UpdateFPS = 25

	return w
}

func (w *GocvWebcam) Resize(s fyne.Size) {
	w.BaseWidget.Resize(s)
}

func (w *GocvWebcam) CreateRenderer() fyne.WidgetRenderer {
	fpsTimer := time.NewTicker(time.Duration(1000/25) * time.Millisecond)
	go func(w *GocvWebcam) {
		for {
			_ = <-fpsTimer.C
			w.Refresh()
		}
	}(w)
	return newGocvWebcamRenderer(w)
}

type gocvGocvWebcamRenderer struct {
	gocvGocvWebcam   *GocvWebcam
	background       *canvas.Rectangle
	currentframe     *canvas.Raster
	goCVVideoCapture *gocv.VideoCapture
}

func (r *gocvGocvWebcamRenderer) actualrenderframe(w, h int) image.Image {
	gocvGocvWebcam, _ := gocv.VideoCaptureDevice(int(r.gocvGocvWebcam.GocvWebcamID))
	img := gocv.NewMat()
	gocvGocvWebcam.Read(&img)
	frameImage, err := img.ToImage()
	if err != nil {
		frameImage = image.NewRGBA(image.Rect(0, 0, w, h))
	}
	return frameImage
}

func newGocvWebcamRenderer(w *GocvWebcam) *gocvGocvWebcamRenderer {
	renderer := &gocvGocvWebcamRenderer{
		gocvGocvWebcam: w,
		background:     canvas.NewRectangle(color.RGBA{255, 0, 255, 255}),
	}
	renderer.currentframe = canvas.NewRaster(renderer.actualrenderframe)
	return renderer
}

func (r *gocvGocvWebcamRenderer) Objects() []fyne.CanvasObject {
	// The order is critical, rect is drawn first then currentframe
	return []fyne.CanvasObject{r.background, r.currentframe}
}

func (r *gocvGocvWebcamRenderer) Layout(s fyne.Size) {
	r.background.Resize(s)
	r.currentframe.Resize(s)
}

func (r *gocvGocvWebcamRenderer) MinSize() fyne.Size {
	return fyne.NewSize(200, 200)
}

func (r *gocvGocvWebcamRenderer) Refresh() {
	r.currentframe.Refresh()
}

func (r *gocvGocvWebcamRenderer) Destroy() {
	r.gocvGocvWebcam.fpsTimer.Stop()
} // Called when the renderer is destroyed
