__version__ = "v1.1.0"

###########
# IMPORTS #
###########

# Board stuff
import board  # type: ignore
import time
import os

# OLED stuff
import busio  # type: ignore
import displayio  # type: ignore
import adafruit_displayio_ssd1306
import terminalio
from adafruit_display_text import label

#########
# SETUP #
#########

# Constants
VELOCITY = 127
WIDTH = 128
HEIGHT = 32
BORDER = 1

# Environment
MIDI_CHANNEL = int(os.getenv("MIDI_CHANNEL", "0"))
j = os.getenv("MIDI_KEYMAPS", "")
MIDI_KEYMAPS = json.loads(j) if j else [{
	0: 60,  # C4
	1: 61,  # C#4
	2: 62,  # D4
	3: 63,  # D#4
	4: 64,  # E4
	5: 65,  # F4
	6: 66,  # F#4
	7: 67,  # G4,
}]

# OLED setup
OLED_SDA = board.GP0
OLED_SCL = board.GP1
i2c = busio.I2C(OLED_SCL, OLED_SDA)
displayio.release_displays()
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)

# Make the display context
splash = displayio.Group()
display.root_group = splash

color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0xFFFFFF  # White

# bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
# splash.append(bg_sprite)

# # Draw a smaller inner rectangle
# inner_bitmap = displayio.Bitmap(WIDTH - BORDER * 2, HEIGHT - BORDER * 2, 1)
# inner_palette = displayio.Palette(1)
# inner_palette[0] = 0x000000  # Black
# inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=BORDER, y=BORDER)
# splash.append(inner_sprite)

class MidiKeyboard():
	def __init__(
			self,
			screen: displayio.Group,
			midi_note_maps: list[dict]
		):
		"""Initialize the MidiKeyboard class

		Args:
			screen (displayio.Group): Group (splash/screen) to write to.
		"""
		self.midi_note_maps = midi_note_maps

		self.layer = 0
		self.locked = False
		self.active_midi_notes = list()
		self.screen = screen

		self.currently_playing = label.Label(
			terminalio.FONT,
			text = "",
			color = 0xFFFFFF,
			x = 3,
			y = HEIGHT // 2 - 1
		)
		self.screen.append(self.currently_playing)

		self.locked_label = label.Label(
			terminalio.FONT,
			text = "",
			color = 0xFFFFFF,
			x = 35,
			y = (HEIGHT // 3) + 4
		)
		self.screen.append(self.locked_label)

		self.layer_label = label.Label(
			terminalio.FONT,
			text = str(self.layer),
			color = 0xFFFFFF,
			x = 35,
			y = (HEIGHT // 3) * 2 + 4
		)
		self.screen.append(self.layer_label)

		self.sw_cc_label = label.Label(
			terminalio.FONT,
			text = "CC1 ",
			color = 0xFFFFFF,
			x = 72,
			y = (HEIGHT // 3) * 1 + 4
		)
		self.screen.append(self.sw_cc_label)

		self.enc_cc_label = label.Label(
			terminalio.FONT,
			text = "CC2 ",
			color = 0xFFFFFF,
			x = 72,
			y = (HEIGHT // 3) * 2 + 4
		)
		self.screen.append(self.enc_cc_label)

		self.update_oled()

	def update_oled(self) -> None:
		self.currently_playing.text = self._get_note_name(self.active_midi_notes[-1]) if len(self.active_midi_notes) > 0 else ""
		self.layer_label.text = str(self.layer)
		self.locked_label.text = "X" if self.locked else "O"

	def _get_note_name(self, note_number: int) -> str:
		"""Convert a MIDI note number to its corresponding note name.
		This helper function was generated by Gemini.

		Args:
			note_number (int): Note number to be converted

		Returns:
			str: formatted note name
		"""
		note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
		octave = (note_number // 12) - 1  # MIDI note 0 is C-1, so C4 is octave 4
		note_in_octave = note_number % 12
		return f"{note_names[note_in_octave]}{octave}"


def outline_screen(screen: displayio.Group) -> None:
	"""Draw a white outline around the screen with a black interior.

	Drawing a white border around the screen helps the display stand out on the
	Macropad. The black interior provides a clean background for the controls and
	text to be displayed on.

	Args:
		screen (displayio.Group): Group (splash/screen) to write to.
	"""

	color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
	color_palette = displayio.Palette(1)
	color_palette[0] = 0xFFFFFF

	bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
	screen.append(bg_sprite)

	inner_bitmap = displayio.Bitmap(WIDTH - BORDER * 2, HEIGHT - BORDER * 2, 1)
	inner_palette = displayio.Palette(1)
	inner_palette[0] = 0x000000
	inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=BORDER, y=BORDER)
	screen.append(inner_sprite)


def splash_screen(screen: displayio.Group, text: str) -> None:
	"""Display a splash screen with a given text.
	Animate the text in from left to right, then out again, for a typewriter effect.

	Args:
		screen (displayio.Group): Group (splash/screen) to write to.
		text (str): The text to display on the splash screen.
	"""
	text_area = label.Label(terminalio.FONT, text="", color=0xFFFFFF, x=32, y=HEIGHT // 2 - 1)
	screen.append(text_area)

	for i in range(len(text) + 1):
		text_area.text = text[0:i]
		time.sleep(0.25)

	time.sleep(2)

	for i in range(len(text) + 1):
		text_area.text = " " * i + text[i:len(text)]
		time.sleep(0.25)

	screen.remove(text_area)



# m = MidiKeyboard(screen=splash, midi_note_maps=MIDI_KEYMAPS)
keyboard = MidiKeyboard(
	screen=splash,
	row_pins=ROW_PINS,
	midi_note_maps=MIDI_KEYMAPS,
	cc_1=64,
	cc_2=7,
	cc_1_default=0,
	cc_2_default=127,
)

if __name__ == '__main__':
	# outline_screen(screen=splash)
	# splash_screen(screen=splash, text=f"HackPiano {__version__[0:2]}")
	# time.sleep(3)
	m.layer = 1
	m.active_midi_notes.append(64)
	# m.sustain = True
	m.update_oled()
	time.sleep(2)
	m.layer = -12
	m.active_midi_notes.append(3)
	# m.sustain = False
	m.update_oled()
	time.sleep(2)
	m.layer = 71
	m.active_midi_notes.remove(64)
	# m.sustain = True
	m.update_oled()
	time.sleep(1)
	m.active_midi_notes.remove(3)
	m.update_oled()

while True:
    pass
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT