package groovy

import (
	"os"

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

const (
	defaultWidth  = 1024
	defaultHeight = 768
)

type Area struct {
	Width  int
	Height int
}

type GameInfo struct {
	Name       string
	Version    string
	Dimensions Area
}

type Manager struct {
	Info           GameInfo
	currentScene   Scene
	currentSceneId uint
	nextSceneId    uint
	scenes         []Scene
	EventMap       map[SceneEvent]func()
}

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

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

	return m.currentScene.Update()
}

// shutdown application
func (m *Manager) Quit() {
	os.Exit(0)
}

// 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 scenes
func (m *Manager) AddScene(s Scene) {
	setDefaultHandlers(m, s)
	s.SetDimensions(m.Info.Dimensions)
	m.scenes = append(m.scenes, 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 Scene) {
	s.SetEventHandler(RESET, func() { m.ResetScenes() })
	s.SetEventHandler(COMPLETED, func() { m.TransitionScene() })
	s.SetEventHandler(ENDGAME, 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.scenes)) {
		m.Quit()
	}
	m.currentSceneId = sceneId
	m.currentScene = m.scenes[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 Area) {
	m.Info.Dimensions = a
}

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

func (m *Manager) GetScene(sceneId uint) Scene {
	return m.scenes[sceneId]
}

func (a *Area) Area() int {
	return a.Height * a.Width
}
