package main

import (
	sg "github.com/d2fn/sumi/internal/graphics"
	"github.com/gen2brain/raylib-go/raylib"
	"image/color"
	"math"
	"math/rand"
)

type ContourLayer struct {
	field        Field
	rng          *rand.Rand
	maxActors    uint32
	actors       []*Actor
	actorIndex   uint32
	actorColor   color.RGBA
	loActorAngle float32
	hiActorAngle float32
}

func NewContourLayer(sketch *Sketch, rng *rand.Rand, field Field, color color.RGBA, loActorAngle float32, hiActorAngle float32) *ContourLayer {

	maxActors := 200000

	actors := make([]*Actor, maxActors)

	layer := ContourLayer{
		rng:          rng,
		field:        field,
		actors:       actors,
		maxActors:    uint32(maxActors),
		actorColor:   color,
		actorIndex:   0,
		loActorAngle: loActorAngle,
		hiActorAngle: hiActorAngle,
	}

	return &layer
}

func (s *ContourLayer) AddActors(color color.RGBA, n, sourceWidth, sourceHeight int32) {
	for range n {
		x := s.rng.Int31() % sourceWidth
		y := s.rng.Int31() % sourceHeight
		newActor :=
			&Actor{
				position: sg.Point{X: float32(x), Y: float32(y)},
				field:    s.field,
				stepSize: 1,
				color:    s.actorColor,
				loAngle:  s.loActorAngle,
				hiAngle:  s.hiActorAngle,
			}
		s.actors[s.actorIndex] = newActor
		s.actorIndex = (s.actorIndex + 1) % s.maxActors
	}
}

func (s *ContourLayer) Update(env *Env, g *sg.Graphics) {
	s.AddActors(s.actorColor, 100, g.WidthInt32(), g.HeightInt32())
}

func (s *ContourLayer) Draw(env *Env, g *sg.Graphics) {
	g.BeginAdditiveBlend()
	for _, actor := range s.actors {
		if actor != nil {
			actor.Draw(env, g)
		}
	}
	g.EndBlend()
}

func (s *ContourLayer) IsDirty() bool {
	return true
}

type Actor struct {
	position sg.Point
	field    Field
	stepSize float32
	color    color.RGBA
	loAngle  float32
	hiAngle  float32
}

func (a *Actor) Draw(env *Env, g *sg.Graphics) {
	v := a.field.Get(a.position.X, a.position.Y)
	rad := rl.Remap(v, 0, 1, a.loAngle, a.hiAngle)
	nextPosition := sg.Point{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))}
	//nextPosition := rl.Vector2{X: a.position.X + a.stepSize*float32(math.Cos(float64(rad))), Y: a.position.Y + a.stepSize*float32(math.Sin(float64(rad)))}

	g.SetStrokeWeight(1.0)
	g.SetStroke(true)
	g.SetStrokeColor(a.color)
	g.DrawLine(a.position, nextPosition)
	//rl.DrawLineV(a.position, nextPosition, a.color)
	a.position = nextPosition
}

type Worm struct {
	position   sg.Point
	angles     []float32
	angleIndex int
	stepSize   int
	renderPct  float32
}

func (w *Worm) Draw(env *Env, g *sg.Graphics) {
	g.PushMatrix()
	g.Translate(w.position)
	//rl.PushMatrix()
	//rl.Translatef(w.position.X, w.position.Y, 0)
	lastAngle := float32(0.0)
	stepCount := 0
	nudged := false
	for i := range w.angles {
		ii := (i + w.angleIndex) % len(w.angles)
		angle := w.angles[ii]
		deltaAngle := angle - lastAngle
		if !nudged {
			rad := float64(deltaAngle * math.Pi / 180.0)
			nudge := sg.Vec{X: float32(w.stepSize) * float32(math.Cos(rad)), Y: float32(w.stepSize) * float32(math.Sin(rad))}
			w.position = w.position.Add(nudge) //rl.Vector2Add(w.position, nudge)
			nudged = true
		}
		rl.Rotatef(deltaAngle, 0, 0, 1)
		rl.DrawLine(0, 0, int32(w.stepSize), 0, rl.NewColor(184, 187, 38, 50))
		rl.Translatef(float32(w.stepSize), 0, 0)
		lastAngle = angle
		stepCount++
		if stepCount > int(float32(len(w.angles))*w.renderPct) {
			break
		}
	}
	g.PopMatrix()
	w.angleIndex = (w.angleIndex + 1) % len(w.angles)
}
