from machine import I2C, Pin
import time
from VL6180X import VL6180X
from ssd1306 import SSD1306_I2C
import writer
import freesans20
import freesans30
from pixel_effects import PIXEL_EFFECTS
import framebuf


# Globale Variable für den Sensor
sensor = None

# Globale Variable für den Interrupt-Handler Status
interrupt_enabled = True
MAX_MEASUREMENTS = 100
measurement_times = []

# Initialisiere den Berührungssensor mit Interrupt
touch_sensor_start = Pin(28, mode=Pin.IN, pull=Pin.PULL_DOWN)
# Initialisiere den Pin für den Taster
Pin_drink = machine.Pin(18, machine.Pin.IN, machine.Pin.PULL_DOWN)

# Pixel Ring definitions
ring_pixel_sizes = [40, 32, 24, 16, 12, 8, 1]  # Beispielgrößen
num_pixels = sum(ring_pixel_sizes)
pin=27
brightness=50 #percent


# Initialisiere I2C for Display
i2c = I2C(0, sda=Pin(4), scl=Pin(5))  # Passen Sie die Pin-Nummern an, wenn nötig

# Display pixels
WIDTH = 128
HEIGHT = 64

# Definition der Bitmaps (8x8)
average_icon = bytearray([
    0b00000001,
    0b00111010,
    0b01000100,
    0b10001010,
    0b10010010,
    0b10100100,
    0b01111000,
    0b10000000
])

flag_icon = bytearray([
0b10101010,  # █░█░█░█░
0b01010101,  # ░█░█░█░█
0b10101010,  # █░█░█░█░
0b01010101,  # ░█░█░█░█
0b10101010,  # █░█░█░█░
0b01010101,  # ░█░█░█░█
0b10101010,  # █░█░█░█░
0b01010101   # ░█░█░█░█
])

# define colors
red = (255, 0, 0)
orange = (255, 50, 0)
yellow = (255, 100, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
indigo = (255, 0, 200)
violet = (200, 0, 100)


# Variablen für die Zeitmessung
start_time = None
stop_time = None
measuring = False


def measure_time():
    if Pin_drink.value()==0:
        print('Frühstart')
        pe.strobe(red,4,3)
        return None

    while Pin_drink.value() == 1:
        time.sleep(0.001)
    start_time = time.ticks_ms()  # Startzeit festlegen
    time.sleep(0.1) # gegen prellen
    while Pin_drink.value() == 0:
        time.sleep(0.001)
    elapsed_time = time.ticks_ms() - start_time


    return elapsed_time
        

def measure_time_below_threshold(sensor, threshold=29):
    """
    Misst die Zeit, während der Sensorwert über dem Schwellenwert liegt und gibt die Zeit in Millisekunden aus, wenn der Wert wieder darunter liegt.

    :param sensor: Sensor-Objekt, das die range()-Methode bereitstellt
    :param threshold: Schwellenwert für den Sensorwert
    :return: Zeit in Millisekunden, wenn der Wert unter den Schwellenwert fällt
    """
    start_time = None    
    while True:
        if sensor.range() > threshold:
            if start_time is None:
                start_time = time.ticks_ms()  # Startzeit festlegen
        else:
            if start_time is not None:
                elapsed_time = time.ticks_ms() - start_time
                start_time = None  # Reset der Startzeit
                return elapsed_time

def calculate_statistics(actual_time):
    """
    Speichert die gemessene Zeit, berechnet die Statistiken und gibt die aktuellen und berechneten Zeiten zurück.

    :param actual_time: Die tatsächliche gemessene Zeit in Millisekunden
    :return: tuple (actual_time, fastest_time, average_time)
    """
    global measurement_times
    fastest_time = 0
    average_time = 0
    
    if actual_time == None:
        actual_time = 99999
    else:  
        # Füge die neue Zeit hinzu und entferne die ältesten, wenn das Array voll ist
        if len(measurement_times) >= MAX_MEASUREMENTS:
            measurement_times.pop(0)
        measurement_times.append(actual_time)
        
        # Berechne die Statistiken
        fastest_time = min(measurement_times) if measurement_times else None
        average_time = sum(measurement_times) / len(measurement_times) if measurement_times else None
    
    return actual_time, fastest_time, average_time
    
def read_start_sensor(pin):
    # Überprüfen Sie den Status des Pins
    return pin.value()

def display_times(display, actual_time, fastest_time, average_time):
    """
    Zeigt die aktuelle Zeit, die schnellste Zeit und die durchschnittliche Zeit auf dem OLED-Display an.
    """
    display.fill(0)  # Löscht das Display

    # Aktuelle Zeit groß und mittig oben anzeigen
    #display.text(f"{actual_time/1000:.3f} s", WIDTH // 2 - len(f"{actual_time} ms") * 3, 10, 1)
    font_writer = writer.Writer(display, freesans30)
    font_writer.set_textpos(10, 0)
    font_writer.printstring(f"{actual_time/1000:.3f} s")
    
    # Durchschnitts-Symbol links unten anzeigen
    display.blit(framebuf.FrameBuffer(average_icon, 8, 8, framebuf.MONO_HLSB), 0, HEIGHT - 20)
    display.blit(framebuf.FrameBuffer(flag_icon, 8, 8, framebuf.MONO_HLSB), WIDTH // 2 , HEIGHT - 20)
    display.text(f"{average_time/1000:.2f}", 10, HEIGHT - 10, 1)
    display.text(f"{fastest_time/1000:.2f}", WIDTH // 2, HEIGHT - 10, 1)

    display.show()  # Aktualisiert das Display

def drink_speed_measurement(sensor):
    print('3...2...1...')
    print('show animation')
    pe.in_and_out("in", 125, red, False)
    pe.in_and_out("out", 500, red, True)
    pe.all_on(green)
    # elapsed_time = measure_time_below_threshold(sensor)
    elapsed_time = measure_time()
    print('__')
    # Berechne die Statistiken und gib die Ergebnisse aus
    actual_time, fastest_time, average_time = calculate_statistics(elapsed_time)
    print(f"Aktuelle Trinkzeit: {actual_time} ms")
    print(f"Schnellste Zeit: {fastest_time} ms")
    print(f"Durchschnittliche Zeit: {average_time} ms")
    
    # Zeige die Zeiten auf dem Display an
    display_times(display, actual_time, fastest_time, average_time)

def sensor_interrupt_handler(pin):
    global interrupt_enabled
    if interrupt_enabled and read_start_sensor(pin) == True:
        interrupt_enabled = False  # Deaktiviere den Interrupt
        drink_speed_measurement(sensor)
        interrupt_enabled = True   # Reaktiviere den Interrupt




# Initialisiere VL6180X
try:
    sensor = VL6180X(i2c)
    print(sensor.identify())
    print("VL6180X-Sensor erfolgreich initialisiert.")
except Exception as e:
    print(f"Fehler bei der Initialisierung des Sensors: {e}")
    while True:
        pass  # Stoppe das Programm bei Fehler
    
# Initialisiere das OLED-Display
try:
    display = SSD1306_I2C(WIDTH, HEIGHT, i2c)
    print("OLED-Display erfolgreich initialisiert.")
except Exception as e:
    print(f"Fehler bei der Initialisierung des Displays: {e}")
    while True:
        pass  # Stoppe das Programm bei Fehler

# Initialisiere den Berührungssensor mit Interrupt
try:
    touch_sensor_start.irq(trigger=Pin.IRQ_RISING, handler=sensor_interrupt_handler)
    print("Touch Sensor erfolgreich initialisiert.")
except Exception as e:
    print(f"Fehler bei der Initialisierung des Touch Sensors: {e}")
    while True:
        pass  # Stoppe das Programm bei Fehler

# Initialisiere der Pixel LED Ringe
try:
    pe = PIXEL_EFFECTS(num_pixels, ring_pixel_sizes, pin, brightness)
    print("LED Pixel Ring erfolgreich initialisiert.")
except Exception as e:
    print(f"Fehler bei der Initialisierung der LED Pixel Ringe: {e}")
    while True:
        pass  # Stoppe das Programm bei Fehler
    
    


# Hauptloop für andere Aufgaben oder LED-Show
while True:
    # Hier können Sie andere Aufgaben ausführen, z.B. eine LED-Show
    # Das Programm wird nicht durch den Hauptloop blockiert, wenn der Interrupt ausgelöst wird
    
    pe.random_lights(yellow, 2,100)
    pe.random_lights(indigo, 2,100)
    pe.clock_hand_effect(indigo, duration=1, speed=0.015, inverted=False)
    
    # Rainbow-Effekt für 10 Sekunden ausführen
    pe.rainbow_inside_out(duration=10)

    # Uhrzeiger-Effekt (läuft für 5 Sekunden)
    pe.clock_hand_effect((255, 255, 0), duration=2.15, speed=0.05, inverted=False)
    
    # Invertierter Uhrzeiger-Effekt (läuft für 5 Sekunden)
    pe.clock_hand_effect(violet, duration=5, speed=0.05, inverted=True)


    pe.random_lights(green, 2,100)
    pe.random_lights(blue, 2,100)
    
    pe.rainbow_inside_out(duration=5)

    pe.twinkle(5, red)
    
    # Funktionsaufruf:
    pe.heartbeat_effect(red, 2)  # 10 Sekunden lang ein roter Herzschlag-Effekt
    pe.heartbeat_effect(orange, 2)  # 10 Sekunden lang ein roter Herzschlag-Effekt
    pe.heartbeat_effect(yellow, 2)  # 10 Sekunden lang ein roter Herzschlag-Effekt
    
    pe.theater_chase(orange, 10)
    
    pe.color_wipe(green, blue)
    pe.theater_chase(green, 10)
    
