package game

import (
	"ducky/assets"
	"ducky/elements"
	"ducky/fonts"
	"ducky/gamedata"
	"fmt"
	"image/color"
	"math"
	"math/rand"

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

const (
	screenWidth  = 640
	screenHeight = 480

	duckyWidth  = 32
	duckyHeight = duckyWidth

	spotResetDuration       = 120
	duckySpottedTextOffsetX = 120
	duckySpottedTextOffsetY = 32
	duckySpottedTextSize    = 30
	toastCount              = 120
)

var (
	audioContext = audio.NewContext(assets.SampleRate)
)

type Game struct {
	initialized bool
	reducky     *elements.Ducky
	ducky       *elements.Ducky
	spotlight   *elements.Spotlight
	cycle       int
	mousepos    gamedata.Coordinates

	darkness  *ebiten.Image
	offscreen *ebiten.Image

	reduckycol int
	reduckyrow int

	spottedticks    int
	spotted         bool
	toast           bool
	toastcounter    int
	disabledarkness bool

	sliders []*elements.Slider

	reachcount int

	musicInitialized bool
	audioplayer      *audio.Player
}

func (g *Game) Update() error {

	g.HandleInput()

	if !g.initialized {
		g.Initialize()
	}

	g.UpdateDuck()
	g.UpdateDetection()
	//g.UpdateSliders()

	g.cycle++

	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	screen.Clear()
	g.darkness.Clear()

	if g.initialized {

		//g.darkness.Fill(color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff})
		g.darkness.Fill(color.Black)
		g.ducky.Draw()
		g.reducky.Draw()

		op := &ebiten.DrawImageOptions{}
		for x := 0; x < screenWidth/duckyWidth; x++ {
			for y := 0; y < screenHeight/duckyWidth; y++ {
				op.GeoM.Reset()
				op.GeoM.Translate(float64(x)*duckyWidth, float64(y)*duckyHeight)
				if !(x == g.reduckycol && y == g.reduckyrow) {
					screen.DrawImage(g.ducky.Sprite, op)
				}
			}
		}

		op = &ebiten.DrawImageOptions{}
		op.GeoM.Translate(float64(g.reduckycol)*duckyWidth, float64(g.reduckyrow)*duckyHeight)
		screen.DrawImage(g.reducky.Sprite, op)

		op = &ebiten.DrawImageOptions{}
		op.GeoM.Translate(-duckyWidth/2, -duckyHeight/2)
		op.GeoM.Translate(g.mousepos.X, g.mousepos.Y)
		//screen.DrawImage(assets.ImageBank[assets.Orb], op)

		g.spotlight.Draw()

		g.offscreen.Clear()
		op = &ebiten.DrawImageOptions{}

		spotlight_w := float64(g.spotlight.Sprite.Bounds().Dx())
		spotlight_h := float64(g.spotlight.Sprite.Bounds().Dy())

		op.GeoM.Translate(-spotlight_w/2, -spotlight_h/2)
		op.GeoM.Translate(g.mousepos.X, g.mousepos.Y)
		//g.offscreen.DrawImage(assets.ImageBank[assets.Orb], op)
		g.offscreen.DrawImage(g.spotlight.Sprite, op)

		op.GeoM.Reset()
		op.Blend = ebiten.BlendXor
		g.offscreen.DrawImage(g.darkness, op)

		if !g.disabledarkness {
			op = &ebiten.DrawImageOptions{}
			if g.toast {
				op.GeoM.Translate(-screenWidth/2, -screenHeight/2)
				op.GeoM.Rotate(float64(g.toastcounter) / (math.Pi * 2))
				op.GeoM.Translate(screenWidth/2, screenHeight/2)
			}
			screen.DrawImage(g.offscreen, op)
		}

		if g.spotted && !g.toast {
			font := &text.GoTextFace{
				Source: fonts.DuckyFont.Karen,
				Size:   duckySpottedTextSize,
			}
			top := &text.DrawOptions{}
			top.GeoM.Translate(screenWidth/2-duckySpottedTextOffsetX, screenHeight/2-duckySpottedTextOffsetY)
			if g.cycle%30 < 15 {
				text.Draw(screen, "DUCKY SPOTTED", font, top)
			}
		}

		for _, s := range g.sliders {
			s.Draw()
			op = &ebiten.DrawImageOptions{}
			op.GeoM.Translate(s.GetPosition().X, s.GetPosition().Y)
			screen.DrawImage(s.Sprite, op)
		}

	}
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenwidth, screenheight int) {
	return screenWidth, screenHeight
}

func (g *Game) Initialize() {
	assets.LoadImages()

	g.offscreen = ebiten.NewImage(screenWidth, screenHeight)
	g.darkness = ebiten.NewImage(screenWidth, screenHeight)

	g.spotlight = elements.NewSpotlight(assets.ImageBank[assets.Orb])
	g.ducky = elements.NewDucky(assets.ImageBank[assets.Ducky])
	g.reducky = elements.NewDucky(assets.ImageBank[assets.ReDucky])

	for i := 0; i < 5; i++ {
		slider := elements.NewSlider()
		g.sliders = append(g.sliders, slider)
	}

	g.initialized = true

	if !g.musicInitialized {
		//s := audio.NewInfiniteLoop(assets.SoundBank[assets.MainLoop], assets.SoundBank[assets.MainLoop].Length())
		s := audio.NewInfiniteLoop(assets.SoundBankOgg[assets.MainLoopOgg], assets.SoundBankOgg[assets.MainLoopOgg].Length())
		g.audioplayer, _ = audioContext.NewPlayer(s)
		g.audioplayer.Play()
		g.musicInitialized = true
	}

	g.Reset()
}

func (g *Game) UpdateDuck() {
	g.ducky.Update()
	g.reducky.Update()

	x, y := ebiten.CursorPosition()
	g.mousepos.X = float64(x)
	g.mousepos.Y = float64(y)
}

func (g *Game) Reset() {
	g.reduckycol = rand.Intn(screenWidth / duckyHeight)
	g.reduckyrow = rand.Intn(screenHeight / duckyWidth)

	for i, slider := range g.sliders {
		var yoffset float64

		if i%2 == 0 {
			yoffset = screenHeight
		} else {
			yoffset = -screenHeight
		}

		slider.SetPosition(gamedata.Coordinates{X: float64(i) * screenWidth / 5, Y: yoffset})
		slider.SetTargetPosition(gamedata.Coordinates{X: float64(i) * screenWidth / 5, Y: 0})
		slider.Reset()
	}

	//fmt.Printf("Ducky Position: %d, %d\n", g.reduckycol, g.reduckyrow)
	g.spottedticks = 0
	g.toast = false
	g.reachcount = 0
	g.toastcounter = 0
}

func (g *Game) HandleInput() {
	if inpututil.IsKeyJustPressed(ebiten.KeyR) {
		g.Reset()
	}

	g.disabledarkness = ebiten.IsKeyPressed(ebiten.KeySpace)
}

func (g *Game) UpdateDetection() {
	x, y := ebiten.CursorPosition()

	if g.reduckycol*duckyWidth <= x && x <= (g.reduckycol+1)*duckyWidth &&
		g.reduckyrow*duckyHeight <= y && y <= (g.reduckyrow+1)*duckyHeight && !g.toast {
		g.spottedticks++
		g.spotted = true
		fmt.Println("ducky spotted!")
	} else {
		g.spotted = false
	}

	if g.spottedticks > spotResetDuration {
		g.toast = true
	}

	if g.toast {
		g.UpdateSliders()
		g.toastcounter++
	}

	if g.toastcounter > toastCount {
		g.Reset()
	}

}

func (g *Game) UpdateSliders() {
	for _, s := range g.sliders {
		s.Update()
	}
}
