package splashmenu

import (
	"bytes"
	"cosmos/diego/groovy"
	_ "embed"
	"image"
	"image/color"
	_ "image/jpeg"
	_ "image/png"
	"log"
	"math"
	"math/rand"

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

const (
	magicRatio          = 12
	maxGlitchOffset     = 25
	minGlitchInterval   = 60
	maxGlitchTimeOffset = 15
	//in seconds
	fadeTimer  = 1.0
	fadeOffset = 2.0
)

var (
	//go:embed assets/bsoft.png
	mImage_png    []byte
	imgDimensions = groovy.Area{Width: 827, Height: 628}

	//go:embed assets/title.png
	bImage_png    []byte
	titlePosition = groovy.Area{Width: 580, Height: 490}

	imgToBlur *ebiten.Image
	bImage    *ebiten.Image

	glitchTimer uint
)

func init() {
	// Decode an image from the image file's byte slice.
	img, _, err := image.Decode(bytes.NewReader(mImage_png))
	if err != nil {
		log.Fatal(err)
	}
	imgToBlur = ebiten.NewImageFromImage(img)

	// Decode an image from the image file's byte slice.
	img, _, err = image.Decode(bytes.NewReader(bImage_png))
	if err != nil {
		log.Fatal(err)
	}
	bImage = ebiten.NewImageFromImage(img)

	glitchTimer = 15
}

type Fader struct {
	Curtain *ebiten.Image
	counter int
	timer   float32
	offset  float32
	alpha   float32
	fps     float32
}

func NewFader(x, y int, t, offset float32) Fader {
	f := Fader{
		Curtain: ebiten.NewImage(x, y),
		timer:   t,
		counter: 0,
		alpha:   0.0,
		fps:     60,
		offset:  offset,
	}
	f.Curtain.Fill(color.RGBA{0x00, 0x00, 0x00, 0xFF})
	return f
}
func (f *Fader) Update() {
	f.counter++

	f.alpha = (float32(f.counter) - (f.offset * f.fps)) / (f.timer * f.fps)
	if f.alpha < 0 {
		f.alpha = 0
	}
}

func (f *Fader) Draw(img *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	op.ColorScale.Scale(1, 1, 1, f.alpha)
	img.DrawImage(f.Curtain, op)
}

func (f *Fader) IsComplete() bool {
	return f.alpha > 1.0
}

type Bsoft struct {
	Dimensions   groovy.Area
	events       map[groovy.SceneEvent]func()
	bgcolor      color.RGBA
	increment    int
	renderTarget *ebiten.Image
	mosaicRatio  uint
	countDown    uint
	curtain      Fader
}

func NewBsoft() Bsoft {
	return Bsoft{
		bgcolor:     backgroundBaseColor,
		events:      make(map[groovy.SceneEvent]func()),
		mosaicRatio: 16,
		increment:   0,
	}
}

func (b *Bsoft) Update() error {
	b.increment++

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

	for _, k := range keysPressed {
		switch k {
		case ebiten.KeyUp:
			b.mosaicRatio = b.mosaicRatio + 1
		case ebiten.KeyDown:
			if b.mosaicRatio > 1 {
				b.mosaicRatio = b.mosaicRatio - 1
			}
		case ebiten.KeyQ:
			if b.events[groovy.COMPLETED] != nil {
				b.events[groovy.COMPLETED]()
			}
		default:
		}

	}

	if b.curtain.IsComplete() {
		if b.events[groovy.COMPLETED] != nil {
			b.events[groovy.COMPLETED]()
			b.curtain.Clear()

		}
	}
	b.curtain.Update()

	return nil
}

func (f *Fader) Clear() {
	f.alpha = 0 //reset fader
	f.counter = 0
}

func (b *Bsoft) Draw(screen *ebiten.Image) {
	b.DrawGlitchLogo(screen)
	b.DrawGlitchLogoText(screen)
	b.curtain.Draw(screen)
}

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

// sets sene dimensions
func (b *Bsoft) SetDimensions(a groovy.Area) {
	b.Dimensions = a
	b.renderTarget = ebiten.NewImage(imgDimensions.Width, imgDimensions.Height)
	b.curtain = NewFader(a.Width, a.Height, fadeTimer, fadeOffset)
}

func (b *Bsoft) DrawGlitchLogo(screen *ebiten.Image) {
	//summation of sinusoids with different frequencies for 'step' response on pixel-glithcy-ness
	var sum float64 = 0

	for i := 0; i < 10; i++ {
		w := math.Sin(float64(b.increment) / (math.Pi * float64(i+1)))
		sum = sum + w
	}
	ratio := sum + magicRatio

	// Shrink the image once.
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Scale(1/ratio, 1/ratio)
	b.renderTarget.DrawImage(imgToBlur, op)

	// Enlarge the shrunk image.
	// The filter is the nearest filter, so the result will be mosaic.
	op = &ebiten.DrawImageOptions{}
	op.GeoM.Scale(ratio, ratio)

	op.GeoM.Translate(float64(b.Dimensions.Width)/2-float64(imgDimensions.Width)/2, float64(b.Dimensions.Height)/2-float64(imgDimensions.Height)/2-35)
	screen.DrawImage(b.renderTarget, op)
}

func (b *Bsoft) DrawGlitchLogoText(screen *ebiten.Image) {
	//glitchy twitch title behaviour
	op := &ebiten.DrawImageOptions{}
	var glitchX int = 0
	var glitchY int = 0
	X := minGlitchInterval
	Y := maxGlitchTimeOffset

	//glithiness active until countdown resolved, randomize the delta offset
	//creates temporary glitch effect on the logo text, this is achieved as follows:
	// 1. if the glitch frame counter is active, glitch offset is in effect and computed
	// 2. otherwise, we engage the glitch after X frames have passed with a random Y frame offset
	if b.countDown > 0 {
		glitchX = rand.Intn(maxGlitchOffset) * getRandomDirection()
		glitchY = rand.Intn(maxGlitchOffset) * getRandomDirection()
		b.countDown--

	} else if b.increment%(X+rand.Intn(Y)) == 0 {
		b.countDown = glitchTimer //set glitch timer, in frames
	}
	op.GeoM.Translate(float64(titlePosition.Width+glitchX), float64(titlePosition.Height+glitchY))
	screen.DrawImage(bImage, op)
}

// returns +1 or -1 as an integer
func getRandomDirection() int {
	if rand.Intn(2) == 1 {
		return -1
	}
	return 1
}
