# quad_editor_touch_zoom.py  (MicroPython)
#
# Touch pads:
#   1..4 : move vertices V1..V4 (tap = change direction, hold = move)
#   5    : zoom in (hold)
#   6    : zoom out (hold)
#
# Assumes your same libs + wiring from your examples:
# - steptime.py, ssd1306.py
# - Touch pins: 26,27,1,2,3,4 (pads 1..6)
# - OLED I2C(1): SCL=7, SDA=6

from steptime import STEPTIME
from ssd1306 import SSD1306_I2C

from machine import Pin, I2C, freq
import utime

# Your steptime demo uses high clock
freq(250000000)

# OLED (same as your hello_oled.py)
i2c = I2C(1, scl=Pin(7), sda=Pin(6), freq=200000)
oled = SSD1306_I2C(128, 64, i2c)

# Touch pads (same as your hello_touch_neopixel.py)
PAD_PINS = [26, 27, 1, 2, 3, 4]  # pads 1..6
for p in PAD_PINS:
    Pin(p, Pin.IN, Pin.PULL_UP)

# Channels: one STEPTIME per pad (sm_id 0..5)
channels = []
for i, pin in enumerate(PAD_PINS):
    channels.append([STEPTIME(i, pin), [1e6], [0]])  # [sm, [min], [delta]]

# Steptime tuning (from your demo)
LOOP = 200
SETTLE = 20000
THRESH = 10000  # adjust if needed

# ---------- Small drawing helpers (in case line() isn't available) ----------
def _line_bresenham(x0, y0, x1, y1, c=1):
    dx = abs(x1 - x0)
    sx = 1 if x0 < x1 else -1
    dy = -abs(y1 - y0)
    sy = 1 if y0 < y1 else -1
    err = dx + dy
    while True:
        if 0 <= x0 < 128 and 0 <= y0 < 64:
            oled.pixel(x0, y0, c)
        if x0 == x1 and y0 == y1:
            break
        e2 = err * 2
        if e2 >= dy:
            err += dy
            x0 += sx
        if e2 <= dx:
            err += dx
            y0 += sy

def draw_line(x0, y0, x1, y1, c=1):
    # Many MicroPython ssd1306 drivers have line(); if not, use bresenham.
    if hasattr(oled, "line"):
        oled.line(x0, y0, x1, y1, c)
    else:
        _line_bresenham(x0, y0, x1, y1, c)

def draw_handle(x, y):
    # small 3x3 box
    oled.rect(x - 1, y - 1, 3, 3, 1)

# ---------- Interaction model ----------
# Each vertex has a direction index: 0=→,1=↓,2=←,3=↑
DIRS = [(1, 0), (0, 1), (-1, 0), (0, -1)]
DIR_CHARS = [">", "v", "<", "^"]

# Model-space vertices (float-like using ints is fine)
verts = [
    [-20, -15],  # V1
    [ 20, -15],  # V2
    [ 20,  15],  # V3
    [-20,  15],  # V4
]
dir_idx = [0, 1, 2, 3]

# View transform
zoom = 1.5
ZOOM_MIN = 0.5
ZOOM_MAX = 6.0

# Movement tuning
MOVE_PX_PER_SEC = 25.0   # model units per second (scaled by 1/zoom for screen feel)
ZOOM_PER_SEC = 1.0       # zoom units per second

# Debounce / tap detection
DEBOUNCE_MS = 180
tap_armed = [True] * 6
last_edge_ms = [0] * 6

def read_touch_deltas():
    deltas = [0] * 6
    for i, ch in enumerate(channels):
        sm, min_val, printable = ch
        sm.put(LOOP)
        sm.put(SETTLE)
        raw = 4294967296 - sm.get()   # same as your demo
        if raw < min_val[0]:
            min_val[0] = raw
        d = raw - min_val[0]
        printable[0] = d
        deltas[i] = d
    return deltas

def pad_pressed(deltas, i):
    return deltas[i] > THRESH

def to_screen(mx, my):
    # center screen at (64,32)
    sx = int(64 + mx * zoom)
    sy = int(32 + my * zoom)
    return sx, sy

def clamp_vert(v):
    # Keep things within a reasonable model range so they remain drawable
    if v[0] < -200: v[0] = -200
    if v[0] >  200: v[0] =  200
    if v[1] < -200: v[1] = -200
    if v[1] >  200: v[1] =  200

# ---------- Main loop ----------
oled.fill(0)
oled.text("Quad editor", 0, 0)
oled.text("Pads 1-4 move", 0, 12)
oled.text("5 zoom+ 6 zoom-", 0, 24)
oled.text("Tap=dir Hold=mv", 0, 36)
oled.show()

last_ms = utime.ticks_ms()

while True:
    now = utime.ticks_ms()
    dt_ms = utime.ticks_diff(now, last_ms)
    last_ms = now
    dt = dt_ms / 1000.0

    deltas = read_touch_deltas()
    pressed = [pad_pressed(deltas, i) for i in range(6)]

    # --- Tap detection for pads 1..4 to cycle direction ---
    for i in range(4):
        if pressed[i]:
            # detect rising edge with debounce
            if tap_armed[i] and utime.ticks_diff(now, last_edge_ms[i]) > DEBOUNCE_MS:
                # rising edge => treat as tap (direction change),
                # BUT also allow hold-move in the same frame.
                dir_idx[i] = (dir_idx[i] + 1) & 3
                tap_armed[i] = False
                last_edge_ms[i] = now
        else:
            tap_armed[i] = True

    # --- Move vertices while holding pads 1..4 ---
    # Make movement feel consistent under zoom: scale by (1/zoom)
    move_step = (MOVE_PX_PER_SEC * dt) / (zoom if zoom > 0.001 else 1.0)
    for i in range(4):
        if pressed[i]:
            dx, dy = DIRS[dir_idx[i]]
            verts[i][0] += dx * move_step
            verts[i][1] += dy * move_step
            clamp_vert(verts[i])

    # --- Zoom with pads 5 and 6 (indices 4 and 5) ---
    if pressed[4]:
        zoom += ZOOM_PER_SEC * dt
    if pressed[5]:
        zoom -= ZOOM_PER_SEC * dt
    if zoom < ZOOM_MIN: zoom = ZOOM_MIN
    if zoom > ZOOM_MAX: zoom = ZOOM_MAX

    # --- Render ---
    oled.fill(0)

    # Title / HUD
    oled.text("Zoom:{:.2f}".format(zoom), 0, 0)
    oled.text("Dir:1{} 2{} 3{} 4{}".format(
        DIR_CHARS[dir_idx[0]],
        DIR_CHARS[dir_idx[1]],
        DIR_CHARS[dir_idx[2]],
        DIR_CHARS[dir_idx[3]]
    ), 0, 10)

    # Convert verts to screen
    pts = [to_screen(v[0], v[1]) for v in verts]

    # Draw quad edges
    for i in range(4):
        x0, y0 = pts[i]
        x1, y1 = pts[(i + 1) % 4]
        draw_line(x0, y0, x1, y1, 1)

    # Draw vertex handles + labels
    for i in range(4):
        x, y = pts[i]
        draw_handle(x, y)
        # labels near each vertex (clamped a bit)
        lx = x + 3
        ly = y - 4
        if lx < 0: lx = 0
        if ly < 0: ly = 0
        if lx > 120: lx = 120
        if ly > 56: ly = 56
        oled.text(str(i + 1), lx, ly)

    # Mini legend
    oled.text("1-4 move", 0, 54)
    oled.text("5+ 6-", 72, 54)

    oled.show()

    utime.sleep_ms(10)
