package main

import (
	"context"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamelog"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens"
	"lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
	"lab.zaar.be/thefish/alchemyst-go/engine/items"
	"lab.zaar.be/thefish/alchemyst-go/engine/mob"
	"lab.zaar.be/thefish/alchemyst-go/engine/mob/movement"
	"lab.zaar.be/thefish/alchemyst-go/engine/screens"
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
	"lab.zaar.be/thefish/alchemyst-go/ui"
	"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
	"lab.zaar.be/thefish/alchemyst-go/util"
	"lab.zaar.be/thefish/alchemyst-go/util/appctx"
	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()
}

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

var State = gamestate.GameState{
	Mainfunc:     make(chan func()),
	Exit:         make(chan struct{}, 1),
	Input:        make(chan string, 1),
	RawInput:     make(chan int, 1),
	FovRecompute: make(chan struct{}, 1),
	Redraw:       make(chan struct{}, 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])

	// set up context
	appctx.NewClientContext(config, &logger)
	mainCtx := appctx.ClientState

	//set up main window
	mw := mainwindow.Init(mainCtx)
	defer mw.Close()

	setupLayers(mw)

	//set up input decoder
	go decodeInput(mainCtx, mw.GetLayer("base"))

	//fixme set up (load / generate) level - move to game / enter or title / exit
	level, rooms := mapgens.DefaultGen(gamemap.NewLevel("test", 1))
	//level, rooms := mapgens.DelaunayMstGen(mainCtx, gamemap.NewLevel("test", 1))
	//level, rooms := mapgens.DelaunayMstExtGen(gamemap.NewLevel("test", 1))
	//level, rooms := mapgens.DelaunayPureGen(gamemap.NewLevel("test", 1))
	State.Level = level

	sidebarWidth := 0

	//Set up viewport
	vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0))

	//set up controller

	controller := ecs.NewController()

	controller.MapComponentClass(ecs.CoordsComponent, types.Coords{})
	controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{})
	controller.MapComponentClass(ecs.MobComponent, mob.Mob{})
	controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{})
	controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{})
	controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{})
	controller.MapComponentClass(ecs.BackpackComponent, items.Backpack{})

	moveable := movement.Moveable{
		Controller: controller,
		Level:      level,
	}

	items.Init(controller)

	bp := items.Backpack{MaxMass: 100, MaxBulk: 100}

	//Set up Screen Manager
	screenMgr := types.NewScreenManager()
	screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr))
	screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp, controller, screenMgr))
	screenMgr.AddScreen("help", screens.NewMenuScreen(
		mw,
		screenMgr,
		"Help",
		"Keybindings:",
		//"[color=yellow]Note[/color]: Many of these are not implemented yet",
		"[color=yellow]Note[/color]: Many of these are not implemented yet",
		types.NewCenteredRect(mw.Rect, 50, 15),
		true).
		SetBgColor("#ef1d494f").
		SetFgColor("white").
		SetItems([]interface{}{
			"hjklyubn, NumPad 12346789, arrow keys - move",
			"s or . - pass turn",
			"g or , - pick up item",
			"i - inventory",
			"? - this screen",
			"Ctrl+q - exit",
			"f or F - fire weapon or throw item",
			"z or Z - cast a spell",
			"p - pray",
			"Ctrl+p - message log",
		}).MakeList(),
	)

	inv := screens.InventoryScreen{
		MenuScreen: screens.NewMenuScreen(
			mw,
			screenMgr,
			"Inventory",
			"Items in your backpack:",
			//"[color=yellow]Note[/color]: Many of these are not implemented yet",
			"",
			types.NewCenteredRect(mw.Rect, 70, 25),
			true).
			SetBgColor("#ef305c70").
			SetFgColor("white"),
	}

	screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen(
		mainCtx,
		mw,
		controller,
		screenMgr,
		&State,
		types.NewCenteredRect(mw.Rect, 70, 25),
		true,
	).SetBgColor("#ef6d559d").
		SetFgColor("white"),
	)

	screenMgr.SetScreenByName("title")

	//fixme set up (load / generate) player - move to game / enter or title / exit
	player := controller.CreateEntity([]ecs.Component{})

	controller.AddComponent(player, types.Appearance{
		Glyph: types.PlainGlyphHolder{"@"},
		ColorSet: types.TileColorSet{
			Fg: types.PlainColorHolder{255, 255, 255, 255},
		},
	})

	controller.AddComponent(player, rooms[0].Center) //implicit Coords
	controller.AddComponent(player, moveable)
	controller.AddComponent(player, bp)

	//fixme adding items
	potion := controller.CreateEntity([]ecs.Component{})
	controller.AddComponent(potion, types.Appearance{
		Glyph: types.PlainGlyphHolder{"!"},
		ColorSet: types.TileColorSet{
			Fg: types.PlainColorHolder{255, 55, 255, 222},
		},
	})
	controller.AddComponent(potion, rooms[0].Center)                 //implicit Coords
	controller.AddComponent(potion, items.Carried{Mass: 5, Bulk: 3}) //fixme generate from blueprint!
	controller.AddComponent(potion, items.Usable{})
	controller.AddComponent(potion, items.Consumable{})
	controller.AddComponent(potion, ecs.Named{Name: "first potion"})

	potion2 := controller.CreateEntity([]ecs.Component{})
	controller.AddComponent(potion2, types.Appearance{
		Glyph: types.PlainGlyphHolder{Glyph: "!"},
		ColorSet: types.TileColorSet{
			Fg: types.PlainColorHolder{A: 255, R: 222, G: 255, B: 55},
		},
	})
	controller.AddComponent(potion2, rooms[1].Center) //implicit Coords
	controller.AddComponent(potion2, items.Carried{Mass: 5, Bulk: 3})
	controller.AddComponent(potion2, items.Usable{})
	controller.AddComponent(potion2, items.Consumable{})
	controller.AddComponent(potion2, ecs.Named{Name: "second potion"})
	//fixme end setting up items

	State.Player = player
	State.Controller = controller

	screenMgr.AddScreen("inventory", inv.MakeInverntory(player))

	gamelog.Log.SetMaxHeight(5)
	gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG Generating level of branch test depth 1")
	gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding")
	gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding")
	gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding")
	gamelog.Log.Msg("TrOlolo")
	gamelog.Log.Msg("[color=red]Game[/color] starting...")

	//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:
			screenMgr.CurrentScreen.HandleInput(pressed)
			break
			//case f := <-State.mainfunc:
			//	f()
			//	break
		case <-State.Exit:
			log.Warn().Msg("quitting NOW")
			exit = true
			break
			// не оставляйте default в бесконечном select {} - сожрет всё CPU
		default:
			screenMgr.CurrentScreen.Render()
			blt.Layer(0) //return to base layer
			blt.Refresh()
		}

	}
	log.Info().Msg("pre-shutdown sequence")
}

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

func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) {
	var exit = false
	var waitForWCspam = true
	for !exit {
		select {
		case keycode := <-State.RawInput:
			if keycode == blt.TK_NONE {
				continue
			}
			if keycode == blt.TK_CLOSE && !waitForWCspam {
				appctx.Logger().Warn().Msg("exiting on window close...")
				State.Exit <- struct{}{}
				appctx.Logger().Warn().Msg("...done")
				return
			}
			var pressed = ""
			var isModifier, _ = util.IntInSlice(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 "Ctrl+q":
					//fallthrough
					//case "Escape":
					appctx.Logger().Info().Msg("exiting on quit command...")
					State.Exit <- struct{}{}
					appctx.Logger().Info().Msg("...done")
					exit = true
					return
				default:
					if pressed != "" {
						waitForWCspam = false
						State.Input <- pressed
					}
				}
			}
		}
	}
}
