package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens"
	"lab.zaar.be/thefish/alchemyst-go/ui"
	"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
	"lab.zaar.be/thefish/alchemyst-go/util"
	blt "lab.zaar.be/thefish/bearlibterminal"
	"os"
	"runtime"
	"time"
)

var modifiers = []int{blt.TK_SHIFT, blt.TK_ALT, blt.TK_CONTROL}

// Рецепт чтобы убежать от [fatal] 'refresh' was not called from the main thread
// https://github.com/golang/go/wiki/LockOSThread
func init() {
	runtime.LockOSThread()
}

type GameState struct {
	mainfunc chan func()
	exit     chan struct{}
	input    chan string
	rawInput chan int
}

// do runs f on the main thread.
func (*GameState) Do(f func()) {
	done := make(chan struct{}, 1)
	State.mainfunc <- func() {
		f()
		done <- struct{}{}
	}
	<-done
}

//we can run logic in separate goroutines
//
//    go doSometing(State,...)
//
//and there we go like this:
// func doSomething(State main.GameState, args...) {
// 		...
// 			State.Do(func() {
//				...do stuff in main thread
//			})
// 		...
// }

var State = GameState{
	mainfunc: make(chan func()),
	exit:     make(chan struct{}, 1),
	input:    make(chan string, 1),
	rawInput: make(chan int, 1),
}

func main() {

	config := util.LoadConfig()
	var logLevels = map[string]zerolog.Level{"debug": zerolog.DebugLevel, "info": zerolog.InfoLevel, "warn": zerolog.WarnLevel}
	var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity])

	mainCtx := util.NewClientContext(config, &logger)

	mw := mainwindow.Init(mainCtx)
	defer mw.Close()

	setupLayers(mw)

	level := gamemap.NewLevel(mainCtx, "test", 1)
	level = mapgens.DefaultGen(level)
	vp := mainwindow.NewViewPort(40, 0, 60, 47, level, mw.GetLayer("base"))
	vp.Render()

	go decodeInput(mainCtx, mw.GetLayer("base"))

	//but every call to bearlibterminal must be wrapped to closure and passed to mainfunc
	var exit = false
	for !exit {

		select {
		case State.rawInput <- ui.ReadKeyCode():
			break
		case pressed := <-State.input:
			mw.GetLayer("base").ClearArea(0, 3, 40, 1)
			mw.GetLayer("base").Print(1, 3, "Key: "+pressed)
			mw.GetLayer("base").Print(1, 6, "█")
			break
		//case f := <-State.mainfunc:
		//	f()
		//	break
		case <-State.exit:
			mainCtx.Logger().Warn().Msg("quitting NOW")
			exit = true
			break
		// не оставляйте default в бесконесчном select {} - сожрет всё CPU
		default:
			vp.Render()
			blt.Refresh()
		}

	}
	mainCtx.Logger().Info().Msg("pre-shutdown sequence")
}

func setupLayers(mainwindow *mainwindow.MainWindow) {
	mainwindow.AddLayer("base", 0, "white")
	mainwindow.AddLayer("overlay", 1, "white")
	mainwindow.AddLayer("menu", 2, "white")
}

func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
	var exit = false
	for !exit{
		select {
		case keycode := <-State.rawInput:
			if keycode == blt.TK_NONE {
				continue
			}
			var pressed= ""
			var isModifier, _= util.InArray(keycode, modifiers)
			if !isModifier {

				pressed = ui.Scancodemap[keycode]

				if blt.Check(blt.TK_SHIFT) != 0 {
					pressed = "Shift+" + pressed
				}
				if blt.Check(blt.TK_ALT) != 0 {
					pressed = "Alt+" + pressed
				}
				if blt.Check(blt.TK_CONTROL) != 0 {
					pressed = "Ctrl+" + pressed
				}

				//global hotkeys
				switch pressed {
				case "F10":
					State.Do(func() {
						blt.Set("window: size=100x47; font: ./resources/fonts-ttf/UbuntuMono-R.ttf, size=11;")
					})
				case "Ctrl+q":
					fallthrough
				case "Escape":
					ctx.Logger().Info().Msg("exiting on quit command...")
					State.exit <- struct{}{}
					ctx.Logger().Info().Msg("...done")
					exit = true
					return
				default:
					State.input <- pressed
				}
			}
		}
	}
}