package splashmenu

import (
	"bytes"
	"cosmos/diego/groovy"
	"image"
	"image/color"
	"log"
	"math"
	"math/rand"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
	"github.com/hajimehoshi/ebiten/v2/vector"

	_ "embed"
)

const (
	GALAXY_SCALE_DIST  = 50
	DEBRIS_SCALE_DIST  = 10
	FLOAT_SCALE        = 8
	ASTEROID_COUNT     = 14
	MAX_ROTATION_SPEED = 3
	MIN_ROTATION_SPEED = 1
	OFF_SCREEN_SCALE   = 2 // number of parallax dimension screens for the logical game display area
)

var (
	//go:embed assets/galaxy4.jpg
	galaxyImg_jpg []byte

	//go:embed assets/debris_00.png
	debrisImg_png []byte

	//go:embed assets/roid1.png
	roid1Img_png []byte

	//go:embed assets/roid2.png
	roid2Img_png []byte

	//go:embed assets/blend.png
	blendImg_png []byte

	galaxyBackground *ebiten.Image
	debrisImage      *ebiten.Image
	blendImage       *ebiten.Image
	blendTmp         *ebiten.Image
	roids            []*ebiten.Image
)

type Asteroid struct {
	cX            int
	cY            int
	rotationSpeed float64
}

func init() {
	// Load up images
	img, _, err := image.Decode(bytes.NewReader(galaxyImg_jpg))
	if err != nil {
		log.Fatal(err)
	}
	galaxyBackground = ebiten.NewImageFromImage(img)

	img, _, err = image.Decode(bytes.NewReader(debrisImg_png))
	if err != nil {
		log.Fatal(err)
	}
	debrisImage = ebiten.NewImageFromImage(img)

	//asteroids!
	img, _, err = image.Decode(bytes.NewReader(roid1Img_png))
	if err != nil {
		log.Fatal(err)
	}
	roids = append(roids, ebiten.NewImageFromImage(img))

	img, _, err = image.Decode(bytes.NewReader(roid2Img_png))
	if err != nil {
		log.Fatal(err)
	}
	roids = append(roids, ebiten.NewImageFromImage(img))

	img, _, err = image.Decode(bytes.NewReader(blendImg_png))
	if err != nil {
		log.Fatal(err)
	}
	blendImage = ebiten.NewImageFromImage(img)
	blendTmp = ebiten.NewImageFromImage(blendImage)
}

type Parallax struct {
	events     map[groovy.SceneEvent]func()
	Dimensions groovy.Area
	increment  int
	asteroids  []Asteroid
}

func NewParallax() Parallax {
	return Parallax{
		events: make(map[groovy.SceneEvent]func()),
	}
}

func (p *Parallax) Update() error {
	p.increment++

	var keysPressed []ebiten.Key
	keysPressed = inpututil.AppendJustPressedKeys(keysPressed[:0])

	//check for user input to transition scene
	if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
		if p.events[groovy.COMPLETED] != nil {
			p.events[groovy.COMPLETED]()
		}
	}

	return nil
}

func (p *Parallax) Draw(screen *ebiten.Image) {

	p.DrawDynamicBackground(screen)
	p.DrawObstacles(screen)
	//p.DrawObstaclesWithShamLighting(screen)

	p.RepositionTest(screen)

}

func (p *Parallax) SetDimensions(a groovy.Area) {
	p.Dimensions = a
}

func (p *Parallax) SetEventHandler(event groovy.SceneEvent, f func()) {
	p.events[event] = f
}

func (p *Parallax) InitializeParallax() {

	if p.Dimensions.Area() <= 0 {
		return
	}

	for i := 0; i < ASTEROID_COUNT; i++ {
		newX := rand.Intn(p.Dimensions.Width) * OFF_SCREEN_SCALE
		newY := rand.Intn(p.Dimensions.Height) * OFF_SCREEN_SCALE
		rS := rand.Float64()*MAX_ROTATION_SPEED + MIN_ROTATION_SPEED
		p.asteroids = append(p.asteroids, Asteroid{cX: newX, cY: newY, rotationSpeed: rS})
	}

	whiteImage.Fill(color.White)
}

func (p *Parallax) DrawDynamicBackground(screen *ebiten.Image) {
	x, y := ebiten.CursorPosition()

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(float64(-x-p.Dimensions.Width)/GALAXY_SCALE_DIST, float64(-y-p.Dimensions.Height)/GALAXY_SCALE_DIST)
	screen.DrawImage(galaxyBackground, op)

	//debris layers
	const DEBRIS_QTY = 3
	const DEBRIS_WIDTH = 640 / 3 * 2
	const DEBRIS_HEIGHT = 480 / 3 * 2
	const TOTAL_LAYERS = 2
	for k := 0; k < TOTAL_LAYERS; k++ {
		for i := 0; i < DEBRIS_QTY; i++ {
			for j := 0; j < DEBRIS_QTY; j++ {
				//compute object origins (pre-shifts)
				x0 := float64(j*DEBRIS_WIDTH) + float64(p.increment/640)
				y0 := float64(i*DEBRIS_HEIGHT) + float64(p.increment/480)
				//now compute delta with parallax effect
				xd := (x0-float64(x))/DEBRIS_SCALE_DIST*float64(k+1) + x0 - float64(p.increment*k)/FLOAT_SCALE
				yd := (y0-float64(y))/DEBRIS_SCALE_DIST*float64(k+1) + y0 - float64(p.increment*k)/FLOAT_SCALE

				op = &ebiten.DrawImageOptions{}
				op.GeoM.Translate(xd, yd)
				//op.GeoM.Rotate(math.Pi / 180 * float64(p.increment))

				op.Blend = ebiten.BlendSourceOver
				screen.DrawImage(debrisImage, op)
				//log.Printf("%d, %d", xoffset, yoffset)

			}
		}
	}
}

func (p *Parallax) DrawObstacles(screen *ebiten.Image) {

	x, y := ebiten.CursorPosition()

	for _, a := range p.asteroids {
		cx := float64(a.cX)
		cy := float64(a.cY)

		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(-100, -66)
		op.GeoM.Rotate(-float64(p.increment) * a.rotationSpeed / (math.Pi * 120))
		op.GeoM.Translate(cx-float64(x), cy-float64(y))
		screen.DrawImage(roids[0], op)
	}
}

func (p *Parallax) DrawObstaclesWithShamLighting(screen *ebiten.Image) {

	x, y := ebiten.CursorPosition()

	for _, a := range p.asteroids {
		cx := float64(a.cX)
		cy := float64(a.cY)

		op := &ebiten.DrawImageOptions{}
		op.GeoM.Translate(-100, -66)
		op.GeoM.Rotate(-float64(p.increment) * a.rotationSpeed / (math.Pi * 120))
		op.GeoM.Translate(100, 66)

		//draw roid to temporary location
		blendTmp.DrawImage(roids[0], op)

		op = &ebiten.DrawImageOptions{}
		op.Blend = ebiten.BlendSourceAtop
		blendTmp.DrawImage(blendImage, op)

		op = &ebiten.DrawImageOptions{}
		op.GeoM.Translate(cx-float64(x), cy-float64(y))
		screen.DrawImage(blendTmp, op)

	}
}

func (p *Parallax) RepositionTest(screen *ebiten.Image) {

	var path vector.Path

	//set up origin and boundaries
	x0 := float32(p.Dimensions.Width / 2)
	y0 := float32(p.Dimensions.Height / 2)

	xd := float32(100.0)
	yd := float32(100.0)

	//connect the dots
	path.MoveTo(x0-xd, y0-yd)
	path.LineTo(x0+xd, y0-yd)
	path.LineTo(x0+xd, y0+yd)
	path.LineTo(x0-xd, y0+yd)
	path.LineTo(x0-xd, y0+yd)
	path.Close()

	var vs []ebiten.Vertex
	var is []uint16

	vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil)
	for i := range vs {
		vs[i].SrcX = 1
		vs[i].SrcY = 1
		vs[i].ColorR = float32(0xff) / 0xFF
		vs[i].ColorG = float32(0x00) / 0xFF
		vs[i].ColorB = float32(0x00) / 0xFF
		vs[i].ColorA = 1
	}
	screen.DrawTriangles(vs, is, whiteSubImage, nil)

	//minX := x0 - xd
	maxX := x0 + xd
	//minY := y0 - yd
	//maxY := y0 + yd
	//178x229

	roX := float64(178 / 4)
	roY := float64(229 / 4)
	startX := float64(maxX) + roX
	startY := float64(y0) + roY/2

	adjustment := 400.0
	currentX := startX - float64(p.increment) + adjustment*math.Floor(float64(p.increment)/adjustment)

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Scale(.5, .5)
	op.GeoM.Translate(-roX, -roY)
	op.GeoM.Rotate(-float64(p.increment) / (math.Pi * 12.0))
	op.GeoM.Translate(currentX, startY)

	screen.DrawImage(roids[1], op)

}
