package screenmanager

import (
	"mover/gamedata"
	"mover/screens"

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

const (
	defaultWidth  = 1024
	defaultHeight = 768
)

type Manager struct {
	Info           gamedata.GameInfo
	currentScene   screens.Screen
	currentSceneId uint
	nextSceneId    uint
	screens        []screens.Screen
	internalerr    error
}

// can be used to create default manager instance
func NewManager() Manager {
	return Manager{
		Info: gamedata.GameInfo{
			Name:    "survive",
			Version: "0.34",
			Dimensions: gamedata.Area{
				Width:  defaultWidth,
				Height: defaultHeight,
			},
		},
		currentSceneId: 0,
		nextSceneId:    1,
		internalerr:    nil,
	}
}

// ebitengine update proxy on behalf of current scene
func (m *Manager) Update() error {
	if m.currentScene == nil {
		return nil
	}

	err := m.currentScene.Update()
	if err != nil {
		return err
	}

	return m.internalerr
}

// shutdown application
func (m *Manager) Quit() {
	m.internalerr = ebiten.Termination
}

// calls current scene's draw method if the currentscene is valid
func (m *Manager) Draw(screen *ebiten.Image) {
	if m.currentScene != nil {
		m.currentScene.Draw(screen)
	}
}

// ebitengine proxy for layout
func (m *Manager) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return m.Info.Dimensions.Width, m.Info.Dimensions.Height
}

// appends scene to the managed screens
func (m *Manager) AddScene(s screens.Screen) {
	setDefaultHandlers(m, s)
	s.SetDimensions(m.Info.Dimensions)
	m.screens = append(m.screens, s)
}

// sets the default callback handlers for a given scene within manager
// Default Handling behaviours:
//
//	reset: sets (scene, nextscene) to {0, 1}
//	scene completion: sets (scene, nextscene) to {nextscene, nextscene+1}
//	end game: shutdown groovy
//
//	note: NOOP and RELOAD are purposefully not mapped; they are scene
//	specific and should be mapped to by user of groovy
func setDefaultHandlers(m *Manager, s screens.Screen) {
	s.SetEventHandler(screens.EventReset, func() { m.ResetScenes() })
	s.SetEventHandler(screens.EventCompleted, func() { m.TransitionScene() })
	s.SetEventHandler(screens.EventEndgame, func() { m.Quit() })
}

// we're going to reset the scene to the first one
func (m *Manager) ResetScenes() {
	m.currentSceneId = 0
	m.nextSceneId = 1
	m.SetCurrentScene(0)
}

// sets the current scene, based on sceneindex n
// n > scenelist, quit
// otherwise, scene = n
func (m *Manager) SetCurrentScene(sceneId uint) {
	if sceneId >= uint(len(m.screens)) {
		m.Quit()
	} else {
		m.currentSceneId = sceneId
		m.currentScene = m.screens[sceneId]
		m.nextSceneId = m.currentSceneId + 1
	}
}

// handle scene transition
func (m *Manager) TransitionScene() {
	m.SetCurrentScene(m.nextSceneId)
}

// set new sceneId as the successor
func (m *Manager) SetNextScene(sceneId uint) {
	m.nextSceneId = sceneId
}

// sets sene dimensions
func (m *Manager) SetDimensions(a gamedata.Area) {
	m.Info.Dimensions = a
}

// report number of total screens
func (m *Manager) SceneCount() uint {
	return uint(len(m.screens))
}

func (m *Manager) GetScene(sceneId uint) screens.Screen {
	return m.screens[sceneId]
}
