package audiospectrum

import (
	"image"
	"image/color"
	"math"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"github.com/disintegration/imaging"
)

//
// Widget code starts here
//
// A visualisation of an audio stream widget with themed or overridden background and foreground
//
type AudioSpectrum struct {
	widget.BaseWidget               // Inherit from BaseWidget
	audioData               []int32 // The data to display in the widget
	StretchSamples          bool
	TransparentBackground   bool
	OverrideForeground      bool
	OverrideForegroundColor color.Color
	OverrideBackground      bool
	OverrideBackgroundColor color.Color
	minSize                 fyne.Size
}

//
// Create a Widget and Extend (initialiase) the BaseWidget
//
func NewAudioSpectrum(data []int32) *AudioSpectrum {
	w := &AudioSpectrum{ // Create this widget with an initial text value
		audioData:               data,
		StretchSamples:          false,
		TransparentBackground:   false,
		OverrideForeground:      false,
		OverrideForegroundColor: color.RGBA{0, 0, 0, 0xff},
		OverrideBackground:      false,
		OverrideBackgroundColor: color.RGBA{0xff, 0xff, 0xff, 0xff},
		minSize:                 fyne.NewSize(200, 64),
	}
	w.ExtendBaseWidget(w) // Initialiase the BaseWidget
	return w
}

//
// Create the renderer. This is called by the fyne application
//
func (w *AudioSpectrum) CreateRenderer() fyne.WidgetRenderer {
	// Pass this widget to the renderer so it can access the text field
	return newAudioSpectrumRenderer(w)
}

//
// Set the minsize (default is 200x64)
//
func (w *AudioSpectrum) SetMinSize(newSize fyne.Size) {
	// Pass this widget to the renderer so it can access the text field
	w.minSize = newSize
}

//
// Widget Renderer code starts here
//
type audioSpectrumRenderer struct {
	widget     *AudioSpectrum    // Reference to the widget holding the current state
	background *canvas.Rectangle // A background rectangle
	canvas     *canvas.Raster    // The waveform
}

//
// Create the renderer with a reference to the widget
// Note: The background and foreground colours are set from the current theme.
//
// Do not size or move canvas objects here.
//
func newAudioSpectrumRenderer(theWidget *AudioSpectrum) *audioSpectrumRenderer {
	r := &audioSpectrumRenderer{
		widget:     theWidget,
		background: canvas.NewRectangle(theme.BackgroundColor()),
	}
	r.canvas = canvas.NewRaster(r.audioDataToImage)
	return r
}

// From arduino map() lol
func int32Map(x int32, in_min int32, in_max int32, out_min int32, out_max int32) int32 {
	var _x = int64(x)
	var _in_min = int64(in_min)
	var _in_max = int64(in_max)
	var _out_min = int64(out_min)
	var _out_max = int64(out_max)
	var r = int64((_x-_in_min)*(_out_max-_out_min)/(_in_max-_in_min) + _out_min)
	return int32(r)
}

func (r *audioSpectrumRenderer) audioDataToImage(w, h int) image.Image {
	foregroundColor := theme.ForegroundColor()
	if r.widget.OverrideForeground {
		foregroundColor = r.widget.OverrideForegroundColor
	}
	upLeft := image.Point{0, 0}
	lowRight := image.Point{w, h}
	if r.widget.StretchSamples {
		lowRight = image.Point{len(r.widget.audioData), h}
	}

	img := image.NewRGBA(image.Rectangle{upLeft, lowRight})
	i := int32(0)
	for i = 0; i < int32(len(r.widget.audioData)); i++ {
		sample := r.widget.audioData[i]
		// /2 because we draw spectrums with 0 at the middle!
		sampleheight := int32Map(sample, 0, math.MaxInt32, 0, int32(h/2))
		y := int32(0)
		//center line
		img.Set(int(i), int(h/2), foregroundColor)
		for y = int32(h); y > int32(h)-sampleheight; y-- {
			img.Set(int(i), int(y)-(h/2), foregroundColor)
		}
		for y = int32(h / 2); y < sampleheight+(int32(h)/2); y++ {
			img.Set(int(i), int(y), foregroundColor)
		}
	}
	if r.widget.StretchSamples {
		return imaging.Resize(img, w, h, imaging.NearestNeighbor)
	}
	return img
}

//
// The Refresh() method is called if the state of the widget changes or the
// theme is changed
//
// Note: The background and foreground colours are set from the current theme
//
func (r *audioSpectrumRenderer) Refresh() {
	backgroundColor := theme.BackgroundColor()
	if r.widget.OverrideBackground {
		backgroundColor = color.RGBA{0, 0, 0xff, 0xff} //r.widget.OverrideBackgroundColor
	}
	r.background.FillColor = backgroundColor
	r.background.Refresh() // Redraw the background first
	r.canvas.Refresh()     // Redraw the waveform on top
}

//
// Given the size required by the fyne application move and re-size the
// canvas objects.
//
func (r *audioSpectrumRenderer) Layout(s fyne.Size) {
	// Make sure the background fills the widget
	r.background.Resize(s)
	r.canvas.Resize(s)
	r.Refresh()
}

//
// Create a minimum size for the widget.
// The smallest size is the size of the text with a border defined by the theme padding
//
func (r *audioSpectrumRenderer) MinSize() fyne.Size {
	// Default is set in newSpectrumRenderer, overridable by users
	return r.widget.minSize
}

//
// Return a list of each canvas object.
//
func (r *audioSpectrumRenderer) Objects() []fyne.CanvasObject {
	if r.widget.TransparentBackground {
		return []fyne.CanvasObject{r.canvas}
	}
	return []fyne.CanvasObject{r.background, r.canvas}
}

//
// Cleanup if resources have been allocated
//
func (r *audioSpectrumRenderer) Destroy() {}
