extends VBoxContainer

const TimeSlot = preload("res://scenes/board/time_slot.tscn")
const ReservationScene = preload("res://scenes/board/reservation.tscn")

const WorkingDayStart = 8
const WorkingDayEnd = 20

const MinimalMinutesToShowTitle = 30
const MinimalMinutesForBigSize = 60

const DateUpdateInterval := 60.0
const ScheduleUpdateInterval := 1.0 # 5.0

@onready var _main: Main = get_tree().get_current_scene()
@onready var _timeline = $Panel/Timeline
@onready var _reservations = $Panel/Reservations
@onready var _date = $TopBar/DateButton
@onready var _room = $TopBar/RoomButton

@onready var _rooms_menu = $RoomsMenu

var _date_update_timer := 0.0
var _schedule_update_timer := 0.0
var _blocked_after_loading := false
var _block_reservation_creation := false

var _rooms : Array[RoomEntity] = []
var _reservations_list : Array[ReservationEntity] = []

func _process(delta):
	_process_hour_size()
	_process_date(delta)
	_process_fonts()
	_process_schedule(delta)

func _process_hour_size():
	var hour_size = get_viewport_rect().size.y/15

	for time_slot in _timeline.get_children():
		time_slot.set_height(hour_size)

func _process_date(delta):
	_date_update_timer += delta
	if _date_update_timer >= DateUpdateInterval:
		update_date()
		_date_update_timer = 0.0

func update_date():
	_date.text = _main.get_selected_date()

func _process_fonts():
	_process_font_size(_date, 35)
	_process_font_size(_room, 35)

func _process_font_size(obj, k):
	var font_size = obj.get_theme_default_font_size()
	var new_font_size = get_viewport_rect().size.y/k
	if font_size != new_font_size:
		obj.add_theme_font_size_override("font_size", new_font_size)

func _process_schedule(delta):
	if _schedule_update_timer >= ScheduleUpdateInterval:
		_schedule_update_timer = 0.0
		_update_schedule()

	_schedule_update_timer += delta

func _ready():
	_remove_reservations()
	_initialize()

func _initialize():
	_connect_signals()

	_remove_time_slots()
	_fill_with_slots()

	await get_tree().create_timer(0.1, false).timeout
	_load_rooms()

	update_date()

func _connect_signals():
	await _main.ready
	var event_handler = _main.get_event_handler()
	event_handler.reservations_updated.connect(_on_reservations_updated)

	_room.pressed.connect(_on_room_button_pressed)
	_rooms_menu.item_clicked.connect(_on_room_selected)

	_date.pressed.connect(_on_date_button_pressed)

func _remove_time_slots():
	for time_slot in _timeline.get_children():
		time_slot.queue_free()

func _fill_with_slots():
	for hour in range(WorkingDayStart, WorkingDayEnd):
		var slot = TimeSlot.instantiate()
		_timeline.add_child(slot)
		slot.set_time(hour)

func _remove_reservations():
	for reservation in _reservations.get_children():
		reservation.queue_free()

func _load_rooms():
	var room_repo = _main.get_room_repo()
	if room_repo.get_selected_room():
		return
		
	_rooms_menu.clear()

	var rooms = await room_repo.list_rooms()
	for room in rooms:
		_rooms.append(room)
		_rooms_menu.add_item(room.title)

	if len(rooms) < 0:
		print('Not enough rooms')

	_rooms_menu.select(0)
	_on_room_selected(0, '')

func _update_schedule(reservations=null):
	if not _main.is_node_ready():
		await _main.ready

	var reservation_repo = _main.get_reservation_repo()

	if reservations == null:
		var room = _main.get_selected_room()
		reservations = await reservation_repo.list_reservations({
			"date": _main.get_selected_date(),
			"room_id": room.id if room else ''
		})

	if reservations_are_updated(reservations):
		return

	_reservations_list = reservations
	_remove_reservations()

	for reservation in reservations:
		var start_time_hours = reservation.start_time.hours
		var start_time_minutes = reservation.start_time.minutes
		var start_time = (start_time_hours - WorkingDayStart)*60 + start_time_minutes

		var finish_time_hours = reservation.finish_time.hours
		var finish_time_minutes = reservation.finish_time.minutes
		var finish_time = (finish_time_hours - WorkingDayStart)*60 + finish_time_minutes

		var reservation_time = finish_time - start_time

		_compose_reservation(start_time, reservation_time, \
			reservation.title, reservation.color, reservation.id)

func reservations_are_updated(new_reservations: Array[ReservationEntity]) -> bool:
	if _reservations_list.size() == 0:
		return false

	if _reservations_list.size() != new_reservations.size():
		return false

	for i in range(_reservations_list.size()):
		var entity_a = _reservations_list[i]
		var entity_b = new_reservations[i]
		
		if !(entity_a is ReservationEntity) || !(entity_b is ReservationEntity):
			return false
		
		if !entity_a.is_identical(entity_b):
			return false

	return true

func _compose_reservation(start_time, duration_time, title, color, id):
	if duration_time < MinimalMinutesToShowTitle:
		title = ""

	var reservation = ReservationScene.instantiate()
	_reservations.add_child(reservation)

	reservation.set_start_time(start_time)
	reservation.set_duration_time(duration_time)
	reservation.set_title(title)
	reservation.set_color(color)
	reservation.set_id(id)

	if duration_time < MinimalMinutesForBigSize:
		reservation.set_font(reservation.Fonts.MEDIUM)

func _input(event):
	if _blocked_after_loading or not _main.get_current_page() == Main.Pages.Board:
		return

	if event is InputEventScreenTouch or (event is InputEventMouseButton and event.pressed):
		if _clicked_on_timeline(event.position):
			await get_tree().create_timer(0.01, false).timeout

			if _block_reservation_creation:
				_block_reservation_creation = false
				return

			if _main.get_current_page() == Main.Pages.Board:
				var time = _get_time_by_position(event.position)

				if _time_is_before_current_time(time) and _selected_date_is_current():
					return

				var creation_page = _main.load_page(Main.Pages.ReservationCreation)
				creation_page.clean()
				creation_page.update()
				creation_page.set_start_time(time)

func _get_time_by_position(position):
	var hour_size = get_viewport_rect().size.y / 15
	var hours_with_minutes_in_float = \
		(position.y - _timeline.global_position.y) / hour_size + WorkingDayStart
	var hours = floor(hours_with_minutes_in_float)
	var minutes = (hours_with_minutes_in_float - hours) * 60
	var minutes_normalized = floor(minutes/30)*30

	return {"hours": hours, "minutes": minutes_normalized}

func _clicked_on_timeline(pos: Vector2):
	var timeline_position = _timeline.global_position
	return (
			pos.x > timeline_position.x and
			pos.x < _timeline.size.x + timeline_position.x
		) and (
			pos.y > timeline_position.y and
			pos.y < _timeline.size.y + timeline_position.y
		)

func _selected_date_is_current():
	return _main.get_selected_date() == _main.get_current_date()

func _time_is_before_current_time(time):
	var current_time = Time.get_time_dict_from_system()
	var time_in_minutes = time.hours*60 + time.minutes
	var current_time_in_minutes = current_time.hour*60 + current_time.minute

	return time_in_minutes < current_time_in_minutes

func _block_board_click_for(seconds: float):
	_blocked_after_loading = true
	await get_tree().create_timer(seconds, false).timeout
	_blocked_after_loading = false

func _on_room_button_pressed():
	if _rooms_menu.visible:
		_rooms_menu.hide()
	else:
		_rooms_menu.show()

func _on_room_selected(idx, text):
	var room_repo = _main.get_room_repo()
	room_repo.set_selected_room(_rooms[idx])

	_block_reservation_creation = true
	_room.text = _rooms[idx].title
	_rooms_menu.hide()

	update()

func _on_date_button_pressed():
	_main.start_date_selection()
	_main.load_page(Main.Pages.CalendarSetting)

func _on_reservations_updated(reservations):
	await get_tree().create_timer(0.1, false).timeout
	update()

func update():
	_block_board_click_for(0.25)
	_initialize()
	_update_schedule()