package main

import (
	"fmt"
	"io"
	"log"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/spf13/afero"
	"opslag.de/schobers/allg5"
	"opslag.de/schobers/fs/ricefs"
	"opslag.de/schobers/fs/vfs"
	"opslag.de/schobers/geom"
	"opslag.de/schobers/krampus19/gut"
)

//go:generate rice embed-syso

func main() {
	err := run()
	if err != nil {
		log.Fatal(err)
	}
}

func resources() (vfs.CopyDir, afero.Fs, error) {
	var embeddedFs = ricefs.NewFs(rice.MustFindBox("res"))
	var osFs = afero.NewBasePathFs(afero.NewOsFs(), "./res")
	var fs = vfs.NewFallbackFs(osFs, embeddedFs)
	copy, err := vfs.NewCopyDir(fs)
	if err != nil {
		return nil, nil, err
	}
	return copy, fs, nil
}

func run() error {
	defer log.Printf("All cleaned up, bye bye!")
	cons := &gut.Console{}
	log.SetOutput(io.MultiWriter(log.Writer(), cons))

	res, resFs, err := resources()
	if err != nil {
		return err
	}
	defer res.Destroy()

	settings := newDefaultSettings()
	err = settings.LoadDefault()
	if err != nil {
		log.Printf("Unable to load settings, falling back on defaults.")
	}
	err = settings.StoreDefault()
	if err != nil {
		log.Printf("Unable to store settings; err: %v", err)
	}

	log.Printf("Initializing Allegro.")
	var init = allg5.InitAll
	init.Audio = false
	err = allg5.Init(init)
	if err != nil {
		return err
	}

	log.Printf("Creating display.")
	var size = geom.Pt(1280, 720)
	dispOptions := allg5.NewDisplayOptions{Vsync: true, Fullscreen: true}
	if settings.Video.Windowed {
		dispOptions.Frameless = true
		dispOptions.Windowed = true
	} else {
		dispOptions.Fullscreen = true
		str := func(m allg5.DisplayMode) string { return fmt.Sprintf("%d x %d", m.Width, m.Height) }
		for _, mode := range allg5.DisplayModes() {
			if str(mode) == settings.Video.DisplayMode {
				size = geom.Pt(mode.Width, mode.Height)
			}
		}
	}
	disp, err := allg5.NewDisplay(size.X, size.Y, dispOptions)
	if err != nil {
		return err
	}
	defer disp.Destroy()

	iconPath, err := res.Retrieve("icon.png")
	if err != nil {
		log.Printf("Unable to retrieve icon.")
	} else {
		icon, err := allg5.LoadBitmap(iconPath)
		if err != nil {
			log.Printf("Unable to load icon bitmap.")
		} else {
			defer icon.Destroy()
			disp.SetIcon(icon)
		}
	}

	eq, err := allg5.NewEventQueue()
	if err != nil {
		return err
	}
	defer eq.Destroy()

	eq.RegisterDisplay(disp)
	eq.RegisterKeyboard()
	eq.RegisterMouse()

	fps := gut.NewFPS()
	defer fps.Destroy()

	game := &game{}
	err = game.Init(disp, settings, res, resFs, cons, fps)
	if err != nil {
		return err
	}
	defer game.Destroy()

	log.Printf("Starting game loop")
	defer log.Printf("Stopped game loop, cleaning up")
	start := time.Now()
	for {
		game.ctx.Tick = time.Now().Sub(start)

		allg5.ClearToColor(game.ctx.Palette.Icon)
		game.Render()
		disp.Flip()
		fps.Count()

		e := eq.Get()
		for e != nil {
			switch e := e.(type) {
			case *allg5.DisplayCloseEvent:
				log.Printf("Stopping game loop, user closed display")
				return nil
			case *allg5.KeyCharEvent:
				if e.KeyCode == allg5.KeyF4 && e.Modifiers&allg5.KeyModAlt != 0 {
					log.Printf("Stopping game loop, user pressed Alt+F4")
					return nil
				}
			case *allg5.DisplayResizeEvent:
				game.ctx.DisplaySize = geom.Pt(disp.Width(), disp.Height())
			}
			game.Handle(e)
			e = eq.Get()
		}

		if game.scene.Proxy == nil {
			log.Printf("Stopping game loop, user quit via an in-game option")
			return nil
		}
	}
}
