from machine import Pin  # type: ignore
from neopixel import NeoPixel  # type: ignore
from ..utils import char_width

from .fonts.font_5x7 import font_5x7
from ..utils.colors import GRAY, RAINBOW, BLACK, WHITE
from ..utils.time_utils import (
	get_german_timestamp_short,
	get_datetime_string,
	get_german_time_ticks,
	get_german_date_ticks,	
)
import time
import math


class NeoPixel_64x64(NeoPixel):
	MATRIX_WIDTH = 64
	MATRIX_HEIGHT = 64

	def __init__(self, pin=28):
		"""
		Initialize LED Display

		Args:
		    width: Matrix width in pixels
		    height: Matrix height in pixels
		    pin: NeoPixel data pin
		"""
		super().__init__(Pin(pin), self.MATRIX_WIDTH * self.MATRIX_HEIGHT)

		# Font configuration
		self.set_font(font_5x7)

	def set_font(self, font):
		"""
		Set the current font for text rendering

		Args:
		    font: Font dictionary from fonts_array
		"""
		self.selected_font = font

		# Höhe des Fonts, Anzahl der Zeilen für jedes Zeichen
		# wir holen den ersten Wert des Fonts
		first_char = next(iter(self.selected_font))
		self.font_height = len(self.selected_font[first_char])
		print(f'Font set: width: per char; height: {self.font_height}')

	def set_pixel(self, x, y, color):
		"""
		Set a single pixel to the specified color

		Args:
		    x: X coordinate
		    y: Y coordinate
		    color: RGB color tuple
		"""
		index = y * self.MATRIX_WIDTH + x
		if 0 <= x < self.MATRIX_WIDTH and 0 <= y < self.MATRIX_HEIGHT:
			self[index] = color

	def clear(self):
		"""Clear the entire display (turn off all pixels)"""
		for i in range(len(self)):
			self[i] = BLACK
		self.write()

	def clear_row(self, row: int, effect: bool = False) -> None:
		"""löscht eine Zeile im Display entsprechend des eingestellten Fonts

		Args:
				row (int): in Pixel start bei 0 !!!
				effect (bool, optional): zeilenweise bzw. pixelweise löschen. Defaults to False.
		"""
		start = row * self.MATRIX_WIDTH
		ende = start + self.font_height * self.MATRIX_WIDTH - 1

		print(f'clear row: {row} --> pixels {start} to {ende}')
		for i in range(start, ende):
			self[i] = BLACK
			if effect and i % self.MATRIX_WIDTH == 0:
				self.write()

		self.write()

	def draw_letter(self, letter, x, y, color):
		"""
		Draw a single letter using current font with optimized width

		Args:
				letter: Character to draw
				x: X position
				y: Y position
				color: RGB color tuple
		"""

		if letter in self.selected_font:
			char_data = self.selected_font[letter]
			charwidth = char_width(char_data)

			# background for the letter (full font size)
			[
				# print(xpos, ypos)
				self.set_pixel(xpos, ypos, GRAY)
				for xpos in range(
					x, x + charwidth
				)  # 8 because full with of character representation
				for ypos in range(y, y + self.font_height)
			]

			for row in range(self.font_height):
				row_data = char_data[row]

				for col in range(charwidth):
					# Check if pixel should be lit (MSB first)
					# Only check bits within the actual character width
					if row_data & (1 << ((charwidth - 1) - col)):
						self.set_pixel(x + col, y + row, color)
		else:
			print(f'oops, letter does not exist in the font -> {letter}')

	def draw_text(self, text, x, y, color):
		"""
		Draw text with optimized character spacing

		Args:
				text: Text to draw
				x: Starting X position
				y: Starting Y position
				color: RGB color tuple
		"""
		current_x = x
		for char in text:
			self.draw_letter(char, current_x, y, color)
			# Move cursor by character width + 1 pixel spacing
			charwidth = char_width(self.selected_font[char])
			current_x += charwidth + 1

	def show_hello(self):
		"""Display HELLO with timestamp"""
		self.clear()

		# Draw HELLO in rainbow colors
		self.draw_text('HELLO!', 6, 4, RAINBOW[2])

		# Show timestamp
		datetimestr = get_german_timestamp_short()
		self.draw_text(datetimestr, 2, 15, RAINBOW[4])

		self.write()

	def vertical_floating_text(
		self, text, x, color=RAINBOW[0], float_range=3, speed=0.2, duration=10
	):
		"""
		Vertical floating text animation

		Args:
		    text: Text to display
		    x: X position (fixed)
		    color: Text color
		    float_range: How many pixels to float up/down
		    speed: Animation speed
		    duration: How long to run animation in seconds
		"""
		start_time = time.time()

		while time.time() - start_time < duration:
			# Calculate floating offset using sine wave
			offset = math.sin(time.time() * speed) * float_range
			current_y = int(self.MATRIX_HEIGHT // 2 + offset - (len(text) * self.font_height) // 2)

			self.clear()

			# Draw each letter vertically
			for i, char in enumerate(text):
				char_y = current_y + (i * self.font_height)
				# Keep text within matrix bounds
				if 0 <= char_y < self.MATRIX_HEIGHT - self.font_height:
					self.draw_letter(char, x, char_y, color)

			self.write()
			time.sleep(0.05)

	def horizontal_floating_text(
		self, text, y, color=RAINBOW[0], float_range=3, speed=0.2, duration=10
	):
		"""
		Horizontal floating text animation

		Args:
		    text: Text to display
		    y: Y position (fixed)
		    color: Text color
		    float_range: How many pixels to float left/right
		    speed: Animation speed
		    duration: How long to run animation in seconds
		"""
		start_time = time.time()
		counter = 0

		while time.time() - start_time < duration:
			# Calculate floating offset using sine wave
			offset = math.sin(counter) * float_range
			current_x = int(offset)  # to right

			self.clear()

			# Draw text at floating position
			for i, char in enumerate(text):
				charwidth = char_width(self.selected_font[char])
				char_x = current_x + (i * (charwidth + 1))
				# Keep text within matrix bounds
				if 0 <= char_x < self.MATRIX_WIDTH - charwidth:
					self.draw_letter(char, char_x, y, color)

			self.write()
			counter += speed
			time.sleep(0.05)

	def rotate_text_left_continuous(self, text, y, color=RAINBOW[0], speed=1.0, duration=10):
		"""
		Continuous left rotation - text wraps around seamlessly

		Args:
		    text: Text to display
		    y: Y position (fixed)
		    color: Text color
		    speed: Movement speed
		    duration: How long to run animation in seconds
		"""
		start_time = time.time()
		# hier müssen wir die Länge/Breite in Pixel des gesamten Textes berechnen
		# text_width_overall = len(text) * (self.font_width + 1)
		### TODO: ACHTUNG: noch zu testen !!!
		# Breite jedes Zeichens
		text_width_overall = sum([char_width(self.selected_font[char]) for char in text])

		position = 0

		while time.time() - start_time < duration:
			self.clear()

			# Draw text at current position
			for i, char in enumerate(text):
				charwidth = char_width(self.selected_font[char])
				char_x = int(position + (i * (charwidth + 1)))
				# Handle wrapping
				if char_x < -charwidth:
					char_x += self.MATRIX_WIDTH + text_width_overall
				if 0 <= char_x < self.MATRIX_WIDTH:
					self.draw_letter(char, char_x, y, color)

			self.write()

			# Move left and wrap around
			position -= speed
			if position < -text_width_overall:
				position = self.MATRIX_WIDTH

			time.sleep(0.05)

	def draw_rectangle(self, x, y, width, height, color, fill=False):
		"""
		Draw a rectangle

		Args:
		    x: Top-left X position
		    y: Top-left Y position
		    width: Rectangle width
		    height: Rectangle height
		    color: RGB color tuple
		    fill: Whether to fill the rectangle
		"""
		if fill:
			for i in range(height):
				for j in range(width):
					self.set_pixel(x + j, y + i, color)
		else:
			# Top and bottom edges
			for i in range(width):
				self.set_pixel(x + i, y, color)
				self.set_pixel(x + i, y + height - 1, color)
			# Left and right edges
			for i in range(height):
				self.set_pixel(x, y + i, color)
				self.set_pixel(x + width - 1, y + i, color)

	def draw_line(self, x1, y1, x2, y2, color):
		"""
		Draw a line using Bresenham's algorithm

		Args:
		    x1, y1: Start coordinates
		    x2, y2: End coordinates
		    color: RGB color tuple
		"""
		dx = abs(x2 - x1)
		dy = abs(y2 - y1)
		sx = 1 if x1 < x2 else -1
		sy = 1 if y1 < y2 else -1
		err = dx - dy

		while True:
			self.set_pixel(x1, y1, color)
			if x1 == x2 and y1 == y2:
				break
			e2 = 2 * err
			if e2 > -dy:
				err -= dy
				x1 += sx
			if e2 < dx:
				err += dx
				y1 += sy

	def write_text(self, text: str, xpos: int, ypos: int, color=WHITE) -> None:
		self.draw_text(text, xpos, ypos, color)  # Pixel setzen
		self.write()  # und anzeigen

	def screen_text(self, text: str):
		"""Text für einen Screen anpassen,
		Anzahl der Zeichen je Zeile begrenzen,
		ebenso wird die Höhe des Screen brücksichtigt


		Args:
				text (str): Text der ausgegeben werden soll
				font: Berechnungen für den Font
				height (int): Pixel
				width (int): Pixel
		"""

		def text_per_row(txt):
			pixs = 0
			visible_text = ''
			for a in txt:
				pixs += char_width(self.selected_font[a]) + 1
				if pixs > self.MATRIX_WIDTH:
					# Zeilenende erreicht
					break
				visible_text += a

			return visible_text

		# Ganzzahl Division
		max_visible_rows = self.MATRIX_HEIGHT // self.font_height
		print(f'rows_visible: {max_visible_rows}')

		text_left = text
		scn_txt = []
		for _ in range(max_visible_rows):
			visible_text = text_per_row(text_left)
			visible_text_len = len(visible_text)
			text_left = text_left[visible_text_len:]

			scn_txt.append(visible_text)

			if not text_left:
				break

		scr_txt_dict = {'visible': scn_txt, 'invisible': text_left}

		return scr_txt_dict



# Example usage
if __name__ == '__main__':
	# Create display instance
	display = NeoPixel_64x64()

	print('LED Matrix Display Initialized')
	print(get_datetime_string())
	print(f'German time: {get_german_time_ticks()}')
	print(f'German date: {get_german_date_ticks()}')

	# Demo various functions
	display.show_hello()
	time.sleep(3)

	display.vertical_floating_text('HELLO', 30, RAINBOW[0], 5, 0.15, 5)
	display.horizontal_floating_text('FLOAT', 28, RAINBOW[1], 10, 0.1, 5)
	display.rotate_text_left_continuous('ROTATE FLOAT', 28, speed=8.0, duration=5)

	# Draw some shapes
	display.clear()
	display.draw_rectangle(5, 5, 10, 10, RAINBOW[0])
	display.draw_rectangle(20, 20, 15, 8, RAINBOW[2], fill=True)
	display.draw_line(0, 0, 63, 63, RAINBOW[4])
	display.write()
