package engine

import (
	"errors"
	"fmt"
	"log"

	"github.com/gdamore/tcell/v2"
	"github.com/gdamore/tcell/v2/views"
	"github.com/google/uuid"
)

const (
	TERMINAL_SIZE_WIDTH      int         = 80
	TERMINAL_SIZE_HEIGHT     int         = 24
	DEFAULT_STYLE_BACKGROUND tcell.Color = tcell.ColorReset
	DEFAULT_STYLE_FOREGROUND tcell.Color = tcell.ColorReset
)

type Drawable interface {
	UniqueId() uuid.UUID
	Draw(v views.View)
}

func Multidraw(drawables ...Drawable) []Drawable {
	arr := make([]Drawable, 0)

	if drawables == nil {
		return arr
	}

	for _, d := range drawables {
		if d == nil {
			continue
		}

		arr = append(arr, d)
	}

	return arr
}

type RenderContext struct {
	screen tcell.Screen
	view   *views.ViewPort

	events    chan tcell.Event
	quit      chan struct{}
	drawables chan Drawable
}

func CreateRenderContext() (*RenderContext, error) {
	screen, sErr := tcell.NewScreen()

	if sErr != nil {
		log.Fatalf("%~v", sErr)
	}

	stopScreen := func() {
		screen.Fini()
	}

	if err := screen.Init(); err != nil {
		stopScreen()
		log.Fatal(err)
		return nil, err
	}

	width, height := screen.Size()

	if width < TERMINAL_SIZE_WIDTH || height < TERMINAL_SIZE_HEIGHT {
		stopScreen()
		log.Fatal("Unable to start; Terminal must be at least 80x24")
		return nil, errors.New("Terminal is undersized; must be at least 80x24")
	}

	view := views.NewViewPort(
		screen,
		(width/2)-(TERMINAL_SIZE_WIDTH/2),
		(height/2)-(TERMINAL_SIZE_HEIGHT/2),
		TERMINAL_SIZE_WIDTH,
		TERMINAL_SIZE_HEIGHT,
	)

	events := make(chan tcell.Event)
	quit := make(chan struct{})

	go screen.ChannelEvents(events, quit)

	context := new(RenderContext)

	context.screen = screen
	context.events = events
	context.quit = quit
	context.view = view
	context.drawables = make(chan Drawable)

	return context, nil
}

func (c *RenderContext) Stop() {
	c.screen.Fini()
}

func (c *RenderContext) CollectInputEvents() []*tcell.EventKey {
	events := make([]tcell.Event, len(c.events))

	select {
	case e := <-c.events:
		events = append(events, e)
	default:
	}

	inputEvents := make([]*tcell.EventKey, 0, len(events))

	for _, e := range events {
		switch ev := e.(type) {
		case *tcell.EventKey:
			inputEvents = append(inputEvents, ev)
		case *tcell.EventResize:
			c.onResize(ev)
		}
	}

	return inputEvents
}

func (c *RenderContext) DrawableQueue() chan Drawable {
	return c.drawables
}

func (c *RenderContext) onResize(ev *tcell.EventResize) {
	width, height := ev.Size()

	c.screen.Clear()

	c.view.Resize(
		(width/2)-(TERMINAL_SIZE_WIDTH/2),
		(height/2)-(TERMINAL_SIZE_HEIGHT/2),
		TERMINAL_SIZE_WIDTH,
		TERMINAL_SIZE_HEIGHT,
	)

	c.screen.Sync()
}

func (c *RenderContext) Draw(deltaTime int64, drawables []Drawable) {
	fps := 1_000_000 / deltaTime

	c.view.Clear()

	msPerFrame := float32(fps) / 1000.0

	fpsText := CreateText(0, 0, 16, 1, fmt.Sprintf("%vms", msPerFrame), tcell.StyleDefault)

	for _, d := range drawables {
		d.Draw(c.view)
	}

	fpsText.Draw(c.view)

	c.screen.Show()
}
