#!/usr/bin/env python3
"""Interactive demo for the Acaia Lunar BLE library.

Connects to an Acaia Lunar scale and streams weight/timer data in real-time.
Type a command letter and press Enter. Press 'h' for help, 'q' to quit.
Exits automatically if the scale disconnects.

Usage:
    python examples/demo.py <SCALE-ADDRESS>
    python examples/demo.py                   # auto-discovers nearby Acaia scales
"""

import asyncio
import logging
import os
import sys

from acaia_lunar_ble import ScaleConnection

HELP = """
Commands:
  c — connect         t — tare
  d — disconnect      1 — start timer
  s — status          2 — stop timer
  g — get settings    3 — reset timer
  r — toggle raw log  h — show commands
  q — quit
"""

raw_logging_enabled = False
first_reading = True
last_weight = ""
last_timer = ""
prev_timer_value = None
timer_running = False


def _print_status_line():
    global first_reading
    if first_reading:
        print()
        first_reading = False
    parts = []
    if last_weight:
        parts.append(last_weight)
    if last_timer and timer_running:
        parts.append(last_timer)
    line = f"  {' | '.join(parts)}"
    print(f"\r{line}\033[K", end="", flush=True)


def on_weight(weight: float):
    global last_weight
    last_weight = f"Weight: {weight:>8.2f} g "
    _print_status_line()


def on_timer(seconds: float):
    global last_timer, prev_timer_value, timer_running
    timer_running = prev_timer_value is not None and seconds != prev_timer_value
    prev_timer_value = seconds
    mins = int(seconds) // 60
    secs = seconds - mins * 60
    last_timer = f"Timer: {mins}:{secs:04.1f} "
    _print_status_line()


def _clear_line():
    """Move to start of line and clear it."""
    print("\r\033[K", end="", flush=True)


def on_settings(settings):
    _clear_line()
    print(f"  Settings: battery={settings.battery}%, units={settings.units}, "
          f"auto_off={settings.auto_off_minutes}min, beep={'on' if settings.beep else 'off'}")


def on_button(button: str):
    _clear_line()
    print(f"  Button pressed: {button}")


def on_raw(data: bytearray):
    if raw_logging_enabled:
        _clear_line()
        hex_str = " ".join(f"{b:02X}" for b in data)
        print(f"  RAW [{len(data):>2d}]: {hex_str}")


async def async_input(prompt: str = "") -> str:
    """Read a line from stdin without blocking the event loop."""
    return await asyncio.to_thread(input, prompt)


async def watch_connection(scale: ScaleConnection, disconnect_event: asyncio.Event):
    """Background task that detects when the scale disconnects unexpectedly."""
    while scale.is_connected:
        await asyncio.sleep(1)
    disconnect_event.set()


async def main():
    global raw_logging_enabled

    address = sys.argv[1] if len(sys.argv) > 1 else None

    if address is None:
        print("No address provided. Scanning for Acaia scales...\n")
        acaia_devices, all_devices = await ScaleConnection.discover(timeout=5.0)
        if not acaia_devices:
            print("No Acaia scales found. Nearby BLE devices:")
            for d in all_devices:
                print(f"  {d.name or '(unknown)'}: {d.address}")
            if not all_devices:
                print("  (none)")
            return
        print(f"Found Acaia scale: {acaia_devices[0].name} ({acaia_devices[0].address})\n")
        address = acaia_devices[0].address

    scale = ScaleConnection(
        address,
        weight_callback=on_weight,
        timer_callback=on_timer,
        settings_callback=on_settings,
        button_callback=on_button,
        notification_callback=on_raw,
    )

    if not await scale.connect():
        print("Connection failed.")
        return

    print("Connected!")
    print(HELP)

    disconnect_event = asyncio.Event()
    watcher = asyncio.create_task(watch_connection(scale, disconnect_event))

    try:
        while True:
            # Race: wait for keypress OR unexpected disconnect
            key_task = asyncio.create_task(async_input("  "))
            disconnect_task = asyncio.create_task(disconnect_event.wait())

            done, _ = await asyncio.wait(
                {key_task, disconnect_task},
                return_when=asyncio.FIRST_COMPLETED,
            )

            if disconnect_task in done:
                key_task.cancel()
                _clear_line()
                print("\nScale disconnected.")
                break

            disconnect_task.cancel()
            cmd = key_task.result().strip().lower()
            _clear_line()
            print()  # blank line below weight

            if cmd == "c":
                if scale.is_connected:
                    print("Already connected.")
                else:
                    if await scale.connect():
                        print("Connected!")
                        disconnect_event.clear()
                        watcher.cancel()
                        watcher = asyncio.create_task(watch_connection(scale, disconnect_event))
                    else:
                        print("Connection failed.")

            elif cmd == "d":
                if scale.is_connected:
                    watcher.cancel()
                    await scale.disconnect()
                    print("Disconnected.")
                else:
                    print("Not connected.")

            elif cmd == "s":
                print(f"Scale {'connected' if scale.is_connected else 'disconnected'}")

            elif cmd == "t":
                await scale.tare()

            elif cmd == "1":
                await scale.start_timer()

            elif cmd == "2":
                await scale.stop_timer()

            elif cmd == "3":
                await scale.reset_timer()

            elif cmd == "g":
                await scale.get_settings()

            elif cmd == "r":
                raw_logging_enabled = not raw_logging_enabled
                print(f"Raw data logging {'ON' if raw_logging_enabled else 'OFF'}")

            elif cmd == "h":
                print(HELP)

            elif cmd == "q":
                break

    except (KeyboardInterrupt, EOFError):
        pass

    watcher.cancel()
    if scale.is_connected:
        await scale.disconnect()
    _clear_line()
    sys.exit(0)


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s %(name)s %(levelname)s: %(message)s",
    )
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        os._exit(0)
