package doodle

import (
	"time"

	"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
	"git.kirsle.net/SketchyMaze/doodle/pkg/log"
	"git.kirsle.net/SketchyMaze/doodle/pkg/native"
	"git.kirsle.net/SketchyMaze/doodle/pkg/usercfg"
	"git.kirsle.net/go/render"
	"git.kirsle.net/go/render/event"
	"git.kirsle.net/go/ui"
)

/*
Touchscreen Control functionality used in the Play Scene.
*/

// LoopTouchable is called as part of PlayScene.Loop while the simulation is running.
//
// It looks for touch events on proportional regions of the window and emulates key
// input bindings to move the character, jump, etc.
//
// TODO: this function manipulates the event.State to set Up, Down, Left, Right and
// Space keys and may need love for reconfigurable keybinds later.
func (s *PlayScene) LoopTouchable(ev *event.State) {
	var (
		middle = s.touchGetMiddleBox()
		cursor = render.NewPoint(ev.CursorX, ev.CursorY)
	)

	// Don't do any of this if the mouse is over the menu bar, so
	// clicking on the menus doesn't make the character move or jump.
	if cursor.Inside(s.menubar.Rect()) || s.Supervisor.GetModal() != nil ||
		s.Supervisor.IsPointInWindow(cursor) {
		return
	}

	// Detect if the player is idle.
	// Idle means that they are not holding any directional or otherwise input key.
	// Keyboard inputs and touch events from this function will set these keys.
	// See if it stays unset long enough to consider idle.
	var isGrounded = (s.Player.HasGravity() && s.Player.Grounded()) || !s.Player.HasGravity()
	if !ev.Up && !ev.Down && !ev.Right && !ev.Left && !ev.Space && isGrounded {
		if s.idleLastStart.IsZero() {
			s.idleLastStart = time.Now()
		} else if time.Since(s.idleLastStart) > balance.PlayModeIdleTimeout {
			if !s.playerIsIdle {
				log.Debug("LoopTouchable: No keys pressed in a while, idle UI start")
			}
			s.playerIsIdle = true

			// Fade in the hint UI by stepping up the alpha value.
			if s.idleHelpAlpha < balance.PlayModeAlphaMax {
				s.idleHelpAlpha += balance.PlayModeAlphaStep
			}

			// cap it from overflow
			if s.idleHelpAlpha > balance.PlayModeAlphaMax {
				s.idleHelpAlpha = balance.PlayModeAlphaMax
			}
		}
	} else {
		s.idleLastStart = time.Time{}
		s.playerIsIdle = false
		s.idleHelpAlpha = 0
	}

	// Click (touch) event?
	if ev.Button1 {
		// Clicked left or right of middle = move left or right.
		// By default the middle box is a dead zone, but if player
		// is already moving laterally allow for quick precision.
		if ev.Left || ev.Right {
			if cursor.X < s.d.width/2 {
				ev.Left = true
				ev.Right = false
			} else if cursor.X > s.d.width/2 {
				ev.Right = true
				ev.Left = false
			}
		} else {
			if cursor.X < middle.X {
				ev.Left = true
				ev.Right = false
			} else if cursor.X > middle.X+middle.W {
				ev.Left = false
				ev.Right = true
			}
		}

		// Clicked above middle = jump.
		ev.Up = cursor.Y < middle.Y

		// Clicked below middle = down (antigravity)
		ev.Down = cursor.Y > middle.Y+middle.H

		// Clicked on the middle box = Use.
		if cursor.X >= middle.X && cursor.X <= middle.X+middle.W &&
			cursor.Y >= middle.Y && cursor.Y <= middle.Y+middle.H {
			ev.Space = true

			// Also cancel any lateral movement.
			ev.Left = false
			ev.Right = false
		}

		s.isTouching = true
	} else {
		if s.isTouching {
			ev.Left = false
			ev.Right = false
			ev.Up = false
			ev.Down = false
			ev.Space = false
			s.isTouching = false
		}
	}
}

// DrawTouchable draws any UI elements if needed for the touch UI.
func (s *PlayScene) DrawTouchable() {
	// If we are not in touch mode (user has not touched their screen at all), don't
	// show the hints - mouse & keyboard mode. And user opt-out setting.
	if !native.IsTouchScreenMode() || usercfg.Current.HideTouchHints {
		return
	}

	var (
		middle     = s.touchGetMiddleBox()
		background = render.RGBA(200, 200, 200, uint8(s.idleHelpAlpha))
		font       = balance.TouchHintsFont
	)
	font.Color.Alpha = uint8(s.idleHelpAlpha)
	font.Shadow.Alpha = uint8(s.idleHelpAlpha)

	// If the player is idle for a while, start showing them a hint UI about
	// the touch screen controls.
	if s.playerIsIdle {
		// Draw the "Use" button over the middle box.
		useBtn := ui.NewLabel(ui.Label{
			Text: "Touch here\nto 'use'\nobjects",
			Font: font,
		})
		useBtn.SetBackground(background)
		useBtn.Resize(middle)
		useBtn.Compute(s.d.Engine)
		useBtn.Present(s.d.Engine, middle.Point())

		// Move Left and Move Right hints.
		moveLeft := ui.NewLabel(ui.Label{
			Text: "Touch here to\nmove left",
			Font: font,
		})
		moveLeft.SetBackground(background)
		moveLeft.Compute(s.d.Engine)
		moveLeft.Present(s.d.Engine, render.Point{
			X: (middle.X / 2) - (moveLeft.Size().W / 2),
			Y: (s.d.height / 2) - (moveLeft.Size().H / 2),
		})

		// Move Left and Move Right hints.
		moveRight := ui.NewLabel(ui.Label{
			Text: "Touch here to\nmove right",
			Font: font,
		})
		moveRight.SetBackground(background)
		moveRight.Compute(s.d.Engine)
		moveRight.Present(s.d.Engine, render.Point{
			X: (middle.X+middle.W+s.d.width)/2 - (moveRight.Size().W / 2),
			Y: (s.d.height / 2) - (moveRight.Size().H / 2),
		})

		// Jump hints.
		moveUp := ui.NewLabel(ui.Label{
			Text: "Touch anywhere above the middle of\nthe screen to jump up in the air",
			Font: font,
		})
		moveUp.SetBackground(background)
		moveUp.Compute(s.d.Engine)
		moveUp.Present(s.d.Engine, render.Point{
			X: (s.d.width / 2) - (moveUp.Size().W / 2),
			Y: (middle.Y / 2) - (moveUp.Size().H / 2),
		})

		// Keybind hints.
		keyHints := ui.NewLabel(ui.Label{
			Text: "Keyboard controls:\n" +
				"WASD or arrow keys for movement\n" +
				"Space key to 'use' objects.",
			Font: font,
		})
		keyHints.SetBackground(background)
		keyHints.Compute(s.d.Engine)
		keyHints.Present(s.d.Engine, render.Point{
			X: (s.d.width / 2) - (keyHints.Size().W / 2),
			Y: (middle.Y+middle.H+s.d.height)/2 - (keyHints.Size().H / 2),
		})
	}
}

// Get the middle box of the screen and return it.
// X,Y are screen positions and W,H is the box size.
func (s *PlayScene) touchGetMiddleBox() render.Rect {
	// Carve up the screen segments.
	var (
		// The application window dimensions.
		width  = s.d.width
		height = s.d.height

		// The middle box.
		middleMinSize = 96 // minimum dimensions
		middle        = render.Rect{
			X: (width / 2) - (middleMinSize / 2),
			Y: (height / 2) - (middleMinSize / 2),
			W: middleMinSize,
			H: middleMinSize,
		}
	)
	return middle
}
