from ableton.v2.control_surface import Component
from ableton.v2.control_surface.components import BackgroundComponent
from ableton.v2.control_surface.control import control_list, ButtonControl, RadioButtonControl

def exquis_folder(browser):
	"Return the BrowserItem representing the Exquis folder in the User Library, or None if it does not exist"

	library = browser.user_library
	for child in library.iter_children:
		if child.is_folder and child.name and child.name.lower() == "exquis":
			return child
	return None

def number_prefix(str):
	"Return the number at the beginning of the string, or -1 if there is none"

	i = 0
	size = len(str)
	while i < size and str[i].isdigit():
		i += 1
	if i == 0:
		return -1
	
	return int(str[:i])

AIRPLANE_ROW_OFFSET = [0, 6, 11, 17, 22, 28, 33, 39, 44, 50, 55, 61]
def airplane_seat_prefix(str):
	"Return the row and column corresponding to the airplane seat prefix (e.g. '11C' => (11, 2)), or (-1, -1) if it is invalid"
	
	i = 0
	size = len(str)
	while i < size and str[i].isdigit():
		i += 1
	row = int(str[:i]) - 1 if i > 0 else -1
	if i < size and str[i] in "abcdef":
		col = ord(str[i]) - ord('a')
		return (row, col)
	return (row, -1)

def preset_index(p, presets, page=-1):
	"Return the index of the given preset in the airplane seat layout, or -1 if it is not in the layout"

	if page >= 0:
		begin = page * 61
		end = (page + 1) * 61
	else:
		begin = 0
		end = 6 * 61
	
	row, col = airplane_seat_prefix(p.name.lower())
	index = -1
	if row >= 0 and row <= 11:
		begin += AIRPLANE_ROW_OFFSET[row]
		if col >= 0 and col < 6:
			index = begin + col
			if index not in presets:
				return begin + col
	
	for i in range(begin, end):
		if i not in presets:
			index = i
			break
	
	return index

def exquis_presets(browser, fn):
	"Return a dictionary of presets in the Exquis folder of the User Library, indexed by their index in the airplane seat layout, with 61 presets per page"

	base = exquis_folder(browser)
	if not base:
		return {}
	
	presets = {}
	for child in base.iter_children:
		if child.is_folder:
			page = number_prefix(child.name) - 1
			if page < 0:
				page = -1
			if page >= 6:
				page = -1
			
			for child2 in child.iter_children:
				if not child2.is_loadable:
					continue
				index = preset_index(child2, presets, page)
				if index >= 0:
					presets[index] = fn(child2)
		elif child.is_loadable:
			index = preset_index(child, presets)
			if index >= 0:
				presets[index] = fn(child)
	
	return presets

def index_to_locator(index: int):
	"Return the locator corresponding to the given index in the airplane seat layout, or None if it is invalid"

	if index < 0 or index >= 61:
		return None
	
	row = 0
	while index >= AIRPLANE_ROW_OFFSET[row]:
		row += 1
	row -= 1
	
	col = index - AIRPLANE_ROW_OFFSET[row]
	
	number = str(row + 1)
	letter = chr(ord('A') + col)
	return number + letter

class PresetsModeComponent(Component):
	"A component that handles the Presets mode of the Exquis (activated when holding the Sounds button), and that allows to load presets from the Exquis folder of the User Library"

	load_buttons = control_list(ButtonControl, 61)
	page_buttons = control_list(RadioButtonControl)

	def set_up_button(self, _):
		self.background.set_up_button(_)
	def set_down_button(self, _):
		self.background.set_down_button(_)
	def set_left_button(self, _):
		self.background.set_left_button(_)
	def set_right_button(self, _):
		self.background.set_right_button(_)

	def __init__(self, *a, **k):
		super(PresetsModeComponent, self).__init__(*a, **k)
		self.background = BackgroundComponent()
		self._last_enabled = None
		self._presets = {}
		self.page = 0
		self.page_count = 0
		self.should_switch_to_notes_mode_on_release = False

	def update(self):
		super(PresetsModeComponent, self).update()
		enabled = self.is_enabled()

		if enabled == self._last_enabled:
			return
		self._last_enabled = self.is_enabled()

		if enabled:
			self.rescan_presets()

	def rescan_presets(self):
		browser = self.application.browser
		self._presets = exquis_presets(browser, lambda p: p.uri)
		last_preset_index = max(self._presets.keys(), default=-1)
		if last_preset_index >= 0:
			self.page_count = last_preset_index // 61 + 1
		else:
			self.page_count = 1
		if self.page_buttons.control_count != self.page_count:
			self.page_buttons.control_count = self.page_count
			if self.page >= self.page_count:
				self.page = self.page_count - 1
			self.page_buttons.checked_index = self.page
		self._update_load_buttons()
	
	def _update_load_buttons(self):
		# Update the button colors accordingly
		for i, button in enumerate(self.load_buttons):
			button.pressed_color = "ItemNavigation.ItemSelected"
			if (self.page * 61 + i) in self._presets:
				button.color = "ItemNavigation.ItemNotSelected"
			else:
				button.color = "ItemNavigation.NoItem"

	@page_buttons.checked
	def on_page_button(self, _):
		self.page = _.index
		if self.page >= self.page_count:
			self.page = self.page_count - 1
		self._update_load_buttons()

	@load_buttons.pressed
	def on_load_button_pressed(self, button):
		self.should_switch_to_notes_mode_on_release = False

	@load_buttons.released_immediately
	def on_load_button_released_immediately(self, button):
		index = self.page * 61 + button.index
		if index not in self._presets:
			locator = index_to_locator(button.index)
			self.canonical_parent.show_message(f"No preset whose name starts with \"{locator}\" in the Exquis folder of the User Library")
			return
		
		# Find the corresponding preset and load it
		browser = self.application.browser
		presets = exquis_presets(browser, lambda p: p)
		preset = next((p for i, p in presets.items() if p.uri == self._presets[index]), None)
		if preset:
			self.song.begin_undo_step()
			browser.load_item(preset)
			self.song.end_undo_step()
			self.canonical_parent.switch_to_notes_mode()
	
	@load_buttons.pressed_delayed
	def on_load_button_pressed_delayed(self, button):
		index = self.page * 61 + button.index
		if index not in self._presets:
			locator = index_to_locator(button.index)
			self.canonical_parent.show_message(f"No preset whose name starts with \"{locator}\" in the Exquis folder of the User Library")
			return

		# Find the corresponding preset and load it in a new track
		browser = self.application.browser
		presets = exquis_presets(browser, lambda p: p)
		preset = next((p for i, p in presets.items() if p.uri == self._presets[index]), None)
		if preset:
			self.song.begin_undo_step()
			track = self.song.create_midi_track(-1)
			if not track:
				self.song.end_undo_step()
				return
			self.song.view.selected_track = track
			browser.load_item(preset)
			track.view.select_instrument()
			self.song.end_undo_step()
			self.should_switch_to_notes_mode_on_release = True
	
	@load_buttons.released_delayed
	def on_load_button_released_delayed(self, button):
		if self.should_switch_to_notes_mode_on_release:
			self.canonical_parent.switch_to_notes_mode()
		self.should_switch_to_notes_mode_on_release = False
			
