"""Test the two-finger pan + photo-sized view layout:
1. A wide photo (landscape) viewed in portrait produces a sketch-view WIDER than
   the shell (cover behavior), so there is real content to pan.
2. Two fingers sliding together (distance constant) PAN the view left/right.
3. The view is centered on first open.
"""
import os, sys, time, json
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service

HERE = os.path.dirname(os.path.abspath(__file__))
GECKODRIVER = os.path.join(HERE, "geckodriver.exe")
BASE = "http://localhost:8000/field-capture.html?debug"

# Two-finger pan: both fingers start near the shell center and translate by
# (dpx, dpy) client px together (distance preserved) => pan, not zoom.
PAN_JS = """
return (function(dpx, dpy, id1, id2){
  const canvas = document.getElementById('sketchCanvas');
  const view = document.getElementById('sketchView');
  canvas.setPointerCapture = function(){};
  canvas.releasePointerCapture = function(){};
  canvas.hasPointerCapture = function(){ return false; };
  const shell = document.querySelector('.sketch-shell');
  const sr = shell.getBoundingClientRect();
  const left = sr.left + shell.clientLeft, top = sr.top + shell.clientTop;
  const T = window.__test;
  const cx = left + shell.clientWidth/2, cy = top + shell.clientHeight/2;
  const opts = { bubbles: true, cancelable: true, pointerType: 'touch' };
  const p1 = { x: cx - 40, y: cy };
  const p2 = { x: cx + 40, y: cy };
  canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: id1, clientX: p1.x, clientY: p1.y})));
  canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: id2, clientX: p2.x, clientY: p2.y})));
  const e1 = { x: p1.x + dpx, y: p1.y + dpy };
  const e2 = { x: p2.x + dpx, y: p2.y + dpy };
  for (let i = 1; i <= 6; i++) {
    canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: id1, clientX: p1.x+(e1.x-p1.x)*i/6, clientY: p1.y+(e1.y-p1.y)*i/6})));
    canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: id2, clientX: p2.x+(e2.x-p2.x)*i/6, clientY: p2.y+(e2.y-p2.y)*i/6})));
  }
  canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: id1, clientX: e1.x, clientY: e1.y})));
  canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: id2, clientX: e2.x, clientY: e2.y})));
  const after = T.ui.viewState[T.ui.sketchId];
  return JSON.stringify({ scale: after.scale, tx: after.tx, ty: after.ty });
})(arguments[0], arguments[1], arguments[2], arguments[3]);
"""

# Pinch-out: fingers start apart and move closer (distance shrinks) => zoom out.
# mult < 1 shrinks the distance. Returns the resulting scale.
ZOOM_OUT_JS = """
return (function(mult, id1, id2){
  const canvas = document.getElementById('sketchCanvas');
  const view = document.getElementById('sketchView');
  canvas.setPointerCapture = function(){};
  canvas.releasePointerCapture = function(){};
  canvas.hasPointerCapture = function(){ return false; };
  const shell = document.querySelector('.sketch-shell');
  const sr = shell.getBoundingClientRect();
  const left = sr.left + shell.clientLeft, top = sr.top + shell.clientTop;
  const T = window.__test;
  const cx = left + shell.clientWidth/2, cy = top + shell.clientHeight/2;
  const opts = { bubbles: true, cancelable: true, pointerType: 'touch' };
  const p1 = { x: cx - 120, y: cy };
  const p2 = { x: cx + 120, y: cy };
  canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: id1, clientX: p1.x, clientY: p1.y})));
  canvas.dispatchEvent(new PointerEvent('pointerdown', Object.assign({}, opts, {pointerId: id2, clientX: p2.x, clientY: p2.y})));
  const e1 = { x: cx - 120*mult, y: cy };
  const e2 = { x: cx + 120*mult, y: cy };
  for (let i = 1; i <= 8; i++) {
    canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: id1, clientX: p1.x+(e1.x-p1.x)*i/8, clientY: p1.y+(e1.y-p1.y)*i/8})));
    canvas.dispatchEvent(new PointerEvent('pointermove', Object.assign({}, opts, {pointerId: id2, clientX: p2.x+(e2.x-p2.x)*i/8, clientY: p2.y+(e2.y-p2.y)*i/8})));
  }
  canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: id1, clientX: e1.x, clientY: e1.y})));
  canvas.dispatchEvent(new PointerEvent('pointerup', Object.assign({}, opts, {pointerId: id2, clientX: e2.x, clientY: e2.y})));
  return JSON.stringify({ scale: T.ui.viewState[T.ui.sketchId].scale });
})(arguments[0], arguments[1], arguments[2]);
"""

def main():
    opts = Options()
    opts.headless = False
    svc = Service(executable_path=GECKODRIVER)
    d = webdriver.Firefox(options=opts, service=svc)
    d.set_window_size(480, 900)
    try:
        d.get(BASE)
        time.sleep(2)
        d.find_element(By.CSS_SELECTOR, '[data-action="newjob"]').click()
        time.sleep(0.8)
        d.find_element(By.CSS_SELECTOR, '[data-action="opensketch"]').click()
        time.sleep(0.8)
        # Inject a WIDE (landscape) photo: 800x300.
        d.execute_script("""
            const T = window.__test;
            const job = T.state.jobs[0];
            const sk = job.sketches[0];
            const c = document.createElement('canvas');
            c.width = 800; c.height = 300;
            const ctx = c.getContext('2d');
            ctx.fillStyle = '#cccccc'; ctx.fillRect(0,0,800,300);
            sk.photo = { dataUrl: c.toDataURL('image/jpeg', 0.8), width: 800, height: 300 };
            T.saveState();
        """)
        d.execute_script(
            "const T=window.__test; T.ui.screen='sketch'; T.ui.sketchId=T.state.jobs[0].sketches[0].id; T.render();"
        )
        time.sleep(0.8)

        # 1. View should be WIDER than the shell (photo-sized, cover).
        dims = json.loads(d.execute_script("""
            const shell = document.querySelector('.sketch-shell');
            const view = document.getElementById('sketchView');
            return JSON.stringify({ shellW: shell.clientWidth, shellH: shell.clientHeight,
                                     viewW: view.offsetWidth, viewH: view.offsetHeight });
        """))
        print("dims:", dims)
        ok1 = dims["viewW"] > dims["shellW"] and abs(dims["viewH"] - dims["shellH"]) < 2
        print("PASS view wider than shell" if ok1 else "FAIL view wider than shell")

        # 2. View should be centered on first open (negative tx).
        v0 = json.loads(d.execute_script(
            "const T=window.__test; const v=T.ui.viewState[T.ui.sketchId]; return JSON.stringify({tx:v.tx, ty:v.ty, scale:v.scale});"
        ))
        print("initial view:", v0)
        ok2 = v0["scale"] == 1 and v0["tx"] < 0
        print("PASS centered on open" if ok2 else "FAIL centered on open")

        # 3. Two-finger pan right by 100px should increase tx (pan right),
        #    keeping scale ~1 (not zoomed).
        pan = json.loads(d.execute_script(PAN_JS, 100, 0, 51, 52))
        print("pan +100px:", pan)
        ok3 = pan["scale"] < 1.1 and pan["tx"] > v0["tx"] + 50
        print("PASS two-finger pan right" if ok3 else "FAIL two-finger pan right")

        # 4. Pinch OUT (fingers closer) should zoom below 1 to fit the entire
        #    wide photo. The fit scale for a 1955px-wide view in a ~500px shell
        #    is ~0.26, so the resulting scale must be well below 1.
        zo = json.loads(d.execute_script(ZOOM_OUT_JS, 0.3, 61, 62))
        print("zoom out:", zo)
        ok4 = zo["scale"] < 0.9
        print("PASS zoom out to fit" if ok4 else "FAIL zoom out to fit")
    finally:
        d.quit()

if __name__ == "__main__":
    main()