#!/usr/bin/env python3
import base64
import html
import json
import subprocess
import urllib.parse
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer


ADB = ["adb", "-s", "emulator-5554"]


def adb(*args, stdin=None):
    return subprocess.run(
        [*ADB, *args],
        input=stdin,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        check=True,
    ).stdout


INDEX = """<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Android Emulator</title>
  <style>
    body { margin: 0; font: 14px system-ui, sans-serif; background: #121212; color: #eee; }
    main { display: grid; grid-template-columns: minmax(320px, 420px) 1fr; min-height: 100vh; }
    #screen { width: 100%; max-height: 100vh; object-fit: contain; background: #000; cursor: crosshair; }
    aside { padding: 16px; display: grid; align-content: start; gap: 12px; }
    button, input { font: inherit; padding: 10px 12px; border-radius: 6px; border: 1px solid #555; }
    button { background: #2f6fed; color: white; cursor: pointer; }
    input { background: #202124; color: #eee; }
    .row { display: flex; gap: 8px; flex-wrap: wrap; }
    .row button { flex: 1 1 auto; }
    @media (max-width: 760px) { main { grid-template-columns: 1fr; } aside { order: -1; } }
  </style>
</head>
<body>
<main>
  <img id="screen" alt="Android screen">
  <aside>
    <div class="row">
      <button onclick="key(3)">Home</button>
      <button onclick="key(4)">Back</button>
      <button onclick="key(187)">Overview</button>
      <button onclick="refresh()">Refresh</button>
    </div>
    <div class="row">
      <button onclick="launchPlay()">Play Store</button>
      <button onclick="swipe(540,1600,540,500,450)">Swipe Up</button>
      <button onclick="swipe(540,500,540,1600,450)">Swipe Down</button>
    </div>
    <input id="text" placeholder="Text to type">
    <div class="row">
      <button onclick="typeText()">Type</button>
      <button onclick="key(66)">Enter</button>
      <button onclick="key(67)">Delete</button>
    </div>
    <div id="status"></div>
  </aside>
</main>
<script>
const img = document.getElementById('screen');
const statusEl = document.getElementById('status');
let naturalW = 1080, naturalH = 1920;

function status(text) { statusEl.textContent = text; }
async function post(path, body = {}) {
  status('Working...');
  const res = await fetch(path, { method: 'POST', body: JSON.stringify(body) });
  if (!res.ok) status(await res.text());
  else status('OK');
  setTimeout(refresh, 250);
}
async function refresh() {
  img.src = '/screen.png?ts=' + Date.now();
}
img.onload = () => { naturalW = img.naturalWidth; naturalH = img.naturalHeight; };
img.onclick = (ev) => {
  const rect = img.getBoundingClientRect();
  const x = Math.round((ev.clientX - rect.left) * naturalW / rect.width);
  const y = Math.round((ev.clientY - rect.top) * naturalH / rect.height);
  post('/tap', { x, y });
};
function key(code) { post('/key', { code }); }
function swipe(x1, y1, x2, y2, ms) { post('/swipe', { x1, y1, x2, y2, ms }); }
function launchPlay() { post('/launch-play'); }
function typeText() { post('/text', { text: document.getElementById('text').value }); }
refresh();
setInterval(refresh, 2000);
</script>
</body>
</html>
"""


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        path = urllib.parse.urlparse(self.path).path
        if path == "/":
            self.send(200, "text/html; charset=utf-8", INDEX.encode())
            return
        if path == "/screen.png":
            try:
                png = adb("exec-out", "screencap", "-p")
                self.send(200, "image/png", png)
            except subprocess.CalledProcessError as exc:
                self.send(500, "text/plain", exc.stderr)
            return
        self.send(404, "text/plain", b"not found")

    def do_POST(self):
        length = int(self.headers.get("content-length", "0"))
        body = json.loads(self.rfile.read(length) or b"{}")
        path = urllib.parse.urlparse(self.path).path
        try:
            if path == "/tap":
                adb("shell", "input", "tap", str(body["x"]), str(body["y"]))
            elif path == "/swipe":
                adb(
                    "shell",
                    "input",
                    "swipe",
                    str(body["x1"]),
                    str(body["y1"]),
                    str(body["x2"]),
                    str(body["y2"]),
                    str(body.get("ms", 300)),
                )
            elif path == "/key":
                adb("shell", "input", "keyevent", str(body["code"]))
            elif path == "/launch-play":
                adb("shell", "monkey", "-p", "com.android.vending", "1")
            elif path == "/text":
                text = str(body.get("text", "")).replace(" ", "%s")
                adb("shell", "input", "text", text)
            else:
                self.send(404, "text/plain", b"not found")
                return
            self.send(204, "text/plain", b"")
        except (KeyError, subprocess.CalledProcessError) as exc:
            message = getattr(exc, "stderr", b"bad request") or str(exc).encode()
            self.send(500, "text/plain", message)

    def log_message(self, fmt, *args):
        return

    def send(self, code, content_type, body):
        self.send_response(code)
        self.send_header("content-type", content_type)
        self.send_header("cache-control", "no-store")
        self.send_header("content-length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)


if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", 8090), Handler).serve_forever()
