extends Camera2D
class_name GameCamera

@export var key: bool = true
@export var drag: bool = true
@export var edge: bool = false
@export var wheel: bool = true

@export var zoom_in_limit: float = 2.5 # The max vector2 zoom value

@export var camera_speed: int = 450 # Camera speed in px/s
@export var camera_margin: int = 50 # Pixels around edge of screen where the mouse will start moving the camera

# Initial zoom value taken from Editor.
var camera_zoom: Vector2 = zoom

const camera_zoom_speed = Vector2(0.5, 0.5)
var camera_movement: Vector2 = Vector2.ZERO
var _prev_mouse_pos: Vector2 = Vector2.ZERO

# INPUTS

# Right mouse button was or is pressed.
var __rmbk: bool = false
# Move camera by keys: left, top, right, bottom.
var __keys: Array[bool] = [false, false, false, false]

func _ready() -> void:
	set_drag_horizontal_enabled(false)
	set_drag_vertical_enabled(false)
	set_position_smoothing_enabled(true)
	set_position_smoothing_speed(4)

func _physics_process(delta: float) -> void:
	# Move camera by keys defined in InputMap (ui_left/top/right/bottom).
	if key:
		if __keys[0]:
			camera_movement.x -= camera_speed * delta
		if __keys[1]:
			camera_movement.y -= camera_speed * delta
		if __keys[2]:
			camera_movement.x += camera_speed * delta
		if __keys[3]:
			camera_movement.y += camera_speed * delta
	
	# Move camera by mouse, when it's on the margin (defined by camera_margin).
	if edge:
		var rec: Rect2 = get_viewport().get_visible_rect()
		var v: Vector2 = get_local_mouse_position() + rec.size / 2
		if rec.size.x - v.x <= camera_margin:
			camera_movement.x += camera_speed * delta
		if v.x <= camera_margin:
			camera_movement.x -= camera_speed * delta
		if rec.size.y - v.y <= camera_margin:
			camera_movement.y += camera_speed * delta
		if v.y <= camera_margin:
			camera_movement.y -= camera_speed * delta
	
	# When RMB is pressed, move camera by difference of mouse position
	if drag and __rmbk:
		camera_movement = _prev_mouse_pos - get_local_mouse_position()

	# If this movement would take us  out of clamp we don't want to do it:
	var position_change: Vector2 = camera_movement * get_zoom()

	if (position.x + position_change.x) < limit_left or \
		(position.x + position_change.x) > limit_right:
		camera_movement.x = 0

	if (position.y + position_change.y) < limit_top or \
		(position.y + position_change.y) > limit_bottom:
		camera_movement.y = 0
	
	if camera_movement.y != 0 or camera_movement.x != 0:
		# Update position of the camera.
		position += camera_movement * get_zoom()

	# Set camera movement to zero, update old mouse position.
	camera_movement = Vector2(0, 0)
	_prev_mouse_pos = get_local_mouse_position()

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton:
		var mouse_event: InputEventMouseButton = event as InputEventMouseButton
		if drag and \
		   mouse_event.button_index == MOUSE_BUTTON_RIGHT:
			# Control by right mouse button.
			if mouse_event.pressed: __rmbk = true
			else: __rmbk = false
		# Check if mouse wheel was used. Not handled by InputMap!
		if wheel:
			if mouse_event.button_index == MOUSE_BUTTON_WHEEL_DOWN and \
			camera_zoom.x - camera_zoom_speed.x > 0 and \
			camera_zoom.y - camera_zoom_speed.y > 0:
				camera_zoom -= camera_zoom_speed
				set_zoom(camera_zoom)
			if mouse_event.button_index == MOUSE_BUTTON_WHEEL_UP and \
			camera_zoom.x + camera_zoom_speed.x < zoom_in_limit and \
			camera_zoom.y + camera_zoom_speed.y < zoom_in_limit:
				camera_zoom += camera_zoom_speed
				set_zoom(camera_zoom)
	# Control by keyboard handled by InpuMap.
	if event.is_action_pressed("ui_left"):
		__keys[0] = true
	if event.is_action_pressed("ui_up"):
		__keys[1] = true
	if event.is_action_pressed("ui_right"):
		__keys[2] = true
	if event.is_action_pressed("ui_down"):
		__keys[3] = true
	if event.is_action_released("ui_left"):
		__keys[0] = false
	if event.is_action_released("ui_up"):
		__keys[1] = false
	if event.is_action_released("ui_right"):
		__keys[2] = false
	if event.is_action_released("ui_down"):
		__keys[3] = false
