package main

import (
	"image"

	"git.kirsle.net/go/render"
)

// App holds the ripple applet state.
type App struct {
	e      render.Engine
	image  image.Image
	source *image.RGBA
	data   *image.RGBA
	tex    render.Texturer

	// computed
	width  int
	height int
	size   int

	// frame buffers
	buffer0 []int
	buffer1 []int
}

// New creates the ripple applet.
func New(e render.Engine, i image.Image) *App {
	var (
		bounds = i.Bounds()
		width  = bounds.Max.X
		height = bounds.Max.Y
		size   = width * height
	)
	a := &App{
		e:       e,
		image:   i,
		source:  render.ImageToRGBA(i),
		data:    render.ImageToRGBA(i),
		width:   width,
		height:  height,
		size:    size,
		buffer0: make([]int, size),
		buffer1: make([]int, size),
	}

	tex, err := a.e.StoreTexture("pic", a.data)
	if err != nil {
		panic(err)
	}
	a.tex = tex
	return a
}

// Disturb creates a disturbance in the water.
func (a *App) Disturb(x, y, z int) {
	if x < 2 || x > Width-2 || y < 1 || y > Height-2 {
		return
	}
	var i = x + y*Width
	a.buffer0[i] += z
	a.buffer0[i-1] -= z
}

// Ripple runs the ripple algorithm.
func (a *App) Ripple() {
	var (
		width = a.width
		size  = a.size
	)

	// average cells to make the surface more even
	for i := width + 1; i < size-width-1; i += 2 {
		for x := 1; x < width-1; x++ {
			avg := (a.buffer0[i] + a.buffer0[i+1] + a.buffer0[i-1] + a.buffer0[i-width] + a.buffer0[i+width]) / 5
			a.buffer0[i] = avg
			i++
		}
	}

	// Loop for every non-edge element.
	y := 0
	for i := width + 1; i < size-width-1; i += 2 {
		y++
		for x := 1; x < width-1; x++ {
			// Wave propagation
			waveHeight := (a.buffer0[i-1]+
				a.buffer0[i+1]+
				a.buffer0[i+Width]+
				a.buffer0[i-Width])/2 - a.buffer1[i]
			a.buffer1[i] = waveHeight

			// Some very fake lighting
			var light = int(float64(waveHeight)*2.0 - float64(a.buffer1[i-2])*0.6)
			if light < -10 {
				light = -10
			} else if light > 100 {
				light = 100
			}

			color := a.source.At(x, y)
			a.data.Set(x, y, render.FromColor(color).Lighten(light).ToColor())

			i++
		}
	}

	aux := a.buffer0
	a.buffer0 = a.buffer1
	a.buffer1 = aux

	a.tex, _ = a.e.StoreTexture("pic", a.data)
}

// Present the visual state.
func (a *App) Present() {
	rect := render.Rect{
		W: a.width,
		H: a.height,
	}
	a.e.Copy(a.tex, rect, rect)
	a.e.Present()
}
