package screens

import (
	"math"
	"mover/assets"
	"mover/gamedata"
	"mover/gameelement"

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

type Primary struct {
	events     map[ScreenManagerEvent]func()
	dimensions gamedata.Area
	elements   []gameelement.GameElement
	gameevents map[gamedata.GameEvent]bool

	paused           bool
	gameover         bool
	musicInitialized bool

	audioplayer *audio.Player
}

func NewPrimary() *Primary {
	p := &Primary{
		events:           make(map[ScreenManagerEvent]func()),
		paused:           false,
		gameover:         false,
		musicInitialized: false,
	}

	gamearea := gamedata.Area{Width: 640, Height: 480}

	//initialize our layer map
	p.gameevents = make(map[gamedata.GameEvent]bool)

	//create background layer
	p.elements = append(p.elements, gameelement.NewBackground(gamearea))

	//create canvas (game) layer
	canvas := gameelement.NewCanvas(gamearea)
	canvas.RegisterEvents(gamedata.GameEventPlayerDeath, p.EventHandlerPlayerDeath)
	canvas.RegisterEvents(gamedata.GameEventCharge, p.EventHandlerCharge)
	canvas.RegisterEvents(gamedata.GameEventNewShot, p.EventHandlerNewShot)
	canvas.RegisterEvents(gamedata.GameEventTargetHit, p.EventHandlerTargetHit)
	canvas.RegisterEvents(gamedata.GameEventExplosion, p.EventHandlerExplosion)
	canvas.RegisterEvents(gamedata.GameEventFireball, p.EventHandlerFireball)
	p.elements = append(p.elements, canvas)

	//rainlayer
	rain := gameelement.NewRainLayer(gamearea)
	rain.Initialize()
	p.elements = append(p.elements, rain)

	//create foreground cloud layer
	clouds := gameelement.NewCloudLayer(gamearea)
	clouds.Initialize()
	p.elements = append(p.elements, clouds)

	return p
}

func (p *Primary) Update() error {

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

	//collect all inputs
	inputs := p.CollectInputs()
	if inputs.Quit {
		p.events[EventEndgame]()
	}

	if inputs.Reset {
		p.Reset()
	}

	p.ProcessEventAudio()

	if inputs.Start {
		if p.gameover {
			p.Reset()
		} else {
			p.TogglePause()
		}
	}

	//primary game loop, for each element pass along the inputs
	//and process its update logic
	if !p.paused {
		for _, ge := range p.elements {
			ge.SetInputs(inputs)
			ge.Update()
		}
	}

	return nil
}

func (p *Primary) Draw(screen *ebiten.Image) {
	//here we simply call each game elements draw function
	//as a layer on top of each other
	for _, ge := range p.elements {
		ge.Draw(screen)
	}
}

func (p *Primary) SetEventHandler(e ScreenManagerEvent, f func()) {
	p.events[e] = f
}

func (p *Primary) SetDimensions(a gamedata.Area) {
	p.dimensions = a
}

func (p *Primary) CollectInputs() gamedata.GameInputs {
	if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
		p.events[EventEndgame]()
	}

	gi := gamedata.GameInputs{}

	//axes
	inpx := ebiten.GamepadAxisValue(0, 0)
	inpy := ebiten.GamepadAxisValue(0, 1)

	//handle wasd input
	if ebiten.IsKeyPressed(ebiten.KeyD) {
		inpx = 1
	}
	if ebiten.IsKeyPressed(ebiten.KeyA) {
		inpx = -1
	}
	if ebiten.IsKeyPressed(ebiten.KeyS) {
		inpy = 1
	}
	if ebiten.IsKeyPressed(ebiten.KeyW) {
		inpy = -1
	}

	gi.XAxis = inpx
	gi.YAxis = inpy

	xaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickHorizontal)
	yaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickVertical)

	if yaxis <= 0.09 && yaxis >= -0.09 {
		yaxis = 0
	}
	if xaxis <= 0.09 && xaxis >= -0.09 {
		xaxis = 0
	}

	gi.ShotAngle = math.Atan2(yaxis, xaxis)

	gi.CycleWeapon = inpututil.IsStandardGamepadButtonJustPressed(0, ebiten.StandardGamepadButtonFrontTopRight)
	gi.Charge = inpututil.IsStandardGamepadButtonJustPressed(0, ebiten.StandardGamepadButtonRightStick)
	gi.Start = inpututil.IsStandardGamepadButtonJustPressed(0, ebiten.StandardGamepadButtonCenterRight)
	gi.Shot = ebiten.IsStandardGamepadButtonPressed(0, ebiten.StandardGamepadButtonFrontBottomRight)
	gi.Quit = inpututil.IsKeyJustPressed(ebiten.KeyQ)
	gi.Reset = inpututil.IsKeyJustPressed(ebiten.KeyR)

	return gi
}

func (p *Primary) TogglePause() {
	p.paused = !p.paused
	var player *audio.Player
	if p.paused {
		player = audioContext.NewPlayerFromBytes(assets.PauseIn)
		p.audioplayer.Pause()
	} else {
		player = audioContext.NewPlayerFromBytes(assets.PauseOut)
		p.audioplayer.Play()
	}
	player.Play()
}

func (p *Primary) Reset() {
	p.paused = false
	p.gameover = false

	for _, ge := range p.elements {
		ge.Initialize()
	}
}

func (p *Primary) ProcessEventAudio() {
	for event, occurred := range p.gameevents {
		if occurred {
			p.PlayAudio(event)
			p.gameevents[event] = false
		}
	}
}

func (p *Primary) PlayAudio(e gamedata.GameEvent) {
	switch e {
	case gamedata.GameEventPlayerDeath:
		player := audioContext.NewPlayerFromBytes(assets.HeroDeath)
		player.Play()
	case gamedata.GameEventCharge:
		player := audioContext.NewPlayerFromBytes(assets.Magic)
		player.Play()
	case gamedata.GameEventNewShot:
		player := audioContext.NewPlayerFromBytes(assets.Shot)
		player.Play()
	case gamedata.GameEventTargetHit:
		player := audioContext.NewPlayerFromBytes(assets.TargetHit)
		player.Play()
	case gamedata.GameEventExplosion:
		player := audioContext.NewPlayerFromBytes(assets.Splode)
		player.Play()
	case gamedata.GameEventFireball:
		player := audioContext.NewPlayerFromBytes(assets.Flare)
		player.Play()
	}
}

func (p *Primary) EventHandlerPlayerDeath() {
	p.gameevents[gamedata.GameEventPlayerDeath] = true
	p.gameover = true
}

func (p *Primary) EventHandlerCharge() {
	p.gameevents[gamedata.GameEventCharge] = true
}

func (p *Primary) EventHandlerNewShot() {
	p.gameevents[gamedata.GameEventNewShot] = true
}

func (p *Primary) EventHandlerTargetHit() {
	p.gameevents[gamedata.GameEventTargetHit] = true
}

func (p *Primary) EventHandlerExplosion() {
	p.gameevents[gamedata.GameEventExplosion] = true
}

func (p *Primary) EventHandlerFireball() {
	p.gameevents[gamedata.GameEventFireball] = true
}
