import ui
import cb
import struct
import csv
from datetime import datetime

# --- GLOBAALIT MUUTTUJAT DATALLE ---
collect_data = []  # [{time, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z}, ...]
is_connected = False
puck_peripheral = None

data_buffer = bytearray()
is_receiving_data = False

# Puck.js tunnetut UUID:t Nordic UART -profiilille
NORDIC_TX_UUID = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'
NORDIC_RX_UUID = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'

# --- BLUETOOTH-LOGIIKKA (CoreBluetooth / cb) ---
class PuckBluetoothManager(object):
    def did_discover_peripheral(self, p):
        if p.name and 'Puck.js' in p.name:
            print(f"Löytyi laite: {p.name}")
            global puck_peripheral
            puck_peripheral = p
            cb.connect_peripheral(p)
            cb.stop_scan()

    def did_connect_peripheral(self, p):
        global is_connected
        is_connected = True
        print("Yhdistetty Puck.js-laitteeseen!")
        status_label.text = "Yhdistetty"
        status_label.text_color = '#4CAF50'
        connect_btn.title = "Katkaise yhteys"
        p.discover_services()

    def did_discover_services(self, p, error):
        for s in p.services:
            if '6E400001' in s.uuid:  # UART Service
                p.discover_characteristics(s)

    def did_discover_characteristics(self, s, error):
        for c in s.characteristics:
            if c.uuid == NORDIC_TX_UUID:
                # Tilaa data Puckilta (Notification päälle)
                puck_peripheral.set_notify_value(c, True)

    def did_update_value(self, c, error):
        global data_buffer, is_receiving_data
        if c.uuid == NORDIC_TX_UUID and c.value:
            raw_bytes = c.value
            
            # Tarkistetaan tekstimuotoiset ohjauskomennot alusta/lopusta
            try:
                text_check = raw_bytes.decode('utf-8', errors='ignore').strip()
                
                if "DATA_START" in text_check:
                    print("Lataus alkoi...")
                    data_buffer = bytearray() # Tyhjennetään vanha puskuri
                    is_receiving_data = True
                    return
                    
                if "DATA_END" in text_check:
                    print(f"Lataus päättyi. Tavuun määrä puskurissa: {len(data_buffer)}")
                    is_receiving_data = False
                    parse_binary_buffer(data_buffer)
                    return
            except:
                pass
            
            # Jos ollaan lataustilassa, lisätään raa'at tavut puskuriin
            if is_receiving_data:
                data_buffer.extend(raw_bytes)

# --- BINÄÄRIDATAN PURKAMINEN PYTHONISSA ---
def parse_binary_buffer(buffer_data):
    global collect_data
    
    # Siivotaan mahdolliset pilkut (',') tai rivinvaihdot, jotka Puck.js lisäsi silmukassa
    # Koska Puck tekee Bluetooth.print(","), poistetaan ne binääristä
    clean_bytes = bytearray([b for b in buffer_data if b != ord(',') and b != ord('\n')])
    
    # ⚠️ MÄÄRITÄ TÄHÄN DATAN TYYPPI:
    # 'f' = 32-bit float (4 tavua per numero)
    # 'h' = 16-bit int (2 tavua per numero)
    # 'i' = 32-bit int (4 tavua per numero)
    data_type = 'f' 
    bytes_per_value = 4 # Jos float, niin 4 tavua
    
    # Yhdessä datapisteessä on 6 arvoa (Acc X,Y,Z + Gyro X,Y,Z)
    bytes_per_row = bytes_per_value * 6 
    
    total_elements = len(clean_bytes) // bytes_per_value
    total_rows = len(clean_bytes) // bytes_per_row
    
    print(f"Puretaan {total_rows} kpl 6-akselisia datapisteitä...")
    
    try:
        # Puretaan tavut numeroiksi struct-kirjastolla
        # '<' tarkoittaa Little-Endian (Puck.js oletus)
        format_string = f"<{total_elements}{data_type}"
        all_numbers = struct.unpack(format_string, clean_bytes[:total_elements * bytes_per_value])
        
        now = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        
        # Ryhmitellään numerot 6 kpl riveihin (Acc X,Y,Z, Gyro X,Y,Z)
        for i in range(0, len(all_numbers), 6):
            if i + 5 < len(all_numbers):
                row = {
                    'time': now,
                    'ax': all_numbers[i],
                    'ay': all_numbers[i+1],
                    'az': all_numbers[i+2],
                    'gx': all_numbers[i+3],
                    'gy': all_numbers[i+4],
                    'gz': all_numbers[i+5]
                }
                collect_data.append(row)
                
        # Pyydetään käyttöliittymää piirtämään uusi data kuvaajiin
        ui.execute_ui_command(acc_chart.set_needs_display)
        ui.execute_ui_command(gyro_chart.set_needs_display)
        print("Data päivitetty kuvaajiin onnistuneesti!")
        
    except Exception as e:
        print(f"Virhe binääridatan purkamisessa: {e}")

# --- CSV-TALLENNUS (Toimii iPadissa natiivisti!) ---
def save_to_csv_action(sender):
    global collect_data
    if not collect_data:
        ui.alert("Ei tallennettavaa dataa!")
        return
        
    filename = ui.input_alert("Tiedostonimi", "Anna CSV-tiedoston nimi (ilman päätettä):", "puck_data")
    if not filename:
        return
        
    full_filename = f"{filename}.csv"
    
    # Kirjoitetaan suoraan iPadin paikalliseen muistiin
    try:
        with open(full_filename, mode='w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=['time', 'ax', 'ay', 'az', 'gx', 'gy', 'gz'])
            writer.writeheader()
            writer.writerows(collect_data)
            
        ui.alert("Valmis!", f"Tiedosto tallennettu nimellä:\n{full_filename}\nLöydät sen Pythonista-kansiosta.")
    except Exception as e:
        ui.alert("Virhe tallennuksessa", str(e))

# --- KÄYTTÖLIITTYMÄN RAKENNE (UI) ---
class ChartView(ui.View):
    """ Natiivi piirtoalusta kuvaajille korvaamaan Highcharts """
    def __init__(self, title, data_keys):
        self.title = title
        self.data_keys = data_keys
        self.background_color = '#ffffff'
        
    def draw(self):
        # Piirretään kuvaajan tausta, raamit ja viivat
        ui.set_color('#333333')
        ui.draw_string(self.title, rect=(10, 5, 200, 20), font=('Arial', 14))
        
        # Piirretään dataikkuna (yksinkertaistettu reaaliaikainen käyrä)
        if len(collect_data) < 2:
            return
            
        # Näytetään esim. viimeiset 50 datapistettä
        plot_data = collect_data[-50:]
        w, h = self.width, self.height
        
        # Piirretään viivat eri akseleille (X=Punainen, Y=Vihreä, Z=Sininen)
        colors = ['#ff0000', '#00ff00', '#0000ff']
        for i, key in enumerate(self.data_keys):
            ui.set_color(colors[i])
            path = ui.Path()
            
            for index, row in enumerate(plot_data):
                x_pos = (index / 50) * w
                # Skaalataan arvo sopivaksi ruudulle (keskipiste h/2)
                y_pos = (h / 2) - (row[key] * 10) 
                
                if index == 0:
                    path.move_to(x_pos, y_pos)
                else:
                    path.line_to(x_pos, y_pos)
            path.stroke()

# --- BLE PAINIKKEIDEN TOIMINNOT ---
ble_manager = PuckBluetoothManager()

def connect_action(sender):
    global is_connected
    if not is_connected:
        status_label.text = "Esitään Puck.js..."
        cb.set_central_delegate(ble_manager)
        cb.scan_for_peripherals()
    else:
        if puck_peripheral:
            cb.disconnect_peripheral(puck_peripheral)
        is_connected = False
        status_label.text = "Ei yhteyttä"
        status_label.text_color = 'gray'
        connect_btn.title = "Yhdistä Puck.js"

def send_g_limit(sender):
    if is_connected and puck_peripheral:
        limit = g_input.text
        # Lähetetään UART-komento Puckille
        cmd = f"g_limit = {limit};\n".encode('utf-8')
        # Etsitään RX-karakteristiikka kirjoitusta varten
        # (Yksinkertaistettu: app.js tekee tämän taustalla)
        print(f"Lähetetään komento: {cmd}")

# --- PÄÄIKKUNAN ASENTELU (Vastaa HTML-sivupalkkiasi) ---
main_view = ui.View(name="Puck.js Data")
main_view.background_color = '#f0f0f0'

# Vasen Sivupalkki (Sidebar)
sidebar = ui.View(frame=(0, 0, 260, 768))
sidebar.background_color = '#ffffff'
sidebar.flex = 'H'

connect_btn = ui.Button(title="Yhdistä Puck.js", frame=(20, 20, 220, 44))
connect_btn.background_color = '#4CAF50'
connect_btn.tint_color = 'white'
connect_btn.action = connect_action

status_label = ui.Label(text="Ei yhteyttä", frame=(20, 70, 220, 30))
status_label.alignment = ui.ALIGN_CENTER
status_label.text_color = 'gray'

g_label = ui.Label(text="G limit raja (g):", frame=(20, 120, 220, 20))
g_input = ui.TextField(text="6", frame=(20, 145, 220, 35))
g_input.keyboard_type = ui.KEYBOARD_NUMBER_PAD

send_btn = ui.Button(title="Lähetä g_limit", frame=(20, 195, 220, 44))
send_btn.background_color = '#4CAF50'
send_btn.tint_color = 'white'
send_btn.action = send_g_limit

save_btn = ui.Button(title="Tallenna tiedot CSV", frame=(20, 250, 220, 44))
save_btn.background_color = '#1447E6'
save_btn.tint_color = 'white'
save_btn.action = save_to_csv_action

sidebar.add_subview(connect_btn)
sidebar.add_subview(status_label)
sidebar.add_subview(g_label)
sidebar.add_subview(g_input)
sidebar.add_subview(send_btn)
sidebar.add_subview(save_btn)

# Oikea Kuvaaja-alue (Main Content)
acc_chart = ChartView("Kiihtyvyys (Acc)", ['ax', 'ay', 'az'])
acc_chart.frame = (270, 10, 744, 360)
acc_chart.flex = 'WH'

gyro_chart = ChartView("Gyroskooppi (Gyro)", ['gx', 'gy', 'gz'])
gyro_chart.frame = (270, 380, 744, 360)
gyro_chart.flex = 'WH'

# Kootaan kaikki näkymään
main_view.add_subview(sidebar)
main_view.add_subview(acc_chart)
main_view.add_subview(gyro_chart)

# Avataan sovellus koko ruudulle iPadissa
main_view.present('fullscreen')
