package main

import (
	"math"
	"math/rand"
	"time"

	"opslag.de/schobers/geom"
)

type moveAnimation struct {
	e        *entity
	from, to geom.Point
	pos      geom.PointF32
}

func newMoveAnimation(e *entity, to geom.Point) *moveAnimation {
	ani := &moveAnimation{e: e, from: e.pos, to: to, pos: e.pos.ToF32()}
	ani.e.pos = to
	return ani
}

func (a *moveAnimation) Animate(start, now time.Duration) bool {
	const duration = 210 * time.Millisecond

	progress := float32((now-start)*1000/duration) * .001
	from, to := a.from.ToF32(), a.to.ToF32()
	if progress >= 1 {
		a.e.scr.pos = to
		return false
	}
	a.e.scr.pos = to.Sub(from).Mul(progress).Add(from)
	return true
}

type sinkAnimation struct {
	e *entity
}

func newSinkAnimation(e *entity) *sinkAnimation {
	return &sinkAnimation{e: e}
}

func (a *sinkAnimation) Animate(start, now time.Duration) bool {
	const duration = 70 * time.Millisecond

	progress := float32((now-start)*1000/duration) * .001
	if progress >= 1 {
		a.e.scr.z = 80
		return false
	}
	a.e.scr.z = progress * 80
	return true
}

type splashAnimation struct {
	particles []splashParticle
}

func newSplashAnimation(pos geom.Point) *splashAnimation {
	pos32 := pos.ToF32()
	a := &splashAnimation{}
	n := rand.Intn(20) + 4
	for i := 0; i < n; i++ {
		angle := rand.Float32() * math.Pi * 2
		a.particles = append(a.particles, splashParticle{
			angle:  angle,
			origin: pos32.Add2D(.45*geom.Cos32(angle), .45*geom.Sin32(angle)), // [-0.45 .. 0.45]
			speed:  .5*math.Pi + 2*math.Pi*rand.Float32(),                     // [0.5*Pi .. 2.5*Pi)
			height: .2 + .8*rand.Float32(),                                    // [0.2 .. 1.0)
			dist:   .5 + .5*rand.Float32(),                                    // [0.5 .. 1.0)
		})
	}
	return a
}

func (a *splashAnimation) Animate(start, now time.Duration) bool {
	const duration = 1670 * time.Millisecond

	progress := float32((now-start)*1000/duration) * .001
	var opaq float32 = 1
	if progress > .6 {
		if progress > 1 {
			opaq = 0
		} else {
			opaq = 2.5 - 2.5*progress
		}
	}
	for i, p := range a.particles {
		progress := geom.Min32(math.Pi, progress*p.speed) / math.Pi
		a.particles[i].part = particle{
			opaq: opaq,
			pos:  p.origin.Add2D(progress*p.dist*geom.Cos32(p.angle), progress*p.dist*geom.Sin32(p.angle)),
			z:    20 + p.height*-100*geom.Sin32(progress*math.Pi),
		}
	}
	if progress > 1 {
		return false
	}
	return true
}

type particle struct {
	pos  geom.PointF32
	z    float32
	opaq float32
}

func splitParticles(y float32, particles []particle) (behind []particle, front []particle) {
	for _, p := range particles {
		if p.pos.Y > y {
			front = append(front, p)
		} else {
			behind = append(behind, p)
		}
	}
	return
}

type splashParticle struct {
	origin geom.PointF32
	angle  float32
	speed  float32
	height float32
	dist   float32

	part particle
}
