const electron = require('electron');
const fs = require('fs');
const path = require('path');
const os = require('os');
const childProcess = require('child_process');
const crypto = require('crypto');
const dgram = require('dgram');
const net = require('net');
const robot = require('robotjs');
const keycode = require('keycode');

const settings = require('./settings');
const log = require('./log');
const eventCaptureModule = require('./event-capture');
const eventCapture = eventCaptureModule.default || eventCaptureModule;
const {
  adjacentRect,
  boundaryMemberForEdge,
  latestLayoutForHost,
  selectEdgeCandidate,
} = require(path.join(__dirname, 'app-ui', 'display-layout'));
const {
  CLIPBOARD_COPY_SHORTCUT,
  CLIPBOARD_PASTE_SHORTCUT,
  DEFAULT_RELEASE_SEQUENCE,
  clipboardShortcutForCode,
  createSequenceMatcher,
  formatKeySequence,
  isClipboardTransferShortcut,
  normalizeKeySequence,
  parseKeySequence,
} = require('./shortcut');
const {
  connectionStatus,
} = require(path.join(__dirname, 'connection-status'));
const {
  connectionsEnabled,
  disconnectConnections,
} = require(path.join(__dirname, 'connection-mode'));
const pairingSecurity = require(path.join(__dirname, 'pairing-security'));

const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const ipcMain = electron.ipcMain;
const powerSaveBlocker = electron.powerSaveBlocker;
const systemPreferences = electron.systemPreferences;
const electronScreen = electron.screen;
const globalShortcut = electron.globalShortcut;
const clipboard = electron.clipboard;
const Menu = electron.Menu;
const Tray = electron.Tray;
const nativeImage = electron.nativeImage;

const DISCOVERY_PORT = Number(settings.discoveryPort || settings.port || 5872);
const TCP_PORT = Number(settings.tcpPort || 5873);
const DISCOVERY_INTERVAL_MS = 2000;
const UDP_REACHABLE_WINDOW_MS = DISCOVERY_INTERVAL_MS * 4;
const POINTER_POLL_MS = 4;
const RELEASE_SEQUENCE_TIMEOUT_MS = 900;
const CONTROL_HEARTBEAT_MS = 500;
const REMOTE_INPUT_TIMEOUT_MS = 10000;
const LATENCY_PROBE_INTERVAL_MS = 1000;
const LATENCY_PROBE_TIMEOUT_MS = 3000;
const LATENCY_MAX_CONSECUTIVE_MISSES = 3;
const PREFERRED_PATH_CHECK_MS = 1500;
const INPUT_RATE_WINDOW_MS = 1000;
const COORD_FLUSH_FAST_MS = 4;
const COORD_FLUSH_SMOOTH_MS = 8;
const COORD_FAST_DELTA_PX = 6;
const COORD_BACKPRESSURE_BYTES = 16 * 1024;
const MOTION_TRACE_INTERVAL_MS = 2000;
const MOTION_FRAME_GAP_MS = 16;
const REMOTE_RENDER_INTERVAL_MS = 4;
const REMOTE_RENDER_FAST_SETTLE_MS = 8;
const REMOTE_RENDER_SMOOTH_SETTLE_MS = 12;
const REMOTE_RENDER_FAST_DISTANCE_PX = 12;
const RETURN_EDGE_REARM_INSET = 8;
const MAX_CLIPBOARD_TEXT_BYTES = 1024 * 1024;
const CLIPBOARD_COPY_DELAY_MS = 180;
const INTERFACE_CACHE_MS = 3000;
const DISPLAY_CACHE_MS = 1000;
const SHORTCUT_DEDUP_MS = 40;
const PAIRING_TIMEOUT_MS = 2 * 60 * 1000;
const ESCAPE_KEY_CODE = 27;
const LEGACY_DEFAULT_RELEASE_SEQUENCE = Object.freeze([ESCAPE_KEY_CODE, ESCAPE_KEY_CODE]);
const CENTER = { x: 500, y: 500 };
const MENU_BAR_ICON_DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAUklEQVR4nO3UMQoAIAwEwfz/01paWURPzckuWEaGIEYQjZrg/AG4PQvAE3DkwVoCFLMAAACoB5B+LlaATDuXeAEUK/cGrMJUD7YugGY9X3ka0AFMUgkGI58thwAAAABJRU5ErkJggg==';

let mainWindow = null;
let menuBarItem = null;
let discoverySocket = null;
let tcpServer = null;
let store = null;
let pointerTimer = null;
let latencyProbeTimer = null;
let preferredPathTimer = null;
let mode = 'onscreen';
let activePeerId = null;
let activeTargetId = null;
let activeEdge = null;
let activeLocalBounds = null;
let remoteX = 0;
let remoteY = 0;
let activeRemoteBounds = null;
let oldPos = { x: -1, y: -1 };
let suppressedKeyups = {};
let lastEdgeLogAt = 0;
let lastControlBlockedLogAt = 0;
let lastIgnoredInputLogAt = 0;
let inboundControlPeerId = null;
let inboundControlUntil = 0;
let edgeSuppressedUntil = 0;
let nextControlHeartbeatAt = 0;
let remoteShortcutsRegistered = false;
let lastRemoteShortcutName = null;
let lastRemoteShortcutAt = 0;
let lastLocalClipboardHash = null;
let cachedInterfaces = null;
let cachedInterfacesAt = 0;
let cachedDisplays = null;
let cachedDisplaysAt = 0;
let nativeKeyTap = null;
let nativeKeyTapBuffer = '';
let nativePointerCaptureReady = false;
let nativeKeySend = null;
let nativeMouseSend = null;

const peers = {};
const connecting = {};
const remoteKeyStates = {};
const remoteMouseStates = {};
const pairingSessions = {};

const possibleModifiers = {
  18: 'alt',
  91: 'command',
  93: 'command',
  17: 'control',
  16: 'shift',
};

const MODIFIER_NAMES = ['command', 'control', 'alt', 'shift'];
const MAC_MODIFIER_KEY_CODES = {
  command: 55,
  shift: 56,
  alt: 58,
  control: 59,
};
const MAC_KEY_CODES = {
  a: 0,
  s: 1,
  d: 2,
  f: 3,
  h: 4,
  g: 5,
  z: 6,
  x: 7,
  c: 8,
  v: 9,
  b: 11,
  q: 12,
  w: 13,
  e: 14,
  r: 15,
  y: 16,
  t: 17,
  '1': 18,
  '2': 19,
  '3': 20,
  '4': 21,
  '6': 22,
  '5': 23,
  '=': 24,
  '9': 25,
  '7': 26,
  '-': 27,
  '8': 28,
  '0': 29,
  ']': 30,
  o: 31,
  u: 32,
  '[': 33,
  i: 34,
  p: 35,
  enter: 36,
  l: 37,
  j: 38,
  quote: 39,
  "'": 39,
  k: 40,
  ';': 41,
  backslash: 42,
  '\\': 42,
  ',': 43,
  '/': 44,
  n: 45,
  m: 46,
  '.': 47,
  tab: 48,
  space: 49,
  '`': 50,
  backspace: 51,
  escape: 53,
  delete: 117,
  home: 115,
  end: 119,
  pageup: 116,
  pagedown: 121,
  left: 123,
  right: 124,
  down: 125,
  up: 126,
};

const modifiers = {};
const releaseMatcher = createSequenceMatcher(() => store && store.releaseSequence, {
  timeoutMs: RELEASE_SEQUENCE_TIMEOUT_MS,
});

powerSaveBlocker.start('prevent-app-suspension');

function normalizeRobotKey(name) {
  name = String(name || '').toLowerCase();
  if (name === 'left command' || name === 'right command' || name === 'cmd' || name === 'meta') {
    return 'command';
  }
  if (name === 'ctrl') {
    return 'control';
  }
  if (name === 'option') {
    return 'alt';
  }
  if (name === 'esc') {
    return 'escape';
  }
  if (name === 'return') {
    return 'enter';
  }
  if (name === 'del') {
    return 'delete';
  }
  if (name === ' ') {
    return 'space';
  }
  return name;
}

function keyCodeFromEvent(event) {
  if (typeof event === 'object' && event !== null) {
    return Number(event.code || event.keyCode || event.c || 0);
  }
  return Number(event || 0);
}

function keyNameFromCode(code) {
  if (possibleModifiers[code]) {
    return possibleModifiers[code];
  }
  return normalizeRobotKey(keycode(code));
}

function keyNameFromEvent(event) {
  if (typeof event === 'object' && event !== null && event.key) {
    return normalizeRobotKey(event.key);
  }
  return keyNameFromCode(keyCodeFromEvent(event));
}

function macKeyCodeFromName(name) {
  const normalized = normalizeRobotKey(name);
  if (isModifierName(normalized)) {
    return MAC_MODIFIER_KEY_CODES[normalized];
  }
  return MAC_KEY_CODES[normalized];
}

function macKeyCodeFromEvent(event) {
  if (typeof event === 'object' && event !== null) {
    const code = event.macCode !== undefined ? event.macCode : event.mac;
    if (code !== undefined && Number.isFinite(Number(code))) {
      return Number(code);
    }
  }
  return macKeyCodeFromName(keyNameFromEvent(event));
}

function macKeyCodeFromFrame(frame) {
  if (frame && frame.mac !== undefined && Number.isFinite(Number(frame.mac))) {
    return Number(frame.mac);
  }
  if (frame && frame.macCode !== undefined && Number.isFinite(Number(frame.macCode))) {
    return Number(frame.macCode);
  }
  return macKeyCodeFromName(keyNameFromFrame(frame));
}

function isNativeKeyTapEvent(event) {
  return typeof event === 'object' && event !== null &&
    (event.macCode !== undefined || event.mac !== undefined);
}

function activeModifierNames() {
  const names = [];
  Object.keys(modifiers).forEach(code => {
    const name = possibleModifiers[code];
    if (name && names.indexOf(name) === -1) {
      names.push(name);
    }
  });
  return names;
}

function isModifierActive(name) {
  return activeModifierNames().indexOf(name) !== -1;
}

function sequenceEquals(sequence, expected) {
  return sequence.length === expected.length &&
    sequence.every((code, index) => code === expected[index]);
}

function escapeOnlyReleaseSequence(sequence) {
  const normalized = normalizeKeySequence(sequence, DEFAULT_RELEASE_SEQUENCE);
  return normalized.length > 1 &&
    normalized.every(code => code === ESCAPE_KEY_CODE);
}

function shouldPassPartialReleaseKey(code, release) {
  return code === ESCAPE_KEY_CODE &&
    release.consume &&
    !release.matched &&
    escapeOnlyReleaseSequence(store && store.releaseSequence);
}

function userDataPath() {
  return path.join(app.getPath('userData'), 'openmouse.json');
}

function readStore() {
  const fallback = {
    hostId: `${os.hostname()}-${Math.random().toString(36).slice(2)}-${Date.now()}`,
    nickname: os.hostname(),
    layout: {},
    autoConnect: true,
    directPeerAddresses: [],
    pairedPeers: {},
    releaseSequence: DEFAULT_RELEASE_SEQUENCE.slice(),
  };

  try {
    const data = JSON.parse(fs.readFileSync(userDataPath(), 'utf8'));
    const releaseSequence = normalizeKeySequence(data.releaseSequence, DEFAULT_RELEASE_SEQUENCE);
    return Object.assign(fallback, data, {
      layout: Object.assign({}, fallback.layout, data.layout || {}),
      pairedPeers: Object.assign({}, fallback.pairedPeers, data.pairedPeers || {}),
      directPeerAddresses: Array.isArray(data.directPeerAddresses)
        ? data.directPeerAddresses.filter(isLinkLocalAddress)
        : [],
      releaseSequence: sequenceEquals(releaseSequence, LEGACY_DEFAULT_RELEASE_SEQUENCE)
        ? DEFAULT_RELEASE_SEQUENCE.slice()
        : releaseSequence,
    });
  } catch (error) {
    return fallback;
  }
}

function writeStore() {
  fs.mkdirSync(app.getPath('userData'), { recursive: true });
  fs.writeFileSync(userDataPath(), JSON.stringify(store, null, 2), { mode: 0o600 });
  try {
    fs.chmodSync(userDataPath(), 0o600);
  } catch (error) {
    log('STORE_PERMISSION_ERROR', error.message);
  }
}

function ipv4ToInt(ip) {
  return ip.split('.').reduce((value, part) => ((value << 8) + Number(part)) >>> 0, 0);
}

function intToIpv4(value) {
  return [24, 16, 8, 0].map(shift => (value >>> shift) & 255).join('.');
}

function cleanAddress(address) {
  return String(address || '').replace(/^::ffff:/, '');
}

function interfaceStatuses() {
  const statuses = {};
  try {
    const output = childProcess.execFileSync('/sbin/ifconfig', ['-a'], { encoding: 'utf8' });
    let current = null;
    output.split('\n').forEach(line => {
      const header = line.match(/^([a-z0-9]+):/i);
      if (header) {
        current = header[1];
        return;
      }
      const status = line.match(/\bstatus:\s+([a-z]+)/i);
      if (current && status) {
        statuses[current] = status[1].toLowerCase();
      }
    });
  } catch (error) {
    return statuses;
  }
  return statuses;
}

function localInterfaces() {
  const now = Date.now();
  if (cachedInterfaces && now - cachedInterfacesAt < INTERFACE_CACHE_MS) {
    return cachedInterfaces;
  }

  const result = [];
  const ifaces = os.networkInterfaces();
  const statuses = interfaceStatuses();

  Object.keys(ifaces).forEach(name => {
    if (statuses[name] === 'inactive') {
      return;
    }
    ifaces[name].forEach(iface => {
      if (iface.family !== 'IPv4' || iface.internal) {
        return;
      }
      const netmask = iface.netmask || '255.255.255.0';
      const broadcast = intToIpv4((ipv4ToInt(iface.address) | (~ipv4ToInt(netmask))) >>> 0);
      result.push({
        name,
        address: iface.address,
        netmask,
        broadcast,
      });
    });
  });

  cachedInterfaces = result;
  cachedInterfacesAt = now;
  return result;
}

function localScreen() {
  try {
    return robot.getScreenSize();
  } catch (error) {
    return { width: 1440, height: 900 };
  }
}

function normalizeBounds(bounds, fallback) {
  const source = bounds || fallback || {};
  return {
    x: Number(source.x || 0),
    y: Number(source.y || 0),
    width: Math.max(1, Number(source.width || 0)),
    height: Math.max(1, Number(source.height || 0)),
  };
}

function boundsRight(bounds) {
  return bounds.x + bounds.width;
}

function boundsBottom(bounds) {
  return bounds.y + bounds.height;
}

function clampCoordinate(value, min, max) {
  return Math.max(min, Math.min(max, value));
}

function copyDisplay(display) {
  return Object.assign({}, display, {
    id: String(display.id || 'main'),
    bounds: normalizeBounds(display.bounds),
    workArea: display.workArea ? normalizeBounds(display.workArea) : undefined,
  });
}

function copyDisplays(displays) {
  return (displays || []).map(copyDisplay);
}

function displayUnion(displays) {
  const bounds = (displays || [])
    .map(display => display && display.bounds)
    .filter(Boolean)
    .map(bound => normalizeBounds(bound));

  if (!bounds.length) {
    return null;
  }

  const minX = Math.min.apply(null, bounds.map(bound => bound.x));
  const minY = Math.min.apply(null, bounds.map(bound => bound.y));
  const maxX = Math.max.apply(null, bounds.map(bound => boundsRight(bound)));
  const maxY = Math.max.apply(null, bounds.map(bound => boundsBottom(bound)));
  return {
    x: minX,
    y: minY,
    width: Math.max(1, maxX - minX),
    height: Math.max(1, maxY - minY),
  };
}

function edgeOpposite(edge) {
  return {
    left: 'right',
    right: 'left',
    top: 'bottom',
    bottom: 'top',
  }[edge] || null;
}

function displayForId(displays, displayId) {
  const id = String(displayId || '');
  return (displays || []).find(display => String(display.id) === id) || null;
}

function defaultDisplayForEdge(edge, displays) {
  const candidates = copyDisplays(displays);
  if (!candidates.length) {
    return null;
  }

  return candidates.sort((a, b) => {
    if (edge === 'left') {
      return a.bounds.x - b.bounds.x || a.bounds.y - b.bounds.y;
    }
    if (edge === 'right') {
      return boundsRight(b.bounds) - boundsRight(a.bounds) || a.bounds.y - b.bounds.y;
    }
    if (edge === 'top') {
      return a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x;
    }
    if (edge === 'bottom') {
      return boundsBottom(b.bounds) - boundsBottom(a.bounds) || a.bounds.x - b.bounds.x;
    }
    return 0;
  })[0];
}

function defaultSourceDisplayIdForEdge(edge, displays) {
  const display = defaultDisplayForEdge(edge, displays || localDisplays());
  return display ? String(display.id) : null;
}

function localWorkspaceBounds() {
  const union = displayUnion(localDisplays());
  if (union) {
    return union;
  }
  const screen = localScreen();
  return { x: 0, y: 0, width: screen.width, height: screen.height };
}

function invalidateDisplayCache() {
  cachedDisplays = null;
  cachedDisplaysAt = 0;
}

function scaleBounds(bounds, scaleFactor) {
  return {
    x: Math.round((bounds && bounds.x) || 0),
    y: Math.round((bounds && bounds.y) || 0),
    width: Math.round((bounds && bounds.width) || 0),
    height: Math.round((bounds && bounds.height) || 0),
  };
}

function localDisplays() {
  const now = Date.now();
  if (cachedDisplays && now - cachedDisplaysAt < DISPLAY_CACHE_MS) {
    return copyDisplays(cachedDisplays);
  }

  try {
    const primary = electronScreen.getPrimaryDisplay();
    const displays = electronScreen.getAllDisplays();
    if (displays && displays.length) {
      cachedDisplays = displays.map((display, index) => ({
        id: String(display.id || index + 1),
        name: display.label || `Display ${index + 1}`,
        bounds: scaleBounds(display.bounds, display.scaleFactor),
        workArea: scaleBounds(display.workArea, display.scaleFactor),
        scaleFactor: display.scaleFactor || 1,
        primary: primary && display.id === primary.id,
      }));
      cachedDisplaysAt = now;
      return copyDisplays(cachedDisplays);
    }
  } catch (error) {
    // Electron's screen module is only available after app ready.
  }

  const screen = localScreen();
  cachedDisplays = [{
    id: 'main',
    name: 'Display 1',
    bounds: { x: 0, y: 0, width: screen.width, height: screen.height },
    workArea: { x: 0, y: 0, width: screen.width, height: screen.height },
    scaleFactor: 1,
    primary: true,
  }];
  cachedDisplaysAt = now;
  return copyDisplays(cachedDisplays);
}

function localInfo() {
  if (!store.identity) {
    pairingSecurity.ensureIdentity(store);
  }
  return {
    app: 'openmouse',
    protocol: 8,
    hostId: store.hostId,
    nickname: store.nickname,
    publicKeyPem: store.identity.publicKeyPem,
    fingerprint: store.identity.fingerprint,
    tcpPort: TCP_PORT,
    discoveryPort: DISCOVERY_PORT,
    screen: localScreen(),
    displays: localDisplays(),
    layout: layoutEntriesForBroadcast(),
    permissions: {
      accessibility: accessibilityTrusted(false),
    },
    interfaces: localInterfaces(),
  };
}

function createPeerMetrics() {
  const now = Date.now();
  return {
    latency: {
      rttMs: null,
      minRttMs: null,
      maxRttMs: null,
      avgRttMs: null,
      samples: 0,
      sent: 0,
      received: 0,
      missed: 0,
      consecutiveMisses: 0,
      pendingId: null,
      pendingSentAt: 0,
      lastProbeAt: 0,
      lastPongAt: 0,
    },
    input: {
      total: 0,
      coord: 0,
      key: 0,
      mouse: 0,
      wheel: 0,
      shortcut: 0,
      windowStartedAt: now,
      windowTotal: 0,
      windowCoord: 0,
      eventsPerSecond: 0,
      coordPerSecond: 0,
      updatedAt: 0,
    },
  };
}

function ensurePeerMetrics(peer) {
  if (!peer.metrics) {
    peer.metrics = createPeerMetrics();
  }
  return peer.metrics;
}

function inputKindForFrame(type) {
  if (type === 'coord') {
    return 'coord';
  }
  if (type === 'kd' || type === 'ku') {
    return 'key';
  }
  if (type === 'md' || type === 'mu') {
    return 'mouse';
  }
  if (type === 'wh') {
    return 'wheel';
  }
  if (type === 'shortcut') {
    return 'shortcut';
  }
  return null;
}

function noteInputFrameSent(hostId, type) {
  const peer = peers[hostId];
  const kind = inputKindForFrame(type);
  if (!peer || !kind) {
    return;
  }

  const input = ensurePeerMetrics(peer).input;
  const now = Date.now();
  const elapsed = now - input.windowStartedAt;
  if (elapsed >= INPUT_RATE_WINDOW_MS) {
    input.eventsPerSecond = Math.round((input.windowTotal * 1000) / Math.max(1, elapsed));
    input.coordPerSecond = Math.round((input.windowCoord * 1000) / Math.max(1, elapsed));
    input.windowStartedAt = now;
    input.windowTotal = 0;
    input.windowCoord = 0;
  }

  input.total += 1;
  input[kind] += 1;
  input.windowTotal += 1;
  if (kind === 'coord') {
    input.windowCoord += 1;
  }
  input.updatedAt = now;
}

function metricsSnapshot(peer) {
  const metrics = ensurePeerMetrics(peer);
  const now = Date.now();
  if (metrics.input.updatedAt && now - metrics.input.updatedAt > INPUT_RATE_WINDOW_MS * 2) {
    metrics.input.eventsPerSecond = 0;
    metrics.input.coordPerSecond = 0;
  }

  const latency = metrics.latency;
  return {
    latency: {
      rttMs: latency.rttMs,
      minRttMs: latency.minRttMs,
      maxRttMs: latency.maxRttMs,
      avgRttMs: latency.avgRttMs,
      samples: latency.samples,
      sent: latency.sent,
      received: latency.received,
      missed: latency.missed,
      consecutiveMisses: latency.consecutiveMisses,
      pending: !!latency.pendingId,
      stale: !!(latency.sent && (!latency.lastPongAt || now - latency.lastPongAt > LATENCY_PROBE_TIMEOUT_MS)),
      lastProbeAt: latency.lastProbeAt,
      lastPongAt: latency.lastPongAt,
    },
    input: {
      total: metrics.input.total,
      eventsPerSecond: metrics.input.eventsPerSecond,
      coordPerSecond: metrics.input.coordPerSecond,
      updatedAt: metrics.input.updatedAt,
    },
  };
}

function accessibilityTrusted(prompt) {
  if (process.platform !== 'darwin') {
    return true;
  }
  if (!systemPreferences || !systemPreferences.isTrustedAccessibilityClient) {
    return false;
  }
  return systemPreferences.isTrustedAccessibilityClient(!!prompt);
}

function pairedPeerRecord(hostId) {
  return store && store.pairedPeers && store.pairedPeers[hostId] || null;
}

function peerIdentityMatches(peer) {
  const trusted = peer && pairedPeerRecord(peer.hostId);
  if (!trusted || !peer.publicKeyPem || trusted.publicKeyPem !== peer.publicKeyPem) {
    return false;
  }
  try {
    return trusted.fingerprint === pairingSecurity.publicKeyFingerprint(peer.publicKeyPem);
  } catch (error) {
    return false;
  }
}

function isPeerPaired(peer) {
  return !!(peer && pairedPeerRecord(peer.hostId) && peerIdentityMatches(peer));
}

function peerConnectionEnabled(peer) {
  const trusted = peer && pairedPeerRecord(peer.hostId);
  return !!(trusted && trusted.enabled !== false);
}

function pairingSessionForPeer(hostId) {
  const session = pairingSessions[hostId];
  if (!session) {
    return null;
  }
  if (session.expiresAt <= Date.now()) {
    delete pairingSessions[hostId];
    if (session.socket &&
        !session.socket.destroyed &&
        !session.socket.openmouseAuthenticated) {
      session.socket.destroy();
    }
    return null;
  }
  return session;
}

function pairingSnapshot(hostId) {
  const session = pairingSessionForPeer(hostId);
  if (!session) {
    return null;
  }
  return {
    state: session.state,
    code: session.code || null,
    expiresAt: session.expiresAt,
    localConfirmed: !!session.localConfirmed,
    remoteConfirmed: !!session.remoteConfirmed,
    message: session.message || null,
  };
}

function snapshot() {
  const releaseSequence = normalizeKeySequence(store.releaseSequence, DEFAULT_RELEASE_SEQUENCE);
  return {
    local: localInfo(),
    tcpPort: TCP_PORT,
    discoveryPort: DISCOVERY_PORT,
    connectionsEnabled: connectionsEnabled(store),
    mode,
    activePeerId,
    activeTargetId,
    inboundControlPeerId,
    layout: store.layout,
    releaseSequence,
    releaseShortcut: formatKeySequence(releaseSequence),
    permissions: {
      accessibility: accessibilityTrusted(false),
    },
    peers: Object.keys(peers).map(id => {
      const peer = peers[id];
      return {
        hostId: id,
        nickname: peer.nickname,
        fingerprint: peer.fingerprint,
        shortFingerprint: pairingSecurity.shortFingerprint(peer.fingerprint),
        paired: isPeerPaired(peer),
        connectionEnabled: peerConnectionEnabled(peer),
        identityChanged: !!peer.identityChanged,
        pairing: pairingSnapshot(id),
        protocol: peer.protocol,
        screen: peer.screen,
        displays: peer.displays || [],
        layout: peer.layout || {},
        permissions: peer.permissions || {},
        tcpPort: peer.tcpPort,
        lastAddress: peer.lastAddress,
        connectedAddress: peer.connectedAddress,
        localAddress: peer.localAddress,
        remoteAddress: peer.remoteAddress,
        networkPath: networkPathForPeer(peer),
        metrics: metricsSnapshot(peer),
        interfaces: peer.interfaces || [],
        connected: !!(peer.socket && !peer.socket.destroyed),
        lastSeen: peer.lastSeen,
        edge: store.layout[id] && store.layout[id].edge,
      };
    }).sort((a, b) => a.nickname.localeCompare(b.nickname)),
    targets: displayTargets(),
  };
}

function displayTargetId(hostId, displayId) {
  return `${hostId}::${displayId || 'main'}`;
}

function parseTargetId(targetId) {
  const parts = String(targetId || '').split('::');
  return {
    hostId: parts[0],
    displayId: parts[1] || null,
  };
}

function primaryDisplay(displays) {
  return (displays || []).find(display => display.primary) || (displays || [])[0] || null;
}

function normalizeLayoutEntry(entry, displays) {
  if (!entry) {
    return null;
  }

  const raw = typeof entry === 'string' ? { edge: entry } : entry;
  if (!raw || !raw.edge) {
    return null;
  }

  const normalized = Object.assign({}, raw, {
    edge: raw.edge,
    crossOffset: Number.isFinite(Number(raw.crossOffset)) ? Number(raw.crossOffset) : 0,
    sourceDisplayId: raw.sourceDisplayId
      ? String(raw.sourceDisplayId)
      : defaultSourceDisplayIdForEdge(raw.edge, displays || localDisplays()),
  });
  return normalized;
}

function activeLayoutForDisplays(hostId, displays) {
  const latest = latestLayoutForHost(store.layout, hostId);
  if (!latest) {
    return null;
  }
  const entry = normalizeLayoutEntry(latest.entry);
  const boundaryDisplay = boundaryMemberForEdge(
    (displays || []).map(display => ({
      id: displayTargetId(hostId, display.id),
      rect: display.bounds,
    })),
    entry.edge
  );
  return boundaryDisplay && {
    targetId: boundaryDisplay.id,
    entry,
  };
}

function layoutHostId(layoutId) {
  if (store.layout[layoutId] && String(layoutId).indexOf('::') === -1) {
    return layoutId;
  }
  return parseTargetId(layoutId).hostId;
}

function hasLayoutForPeer(hostId) {
  return Object.keys(store.layout || {}).some(layoutId => {
    const entry = normalizeLayoutEntry(store.layout[layoutId]);
    return entry && layoutHostId(layoutId) === hostId;
  });
}

function layoutEntriesForBroadcast() {
  const displays = localDisplays();
  const result = {};
  const visitedHosts = {};
  Object.keys(store.layout || {}).forEach(layoutId => {
    const hostId = layoutHostId(layoutId);
    if (visitedHosts[hostId]) {
      return;
    }
    visitedHosts[hostId] = true;
    const latest = latestLayoutForHost(store.layout, hostId);
    const entry = latest && normalizeLayoutEntry(latest.entry, displays);
    if (!entry) {
      return;
    }
    result[latest.layoutId] = entry;
  });
  return result;
}

function sameLayoutPlacement(a, b) {
  return !!a && !!b &&
    a.edge === b.edge &&
    String(a.sourceDisplayId || '') === String(b.sourceDisplayId || '') &&
    Number(a.crossOffset || 0) === Number(b.crossOffset || 0);
}

function shouldAdoptSharedLayout(current, next) {
  if (!current) {
    return true;
  }
  if (current.sharedFromHostId && current.sharedFromHostId === next.sharedFromHostId) {
    return !sameLayoutPlacement(current, next);
  }

  const currentUpdatedAt = Number(current.updatedAt || 0);
  const nextUpdatedAt = Number(next.updatedAt || 0);
  return !!nextUpdatedAt && (!currentUpdatedAt || nextUpdatedAt > currentUpdatedAt);
}

function adoptSharedLayoutsFromPeer(peer, remoteLayout) {
  if (!peer || !remoteLayout) {
    return false;
  }

  let changed = false;
  const localDisplaysNow = localDisplays();
  const localPrimary = primaryDisplay(localDisplaysNow);
  Object.keys(remoteLayout).forEach(layoutId => {
    const remoteEntry = normalizeLayoutEntry(remoteLayout[layoutId], peer.displays || []);
    if (!remoteEntry || remoteEntry.auto) {
      return;
    }

    const parsed = parseTargetId(layoutId);
    const targetsLocalDisplay = layoutId === store.hostId || parsed.hostId === store.hostId;
    if (!targetsLocalDisplay) {
      return;
    }

    const localSourceDisplayId = parsed.displayId || (localPrimary && localPrimary.id);
    const remoteSourceDisplayId = remoteEntry.sourceDisplayId ||
      defaultSourceDisplayIdForEdge(remoteEntry.edge, peer.displays || []);
    const reverseEdge = edgeOpposite(remoteEntry.edge);
    if (!localSourceDisplayId || !remoteSourceDisplayId || !reverseEdge) {
      return;
    }

    const reverseTargetId = displayTargetId(peer.hostId, remoteSourceDisplayId);
    const next = {
      edge: reverseEdge,
      crossOffset: -Number(remoteEntry.crossOffset || 0),
      sourceDisplayId: String(localSourceDisplayId),
      updatedAt: Number(remoteEntry.updatedAt || 0),
      sharedFromHostId: peer.hostId,
    };
    const current = normalizeLayoutEntry(store.layout[reverseTargetId], localDisplaysNow);
    if (!shouldAdoptSharedLayout(current, next)) {
      return;
    }

    removeLayoutsForHost(peer.hostId, reverseTargetId);
    store.layout[reverseTargetId] = next;
    changed = true;
  });

  return changed;
}

function forgetPeerLayout(hostId) {
  Object.keys(store.layout || {}).forEach(layoutId => {
    if (layoutHostId(layoutId) === hostId) {
      delete store.layout[layoutId];
    }
  });
}

function removeLayoutsForHost(hostId, keepLayoutId) {
  Object.keys(store.layout || {}).forEach(layoutId => {
    if (layoutId !== keepLayoutId && layoutHostId(layoutId) === hostId) {
      delete store.layout[layoutId];
    }
  });
}

function displayTargets() {
  const result = [];
  Object.keys(peers).forEach(hostId => {
    const peer = peers[hostId];
    if (!isPeerPaired(peer)) {
      return;
    }
    const displays = peer.displays && peer.displays.length
      ? peer.displays
      : [{
        id: 'main',
        name: 'Display 1',
        bounds: { x: 0, y: 0, width: peer.screen.width, height: peer.screen.height },
        primary: true,
      }];
    const activeLayout = activeLayoutForDisplays(hostId, displays);

    displays.forEach((display, index) => {
      display = copyDisplay(display);
      const targetId = displayTargetId(hostId, display.id);
      const layoutEntry = activeLayout && activeLayout.targetId === targetId
        ? activeLayout.entry
        : null;
      result.push({
        targetId,
        hostId,
        displayId: String(display.id),
        nickname: `${peer.nickname} ${display.name || `Display ${index + 1}`}`,
        peerNickname: peer.nickname,
        displayName: display.name || `Display ${index + 1}`,
        bounds: display.bounds,
        screen: {
          width: display.bounds.width,
          height: display.bounds.height,
        },
        primary: !!display.primary,
        protocol: peer.protocol,
        permissions: peer.permissions || {},
        connected: !!(peer.socket && !peer.socket.destroyed),
        lastAddress: peer.lastAddress,
        connectedAddress: peer.connectedAddress,
        networkPath: networkPathForPeer(peer),
        metrics: metricsSnapshot(peer),
        edge: layoutEntry && layoutEntry.edge,
        crossOffset: layoutEntry && layoutEntry.crossOffset,
        sourceDisplayId: layoutEntry && layoutEntry.sourceDisplayId,
        layoutUpdatedAt: layoutEntry && layoutEntry.updatedAt,
      });
    });
  });

  return result.sort((a, b) => a.nickname.localeCompare(b.nickname));
}

function pushState() {
  if (mainWindow && mainWindow.webContents) {
    mainWindow.webContents.send('state', snapshot());
  }
  updateMenuBar();
}

function connectedPeerCount() {
  return Object.keys(peers).filter(hostId => {
    const peer = peers[hostId];
    return peer && peer.socket && !peer.socket.destroyed;
  }).length;
}

function currentConnectionStatus() {
  return connectionStatus({
    connectionsEnabled: connectionsEnabled(store),
    connectedCount: connectedPeerCount(),
  });
}

function showMainWindow() {
  if (!mainWindow) {
    createWindow();
    return;
  }
  if (mainWindow.isMinimized && mainWindow.isMinimized()) {
    mainWindow.restore();
  }
  mainWindow.show();
  mainWindow.focus();
}

function updateMenuBar() {
  if (!menuBarItem || !Menu || !store) {
    return;
  }
  const status = currentConnectionStatus();
  menuBarItem.setToolTip(`OpenMouse · ${status}`);
  menuBarItem.setContextMenu(Menu.buildFromTemplate([
    {
      label: status,
      enabled: false,
    },
    { type: 'separator' },
    {
      label: 'Connect to Nearby Macs',
      type: 'checkbox',
      checked: connectionsEnabled(store),
      click: menuItem => setConnectionsEnabled(menuItem.checked),
    },
    {
      label: 'Open OpenMouse',
      click: showMainWindow,
    },
    { type: 'separator' },
    {
      label: 'Quit OpenMouse',
      click: () => app.quit(),
    },
  ]));
}

function createMenuBarItem() {
  if (!Tray || !nativeImage || menuBarItem) {
    return;
  }
  const image = nativeImage.createFromDataURL(MENU_BAR_ICON_DATA_URL);
  image.setTemplateImage(true);
  menuBarItem = new Tray(image);
  menuBarItem.setTitle('OpenMouse');
  menuBarItem.on('click', showMainWindow);
  const trayBounds = menuBarItem.getBounds ? menuBarItem.getBounds() : null;
  log(
    'MENU_BAR',
    image.isEmpty()
      ? 'icon failed to load'
      : `status item ready${trayBounds ? ` at ${trayBounds.x},${trayBounds.y} ${trayBounds.width}x${trayBounds.height}` : ''}`
  );
  updateMenuBar();
}

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 920,
    height: 620,
    minWidth: 760,
    minHeight: 520,
    title: 'OpenMouse',
    backgroundColor: '#f6f7f4',
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    },
  });

  mainWindow.loadFile(path.join(__dirname, 'app-ui', 'index.html'));
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

function rememberPeer(info, address) {
  if (!info || info.app !== 'openmouse' || info.hostId === store.hostId) {
    return null;
  }

  const remoteAddress = cleanAddress(address);
  const current = peers[info.hostId] || {};
  let publicKeyPem = info.publicKeyPem || current.publicKeyPem || null;
  let fingerprint = current.fingerprint || null;
  let invalidIdentity = false;
  if (publicKeyPem) {
    try {
      fingerprint = pairingSecurity.publicKeyFingerprint(publicKeyPem);
    } catch (error) {
      publicKeyPem = null;
      invalidIdentity = true;
    }
  }
  const trusted = pairedPeerRecord(info.hostId);
  const hasControlSocket = current.socket && !current.socket.destroyed;
  const peer = Object.assign(current, {
    hostId: info.hostId,
    nickname: info.nickname || info.hostId,
    tcpPort: Number(info.tcpPort || TCP_PORT),
    protocol: Number(info.protocol || 1),
    screen: info.screen || { width: 1440, height: 900 },
    displays: normalizeDisplays(info),
    layout: info.layout || {},
    permissions: info.permissions || {},
    interfaces: info.interfaces || [],
    publicKeyPem,
    fingerprint,
    invalidIdentity,
    identityChanged: !!(trusted && (
      !publicKeyPem ||
      trusted.publicKeyPem !== publicKeyPem ||
      trusted.fingerprint !== fingerprint
    )),
    discoveryAddress: remoteAddress || current.discoveryAddress,
    lastAddress: hasControlSocket ? current.lastAddress : (remoteAddress || current.lastAddress),
    lastSeen: Date.now(),
  });

  peers[info.hostId] = peer;

  let learnedDirectAddress = false;
  let adoptedSharedLayout = false;
  if (isPeerPaired(peer)) {
    learnedDirectAddress = rememberDirectPeerAddresses(info, remoteAddress);
    adoptedSharedLayout = adoptSharedLayoutsFromPeer(peer, info.layout);
    if (!hasLayoutForPeer(info.hostId)) {
      const edge = firstOpenEdge();
      store.layout[info.hostId] = {
        edge,
        sourceDisplayId: defaultSourceDisplayIdForEdge(edge),
        auto: true,
      };
      writeStore();
    } else if (adoptedSharedLayout || learnedDirectAddress) {
      writeStore();
    }
  }

  pushState();
  if (isPeerPaired(peer)) {
    maybeReconnectToBetterAdvertisedPath(peer);
  }
  return peer;
}

function normalizeDisplays(info) {
  if (info.displays && info.displays.length) {
    return info.displays.map((display, index) => {
      const bounds = display.bounds || { x: 0, y: 0, width: info.screen.width, height: info.screen.height };
      return {
        id: String(display.id || index + 1),
        name: display.name || `Display ${index + 1}`,
        bounds: normalizeBounds(bounds),
        workArea: display.workArea ? normalizeBounds(display.workArea) : undefined,
        scaleFactor: display.scaleFactor || 1,
        primary: !!display.primary,
      };
    });
  }

  return [{
    id: 'main',
    name: 'Display 1',
    bounds: { x: 0, y: 0, width: info.screen.width, height: info.screen.height },
    primary: true,
  }];
}

function firstOpenEdge() {
  const used = Object.keys(store.layout).map(id => store.layout[id].edge);
  return ['right', 'left', 'top', 'bottom'].find(edge => used.indexOf(edge) === -1) || 'right';
}

function startDiscovery() {
  discoverySocket = dgram.createSocket({ type: 'udp4', reuseAddr: true });

  discoverySocket.on('error', error => {
    log('DISCOVERY_ERROR', error.message);
    pushState();
  });

  discoverySocket.on('message', (buffer, rinfo) => {
    if (!connectionsEnabled(store)) {
      return;
    }
    let message;
    try {
      message = JSON.parse(buffer.toString('utf8'));
    } catch (error) {
      return;
    }

    if (message && message.app === 'openmouse' && message.type) {
      handleUdpFrame(message, rinfo);
      return;
    }

    const peer = rememberPeer(message, rinfo.address);
    if (peer) {
      peer.lastUdpAddress = rinfo.address;
      peer.udpReachableAt = Date.now();
    }
    if (peer && store.autoConnect && isPeerPaired(peer) && peerConnectionEnabled(peer)) {
      connectPeer(peer.hostId);
    }
  });

  discoverySocket.bind(DISCOVERY_PORT, '0.0.0.0', () => {
    discoverySocket.setBroadcast(true);
    broadcastPresence();
    setInterval(broadcastPresence, DISCOVERY_INTERVAL_MS);
    log('DISCOVERY', `broadcasting on UDP ${DISCOVERY_PORT}`);
  });
}

function handleUdpFrame(frame, rinfo) {
  if (!connectionsEnabled(store)) {
    return;
  }
  const peer = frame.hostId && peers[frame.hostId];
  if (!peer ||
      !isPeerPaired(peer) ||
      !peer.socket ||
      !peer.socket.openmouseSessionKey ||
      frame.type !== 'sealed' ||
      !frame.sealed) {
    return;
  }
  if (rinfo && rinfo.address) {
    peer.lastUdpAddress = rinfo.address;
    peer.udpReachableAt = Date.now();
  }
  const sequence = Number(frame.sealed.seq);
  if (!Number.isSafeInteger(sequence) || sequence <= Number(peer.udpRecvSeq || 0)) {
    return;
  }
  let clearFrame;
  try {
    clearFrame = pairingSecurity.openFrame(
      peer.socket.openmouseSessionKey,
      frame.sealed,
      {
        transport: 'udp',
        senderHostId: frame.hostId,
        receiverHostId: store.hostId,
      }
    );
  } catch (error) {
    log('UDP_SECURE_FRAME_ERROR', `${peer.nickname}: ${error.message}`);
    return;
  }
  peer.udpRecvSeq = sequence;
  if (clearFrame.type === 'coord') {
    if (!acceptRemoteInputFromPeer(frame.hostId, clearFrame.type)) {
      return;
    }
    remoteMouseMove(
      frame.hostId,
      clearFrame.x,
      clearFrame.y,
      clearFrame.seq,
      clearFrame.sampleAt
    );
  }
}

function broadcastPresence() {
  if (!connectionsEnabled(store) || !discoverySocket) {
    return;
  }

  const payload = Buffer.from(JSON.stringify(localInfo()));
  const broadcasts = ['255.255.255.255'];
  const interfaces = localInterfaces();
  interfaces.forEach(iface => {
    if (broadcasts.indexOf(iface.broadcast) === -1) {
      broadcasts.push(iface.broadcast);
    }
  });

  broadcasts.forEach(address => {
    discoverySocket.send(payload, 0, payload.length, DISCOVERY_PORT, address);
  });
  interfaces.forEach(iface => {
    broadcastPresenceFromInterface(payload, iface);
  });
}

function broadcastPresenceFromInterface(payload, iface) {
  if (!iface || !iface.address) {
    return;
  }

  const socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
  const close = () => {
    try {
      socket.close();
    } catch (error) {
      // The socket may already be closed after an interface disappears.
    }
  };

  socket.on('error', close);
  socket.bind(0, iface.address, () => {
    try {
      socket.setBroadcast(true);
      socket.send(
        payload,
        0,
        payload.length,
        DISCOVERY_PORT,
        '255.255.255.255',
        close
      );
    } catch (error) {
      close();
    }
  });
  if (socket.unref) {
    socket.unref();
  }
}

function startTcpServer() {
  tcpServer = net.createServer(socket => {
    if (!connectionsEnabled(store)) {
      socket.destroy();
      return;
    }
    wireSocket(socket, false);
  });

  tcpServer.on('error', error => {
    log('TCP_ERROR', error.message);
  });

  tcpServer.listen(TCP_PORT, '0.0.0.0', () => {
    log('TCP_LISTENING', `bound to port ${TCP_PORT}`);
    pushState();
  });
}

function candidateAddresses(peer, includeInferredDirect) {
  const result = [];
  if (includeInferredDirect !== false) {
    pairedDirectAddresses().forEach(address => {
      if (result.indexOf(address) === -1) {
        result.push(address);
      }
    });
  }
  (peer.interfaces || []).forEach(iface => {
    if (iface.address && result.indexOf(iface.address) === -1) {
      result.push(iface.address);
    }
  });
  if (peer.lastAddress && result.indexOf(peer.lastAddress) === -1) {
    result.push(peer.lastAddress);
  }
  if (peer.discoveryAddress && result.indexOf(peer.discoveryAddress) === -1) {
    result.push(peer.discoveryAddress);
  }
  return result
    .filter(address => address && address !== '0.0.0.0' && address !== '127.0.0.1')
    .sort((a, b) => addressRank(a, peer) - addressRank(b, peer));
}

function pairedDirectAddresses() {
  const result = [];
  localInterfaces().forEach(iface => {
    if (!isFastDirectInterface(iface)) {
      return;
    }

    const parts = iface.address.split('.');
    if (parts.length !== 4) {
      return;
    }

    const last = Number(parts[3]);
    if (last !== 1 && last !== 2) {
      return;
    }

    parts[3] = String(last === 1 ? 2 : 1);
    const address = parts.join('.');
    if (result.indexOf(address) === -1) {
      result.push(address);
    }
  });
  return result;
}

function isLinkLocalAddress(address) {
  return cleanAddress(address).indexOf('169.254.') === 0;
}

function isThunderboltInterface(iface) {
  return !!iface && iface.name.indexOf('bridge') === 0;
}

function rememberDirectPeerAddresses(info, remoteAddress) {
  if (!store) {
    return false;
  }
  const addresses = [remoteAddress].concat((info && info.interfaces || []).map(iface => iface.address));
  const remembered = Array.isArray(store.directPeerAddresses)
    ? store.directPeerAddresses.slice()
    : [];
  let changed = false;

  addresses.forEach(address => {
    address = cleanAddress(address);
    if (!isLinkLocalAddress(address) || remembered.indexOf(address) !== -1) {
      return;
    }
    remembered.push(address);
    changed = true;
  });

  if (changed) {
    store.directPeerAddresses = remembered.slice(-8);
  }
  return changed;
}

function arpDirectAddresses() {
  const thunderboltInterfaces = localInterfaces()
    .filter(isThunderboltInterface)
    .map(iface => iface.name);
  if (!thunderboltInterfaces.length) {
    return [];
  }

  try {
    const output = childProcess.execFileSync('/usr/sbin/arp', ['-an'], { encoding: 'utf8' });
    const result = [];
    const pattern = /\((\d+(?:\.\d+){3})\)\s+at\s+(?!\(incomplete\))\S+\s+on\s+([a-z0-9]+)/ig;
    let match = pattern.exec(output);
    while (match) {
      if (thunderboltInterfaces.indexOf(match[2]) !== -1 && isLinkLocalAddress(match[1])) {
        result.push(match[1]);
      }
      match = pattern.exec(output);
    }
    return result;
  } catch (error) {
    return [];
  }
}

function adjacentThunderboltAddresses() {
  const result = [];
  localInterfaces().forEach(iface => {
    if (!isThunderboltInterface(iface) || !isLinkLocalAddress(iface.address)) {
      return;
    }
    const parts = iface.address.split('.');
    const last = Number(parts[3]);
    [last - 1, last + 1].forEach(candidate => {
      if (candidate <= 0 || candidate >= 255) {
        return;
      }
      parts[3] = String(candidate);
      result.push(parts.join('.'));
    });
  });
  return result;
}

function directPeerHintAddresses() {
  const localAddresses = localInterfaces().map(iface => cleanAddress(iface.address));
  const candidates = []
    .concat(settings.peerIp || [])
    .concat(store && store.directPeerAddresses || [])
    .concat(arpDirectAddresses())
    .concat(adjacentThunderboltAddresses())
    .concat(pairedDirectAddresses());
  return candidates
    .map(cleanAddress)
    .filter((address, index, all) =>
      isLinkLocalAddress(address) &&
      localAddresses.indexOf(address) === -1 &&
      all.indexOf(address) === index
    );
}

function isFastDirectInterface(iface) {
  return isThunderboltInterface(iface) ||
    iface.name.indexOf('en1') === 0 ||
    iface.address.indexOf('10.10.20.') === 0;
}

function sameSubnet(address, iface) {
  address = cleanAddress(address);
  if (!address || address.indexOf('.') === -1 || !iface || !iface.address) {
    return false;
  }
  const mask = ipv4ToInt(iface.netmask || '255.255.255.0');
  return (ipv4ToInt(address) & mask) === (ipv4ToInt(iface.address) & mask);
}

function interfaceKind(iface) {
  if (!iface) {
    return { kind: 'unknown', label: 'Unknown' };
  }
  if (isThunderboltInterface(iface)) {
    return { kind: 'thunderbolt', label: 'Thunderbolt' };
  }
  if (iface.name === 'en0') {
    return { kind: 'wifi', label: 'Wi-Fi' };
  }
  if (iface.name.indexOf('en') === 0) {
    return { kind: 'ethernet', label: 'Ethernet' };
  }
  return { kind: 'network', label: iface.name };
}

function networkPathForAddress(address) {
  const remoteAddress = cleanAddress(address);
  const match = localInterfaces().find(iface => sameSubnet(remoteAddress, iface));
  const kind = interfaceKind(match);
  return {
    kind: kind.kind,
    label: kind.label,
    interfaceName: match ? match.name : null,
    remoteAddress,
    detail: match ? `${kind.label} ${match.name}` : 'Unknown path',
  };
}

function networkPathForPeer(peer) {
  const connected = !!(peer && peer.socket && !peer.socket.destroyed);
  const path = networkPathForAddress(controlAddressForPeer(peer));
  return Object.assign(path, {
    connected,
    localAddress: cleanAddress(peer && peer.localAddress),
    remoteAddress: cleanAddress(controlAddressForPeer(peer) || path.remoteAddress),
  });
}

function controlAddressForPeer(peer) {
  if (!peer) {
    return null;
  }
  if (peer.socket && !peer.socket.destroyed) {
    return cleanAddress(peer.connectedAddress || peer.remoteAddress || peer.lastAddress);
  }
  return cleanAddress(peer.lastAddress || peer.discoveryAddress);
}

function addressMatchesFastDirectInterface(address) {
  return localInterfaces().some(iface => sameSubnet(address, iface) && isFastDirectInterface(iface));
}

function isPreferredDirectAddress(address, peer) {
  address = cleanAddress(address);
  if (!address) {
    return false;
  }
  if (addressMatchesFastDirectInterface(address)) {
    return true;
  }
  if (!isLinkLocalAddress(address)) {
    return false;
  }
  if (peer && isLinkLocalAddress(peer.localAddress) && address === cleanAddress(peer.connectedAddress || peer.remoteAddress)) {
    return true;
  }

  const localHasThunderbolt = localInterfaces().some(isThunderboltInterface);
  if (!localHasThunderbolt) {
    return false;
  }

  return !peer || (peer.interfaces || []).some(iface => cleanAddress(iface.address) === address);
}

function shouldKeepPreferredDirectPath(currentAddress, bestAddress, peer) {
  if (!currentAddress || !bestAddress || currentAddress === bestAddress) {
    return false;
  }
  return isPreferredDirectAddress(currentAddress, peer) &&
    !isPreferredDirectAddress(bestAddress, peer);
}

function addressRank(address, peer) {
  address = cleanAddress(address);
  let rank = 50;
  let matchedFastDirect = false;
  let matchedSubnet = false;
  localInterfaces().forEach(iface => {
    if (sameSubnet(address, iface)) {
      matchedSubnet = true;
      const fastDirect = isFastDirectInterface(iface);
      matchedFastDirect = matchedFastDirect || fastDirect;
      rank = Math.min(rank, fastDirect ? 0 : 10);
    }
  });

  if (!matchedSubnet && isPreferredDirectAddress(address, peer)) {
    matchedFastDirect = true;
    rank = Math.min(rank, 2);
  }

  if (address === cleanAddress(controlAddressForPeer(peer))) {
    rank += matchedFastDirect ? 0 : 1;
  }
  if (address.indexOf('169.254.') === 0 && !matchedFastDirect) {
    rank += 15;
  }
  return rank;
}

function maybeReconnectToBetterAdvertisedPath(peer) {
  if (!peer ||
      !peerConnectionEnabled(peer) ||
      !store.autoConnect ||
      mode !== 'onscreen' ||
      connecting[peer.hostId]) {
    return;
  }
  const currentAddress = controlAddressForPeer(peer);
  if (!peer.socket || peer.socket.destroyed || !currentAddress) {
    return;
  }

  const addresses = candidateAddresses(peer);
  const best = addresses[0];
  if (!best || best === currentAddress) {
    return;
  }
  if (shouldKeepPreferredDirectPath(currentAddress, best, peer)) {
    return;
  }

  const currentRank = addressRank(currentAddress, peer);
  const bestRank = addressRank(best, peer);
  if (bestRank + 5 >= currentRank) {
    return;
  }

  log('RECONNECT_BETTER_PATH', `${peer.nickname} ${currentAddress} -> ${best}`);
  connectPeer(peer.hostId, { allowUpgrade: true, addresses: [best] });
}

function checkPreferredPeerPaths() {
  if (!store || !store.autoConnect || mode !== 'onscreen') {
    return;
  }
  connectDirectPeerHints();
  Object.keys(peers).forEach(hostId => {
    const peer = peers[hostId];
    if (!peer ||
        !isPeerPaired(peer) ||
        !peerConnectionEnabled(peer) ||
        connecting[hostId]) {
      return;
    }
    if (!peer.socket || peer.socket.destroyed) {
      connectPeer(hostId);
      return;
    }
    maybeReconnectToBetterAdvertisedPath(peer);
  });
}

function connectDirectPeerHints() {
  directPeerHintAddresses().forEach(address => {
    const knownPeer = Object.keys(peers)
      .map(hostId => peers[hostId])
      .find(peer => candidateAddresses(peer).indexOf(address) !== -1);
    if (knownPeer) {
      if ((!knownPeer.socket || knownPeer.socket.destroyed) && !connecting[knownPeer.hostId]) {
        connectPeer(knownPeer.hostId, { addresses: [address] });
      }
      return;
    }
    // Unknown devices remain discoverable over UDP. A control socket is only
    // opened after the user explicitly pairs them.
  });
}

function startPreferredPathChecks() {
  if (preferredPathTimer) {
    return;
  }
  preferredPathTimer = setInterval(checkPreferredPeerPaths, PREFERRED_PATH_CHECK_MS);
}

function connectPeer(hostId, options) {
  options = options || {};
  const peer = peers[hostId];
  if (!connectionsEnabled(store) ||
      !peer ||
      (!isPeerPaired(peer) && !options.pairing) ||
      (isPeerPaired(peer) &&
        !peerConnectionEnabled(peer) &&
        !options.pairing &&
        !options.manual) ||
      connecting[hostId] ||
      (!options.allowUpgrade && peer.socket && !peer.socket.destroyed)) {
    return;
  }

  const addresses = options.addresses ||
    candidateAddresses(peer, options.pairing ? false : undefined);
  if (!addresses.length) {
    if (options.pairing) {
      failPairing(hostId, 'OpenMouse could not find an address for this Mac.');
    }
    return;
  }

  connecting[hostId] = true;
  log(options.allowUpgrade ? 'CONNECT_UPGRADE' : 'CONNECT', `${peer.nickname} via ${addresses.join(', ')}`);
  tryConnectAddress(peer, addresses, 0, options);
}

function tryConnectAddress(peer, addresses, index, options) {
  if (index >= addresses.length) {
    connecting[peer.hostId] = false;
    if (options && options.pairing) {
      failPairing(peer.hostId, 'OpenMouse could not reach this Mac.');
    }
    pushState();
    return;
  }

  const address = addresses[index];
  const socket = net.createConnection({ host: address, port: peer.tcpPort || TCP_PORT });
  let didConnect = false;
  socket.setNoDelay(true);
  socket.setTimeout(addressRank(address, peer) === 0 ? 450 : 2500);

  socket.on('connect', () => {
    didConnect = true;
    socket.setTimeout(0);
    connecting[peer.hostId] = false;
    peer.lastAddress = cleanAddress(address);
    peer.localAddress = cleanAddress(socket.localAddress);
    peer.remoteAddress = cleanAddress(socket.remoteAddress || address);
    peer.connectedAddress = cleanAddress(socket.remoteAddress || address);
    const path = networkPathForPeer(peer);
    log('CONNECT_OK', `${peer.nickname} ${peer.lastAddress}:${peer.tcpPort || TCP_PORT} via ${path.detail}`);
    const wireOptions = Object.assign({}, options, {
      expectedHostId: peer.hostId,
      onWrongPeer() {
        connecting[peer.hostId] = true;
        tryConnectAddress(peer, addresses, index + 1, options);
      },
    });
    wireSocket(socket, true, wireOptions);
  });

  socket.once('error', error => {
    if (didConnect) {
      return;
    }
    log('CONNECT_FAIL', `${peer.nickname} ${address}: ${error.message}`);
    socket.destroy();
    tryConnectAddress(peer, addresses, index + 1, options);
  });

  socket.once('timeout', () => {
    if (didConnect) {
      return;
    }
    log('CONNECT_TIMEOUT', `${peer.nickname} ${address}`);
    socket.destroy();
    tryConnectAddress(peer, addresses, index + 1, options);
  });
}

function writeRawFrame(socket, frame) {
  if (!socket || socket.destroyed) {
    return false;
  }
  try {
    socket.write(JSON.stringify(frame) + '\n');
    return true;
  } catch (error) {
    log('SOCKET_WRITE_ERROR', error.message);
    socket.destroy();
    return false;
  }
}

function sendPlainFrame(socket, type, payload) {
  return writeRawFrame(socket, Object.assign({ type }, payload || {}));
}

function sendFrame(socket, type, payload) {
  if (!socket || !socket.openmouseSessionKey || !socket.openmouseAuthenticated) {
    return false;
  }
  socket.openmouseSendSeq = Number(socket.openmouseSendSeq || 0) + 1;
  try {
    return writeRawFrame(socket, pairingSecurity.sealFrame(
      socket.openmouseSessionKey,
      Object.assign({ type }, payload || {}),
      {
        transport: 'tcp',
        senderHostId: store.hostId,
        receiverHostId: socket.openmouseRemoteId,
      },
      socket.openmouseSendSeq
    ));
  } catch (error) {
    log('SECURE_SEND_ERROR', error.message);
    socket.destroy();
    return false;
  }
}

function sendUdpFrame(hostId, type, payload) {
  const peer = peers[hostId];
  const address = controlAddressForPeer(peer);
  const socket = peer && peer.socket;
  if (!connectionsEnabled(store) ||
      !peer ||
      !isPeerPaired(peer) ||
      !socket ||
      !socket.openmouseSessionKey ||
      !discoverySocket ||
      !address) {
    return false;
  }

  peer.udpSendSeq = Number(peer.udpSendSeq || 0) + 1;
  const sealed = pairingSecurity.sealFrame(
    socket.openmouseSessionKey,
    Object.assign({ type }, payload || {}),
    {
      transport: 'udp',
      senderHostId: store.hostId,
      receiverHostId: hostId,
    },
    peer.udpSendSeq
  );
  const frame = Buffer.from(JSON.stringify(Object.assign({
    app: 'openmouse',
    protocol: 8,
    type: 'sealed',
    hostId: store.hostId,
    sealed,
  })));

  discoverySocket.send(frame, 0, frame.length, DISCOVERY_PORT, address);
  return true;
}

function ensurePeerCoordQueue(peer) {
  if (!peer.coordQueue) {
    peer.coordQueue = {
      payload: null,
      timer: null,
      dueAt: 0,
      lastSentAt: 0,
      lastSample: null,
      nextSeq: 0,
    };
  }
  return peer.coordQueue;
}

function clearPeerCoordQueue(peer) {
  if (!peer || !peer.coordQueue) {
    return;
  }
  if (peer.coordQueue.timer) {
    clearTimeout(peer.coordQueue.timer);
  }
  peer.coordQueue.timer = null;
  peer.coordQueue.dueAt = 0;
  peer.coordQueue.payload = null;
  peer.coordQueue.lastSentAt = 0;
  peer.coordQueue.lastSample = null;
}

function coordFlushDelayForPayload(queue, payload) {
  const x = numericMouseCoordinate(payload && payload.x, 0);
  const y = numericMouseCoordinate(payload && payload.y, 0);
  const previous = queue.lastSample;
  queue.lastSample = { x, y };
  if (!previous) {
    return COORD_FLUSH_FAST_MS;
  }
  const delta = Math.max(Math.abs(x - previous.x), Math.abs(y - previous.y));
  return delta >= COORD_FAST_DELTA_PX ? COORD_FLUSH_FAST_MS : COORD_FLUSH_SMOOTH_MS;
}

function socketBackpressured(socket) {
  return !!socket &&
    typeof socket.writableLength === 'number' &&
    socket.writableLength > COORD_BACKPRESSURE_BYTES;
}

function scheduleCoordFlush(hostId, delay) {
  const peer = peers[hostId];
  if (!peer) {
    return;
  }
  const queue = ensurePeerCoordQueue(peer);
  const dueAt = Date.now() + Math.max(1, delay);
  if (queue.timer && queue.dueAt <= dueAt) {
    return;
  }
  if (queue.timer) {
    clearTimeout(queue.timer);
  }
  queue.dueAt = dueAt;
  queue.timer = setTimeout(() => {
    queue.timer = null;
    queue.dueAt = 0;
    flushPendingCoord(hostId);
  }, Math.max(1, delay));
}

function sequencedCoordPayload(queue, payload) {
  queue.nextSeq += 1;
  return Object.assign({}, payload, {
    seq: queue.nextSeq,
    sampleAt: Date.now(),
  });
}

function canSendCoordOverUdp(peer) {
  return !!(peer &&
    connectionsEnabled(store) &&
    discoverySocket &&
    controlAddressForPeer(peer) &&
    peer.udpReachableAt &&
    Date.now() - peer.udpReachableAt <= UDP_REACHABLE_WINDOW_MS);
}

function sendCoordFrame(hostId, payload, ordered) {
  const peer = peers[hostId];
  if (ordered) {
    return sendToPeer(hostId, 'coord', payload) || sendUdpFrame(hostId, 'coord', payload);
  }
  if (canSendCoordOverUdp(peer)) {
    return sendUdpFrame(hostId, 'coord', payload) || sendToPeer(hostId, 'coord', payload);
  }
  return sendToPeer(hostId, 'coord', payload) || sendUdpFrame(hostId, 'coord', payload);
}

function sendCoordToPeerNow(hostId, payload, ordered) {
  const peer = peers[hostId];
  if (!peer) {
    return false;
  }
  const socket = peer.socket && !peer.socket.destroyed ? peer.socket : null;
  if (!canSendCoordOverUdp(peer) && socket && socketBackpressured(socket)) {
    const queue = ensurePeerCoordQueue(peer);
    queue.payload = payload;
    scheduleCoordFlush(hostId, COORD_FLUSH_SMOOTH_MS);
    return true;
  }

  const sent = sendCoordFrame(hostId, payload, !!ordered);
  if (sent) {
    ensurePeerCoordQueue(peer).lastSentAt = Date.now();
    noteInputFrameSent(hostId, 'coord');
  }
  return sent;
}

function flushPendingCoord(hostId) {
  const peer = peers[hostId];
  const queue = peer && peer.coordQueue;
  if (!peer || !queue || !queue.payload) {
    return false;
  }
  const payload = queue.payload;
  queue.payload = null;
  return sendCoordToPeerNow(hostId, payload);
}

function sendCoordToPeer(hostId, payload) {
  const peer = peers[hostId];
  if (!peer) {
    return false;
  }
  const queue = ensurePeerCoordQueue(peer);
  const frame = sequencedCoordPayload(queue, payload);
  const delay = coordFlushDelayForPayload(queue, payload);
  const socket = peer.socket && !peer.socket.destroyed ? peer.socket : null;
  const now = Date.now();
  const wait = Math.max(1, delay - (now - queue.lastSentAt));
  if (now - queue.lastSentAt >= delay &&
      (canSendCoordOverUdp(peer) || !socketBackpressured(socket))) {
    return sendCoordToPeerNow(hostId, frame, false);
  }

  queue.payload = frame;
  scheduleCoordFlush(hostId, wait);
  return true;
}

function beginCoordStream(hostId, payload) {
  const peer = peers[hostId];
  if (!peer) {
    return false;
  }
  clearPeerCoordQueue(peer);
  const queue = ensurePeerCoordQueue(peer);
  queue.lastSample = {
    x: numericMouseCoordinate(payload && payload.x, 0),
    y: numericMouseCoordinate(payload && payload.y, 0),
  };
  const sent = sendCoordToPeerNow(hostId, sequencedCoordPayload(queue, payload), true);
  log(
    'COORD_STREAM',
    `${peerName(hostId)} initial=TCP motion=${canSendCoordOverUdp(peer) ? 'UDP' : 'TCP'} interval=${COORD_FLUSH_FAST_MS}-${COORD_FLUSH_SMOOTH_MS}ms`
  );
  return sent;
}

function socketMember(socket, local) {
  const hello = local ? socket.openmouseLocalHello : socket.openmouseRemoteHello;
  if (!hello) {
    return null;
  }
  return {
    hostId: local ? store.hostId : socket.openmouseRemoteId,
    publicKeyPem: local ? store.identity.publicKeyPem : hello.publicKeyPem,
    challenge: hello.challenge,
    ephemeralPublicKeyPem: hello.ephemeralPublicKeyPem,
    connectRequested: !!hello.connectRequested,
  };
}

function securityTranscriptForSocket(socket) {
  const local = socketMember(socket, true);
  const remote = socketMember(socket, false);
  if (!local || !remote) {
    return null;
  }
  return pairingSecurity.socketTranscript([local, remote]);
}

function pairingTranscriptForSession(session) {
  const socket = session && session.socket;
  const local = socketMember(socket, true);
  const remote = socketMember(socket, false);
  if (!local || !remote || !session.localNonce || !session.remoteNonce) {
    return null;
  }
  local.pairingNonce = session.localNonce;
  remote.pairingNonce = session.remoteNonce;
  return pairingSecurity.pairingTranscript(session.pairingId, [local, remote]);
}

function updatePairingCode(session) {
  const transcript = pairingTranscriptForSession(session);
  if (!transcript) {
    return false;
  }
  session.transcript = transcript;
  session.code = pairingSecurity.pairingCode(transcript);
  session.state = 'verify';
  pushState();
  return true;
}

function destroyPendingSocket(peer) {
  if (peer && peer.pendingSocket && !peer.pendingSocket.destroyed) {
    peer.pendingSocket.destroy();
  }
  if (peer) {
    peer.pendingSocket = null;
  }
}

function failPairing(hostId, message, notifyRemote) {
  const session = pairingSessions[hostId];
  if (!session) {
    return;
  }
  if (notifyRemote && session.socket && !session.socket.destroyed) {
    sendPlainFrame(session.socket, 'pair:reject', {
      pairingId: session.pairingId,
      reason: message || 'Pairing rejected',
    });
  }
  session.state = 'failed';
  session.message = message || 'Pairing could not be completed.';
  session.code = null;
  if (session.socket && !session.socket.openmouseAuthenticated) {
    session.socket.destroy();
  }
  pushState();
}

function startPairing(hostId) {
  const peer = peers[hostId];
  if (!connectionsEnabled(store) || !peer || !peer.publicKeyPem || peer.identityChanged) {
    return;
  }
  if (isPeerPaired(peer)) {
    connectPeer(hostId);
    return;
  }
  const existing = pairingSessionForPeer(hostId);
  if (existing && existing.state !== 'failed') {
    return;
  }
  pairingSessions[hostId] = {
    hostId,
    pairingId: pairingSecurity.randomToken(18),
    localNonce: pairingSecurity.randomToken(24),
    remoteNonce: null,
    initiator: true,
    state: 'connecting',
    code: null,
    localConfirmed: false,
    remoteConfirmed: false,
    expiresAt: Date.now() + PAIRING_TIMEOUT_MS,
    socket: null,
  };
  pushState();
  connectPeer(hostId, { pairing: true });
}

function sendPairRequest(session) {
  if (!session || !session.socket || session.requestSent) {
    return;
  }
  session.requestSent = sendPlainFrame(session.socket, 'pair:request', {
    pairingId: session.pairingId,
    nonce: session.localNonce,
  });
}

function handlePairRequest(socket, frame) {
  const hostId = socket.openmouseRemoteId;
  const peer = hostId && peers[hostId];
  if (!peer || !peer.publicKeyPem || peer.identityChanged || pairedPeerRecord(hostId)) {
    sendPlainFrame(socket, 'pair:reject', {
      pairingId: frame.pairingId,
      reason: 'Pairing is not available for this Mac.',
    });
    return;
  }
  const pairingId = String(frame.pairingId || '');
  const remoteNonce = String(frame.nonce || '');
  if (!pairingId || !remoteNonce) {
    return;
  }
  const existing = pairingSessionForPeer(hostId);
  if (existing && existing.pairingId !== pairingId) {
    sendPlainFrame(socket, 'pair:reject', {
      pairingId,
      reason: 'Another pairing request is already open.',
    });
    return;
  }
  const session = existing || {
    hostId,
    pairingId,
    localNonce: pairingSecurity.randomToken(24),
    initiator: false,
    state: 'verify',
    code: null,
    localConfirmed: false,
    remoteConfirmed: false,
    expiresAt: Date.now() + PAIRING_TIMEOUT_MS,
  };
  session.remoteNonce = remoteNonce;
  session.socket = socket;
  pairingSessions[hostId] = session;
  peer.pendingSocket = socket;
  sendPlainFrame(socket, 'pair:challenge', {
    pairingId,
    nonce: session.localNonce,
  });
  updatePairingCode(session);
}

function handlePairChallenge(socket, frame) {
  const hostId = socket.openmouseRemoteId;
  const session = hostId && pairingSessionForPeer(hostId);
  if (!session ||
      !session.initiator ||
      session.pairingId !== String(frame.pairingId || '')) {
    return;
  }
  session.remoteNonce = String(frame.nonce || '');
  session.socket = socket;
  updatePairingCode(session);
}

function rememberPairedPeer(peer) {
  store.pairedPeers[peer.hostId] = {
    publicKeyPem: peer.publicKeyPem,
    fingerprint: peer.fingerprint,
    nickname: peer.nickname,
    pairedAt: Date.now(),
    enabled: true,
  };
  peer.identityChanged = false;
  if (!hasLayoutForPeer(peer.hostId)) {
    const edge = firstOpenEdge();
    store.layout[peer.hostId] = {
      edge,
      sourceDisplayId: defaultSourceDisplayIdForEdge(edge),
      auto: true,
      updatedAt: Date.now(),
    };
  }
  writeStore();
}

function promoteAuthenticatedSocket(peer, socket) {
  const newAddress = cleanAddress(socket.remoteAddress);
  if (peer.socket && peer.socket !== socket && !peer.socket.destroyed) {
    const currentAddress = controlAddressForPeer(peer);
    const currentRank = addressRank(currentAddress, peer);
    const newRank = addressRank(newAddress, peer);
    const desiredOutgoing = store.hostId < peer.hostId;
    const currentPreferred = !!peer.socket.openmouseOutgoing === desiredOutgoing;
    const newPreferred = !!socket.openmouseOutgoing === desiredOutgoing;
    if (newRank > currentRank || (newRank === currentRank && (currentPreferred || !newPreferred))) {
      log('SOCKET_REJECT', `${peer.nickname} ${newAddress} rank=${newRank}`);
      socket.destroy();
      return false;
    }
    peer.socket.destroy();
  }
  socket.openmouseAuthenticated = true;
  socket.openmouseSendSeq = 0;
  socket.openmouseRecvSeq = 0;
  peer.socket = socket;
  peer.pendingSocket = null;
  peer.udpSendSeq = 0;
  peer.udpRecvSeq = 0;
  peer.lastAddress = newAddress;
  peer.localAddress = cleanAddress(socket.localAddress);
  peer.remoteAddress = cleanAddress(socket.remoteAddress);
  peer.connectedAddress = newAddress;
  const path = networkPathForPeer(peer);
  log('SECURE_CONNECTED', `${peer.nickname} ${peer.lastAddress} via ${path.detail}`);
  syncClipboardToPeer(peer.hostId, 'connect', true);
  pushState();
  return true;
}

function completePairing(session) {
  if (!session || !session.localConfirmed || !session.remoteConfirmed) {
    return;
  }
  const peer = peers[session.hostId];
  const socket = session.socket;
  const transcript = securityTranscriptForSocket(socket);
  if (!peer || !socket || !transcript) {
    failPairing(session.hostId, 'The secure connection closed before pairing completed.');
    return;
  }
  rememberPairedPeer(peer);
  socket.openmouseSessionKey = pairingSecurity.deriveSessionKey(
    socket.openmouseEphemeral.privateKeyPem,
    socket.openmouseRemoteHello.ephemeralPublicKeyPem,
    transcript
  );
  session.state = 'paired';
  session.code = null;
  promoteAuthenticatedSocket(peer, socket);
  broadcastPresence();
  pushState();
}

function confirmPairing(hostId) {
  const session = pairingSessionForPeer(hostId);
  if (!session || session.state !== 'verify' || !session.transcript || session.localConfirmed) {
    return;
  }
  const signature = pairingSecurity.signTranscript(
    store.identity.privateKeyPem,
    session.transcript
  );
  if (!sendPlainFrame(session.socket, 'pair:confirm', {
    pairingId: session.pairingId,
    signature,
  })) {
    failPairing(hostId, 'The other Mac disconnected.');
    return;
  }
  session.localConfirmed = true;
  completePairing(session);
  pushState();
}

function handlePairConfirm(socket, frame) {
  const hostId = socket.openmouseRemoteId;
  const session = hostId && pairingSessionForPeer(hostId);
  const peer = hostId && peers[hostId];
  if (!session ||
      !peer ||
      session.pairingId !== String(frame.pairingId || '') ||
      !session.transcript) {
    return;
  }
  if (!pairingSecurity.verifyTranscript(
    peer.publicKeyPem,
    session.transcript,
    frame.signature
  )) {
    failPairing(hostId, 'The pairing signature did not match.', true);
    return;
  }
  session.remoteConfirmed = true;
  completePairing(session);
  pushState();
}

function sendSocketAuth(socket) {
  if (socket.openmouseAuthSent) {
    return;
  }
  const transcript = securityTranscriptForSocket(socket);
  if (!transcript) {
    return;
  }
  const peer = peers[socket.openmouseRemoteId];
  const connectionEnabled = peerConnectionEnabled(peer);
  const authTranscript = `${transcript}|connection-enabled:${connectionEnabled}`;
  socket.openmouseAuthSent = sendPlainFrame(socket, 'auth', {
    connectionEnabled,
    signature: pairingSecurity.signTranscript(store.identity.privateKeyPem, authTranscript),
  });
}

function handleSocketAuth(socket, frame) {
  const hostId = socket.openmouseRemoteId;
  const peer = hostId && peers[hostId];
  const transcript = securityTranscriptForSocket(socket);
  const explicitConnect = !!(socket.openmouseRemoteHello &&
    socket.openmouseRemoteHello.connectRequested);
  const localConnect = !!(socket.openmouseLocalHello &&
    socket.openmouseLocalHello.connectRequested);
  const remoteEnabled = !!frame.connectionEnabled;
  const authTranscript = `${transcript}|connection-enabled:${remoteEnabled}`;
  if (!peer ||
      !isPeerPaired(peer) ||
      !transcript ||
      !pairingSecurity.verifyTranscript(peer.publicKeyPem, authTranscript, frame.signature)) {
    log('AUTH_REJECTED', hostId || cleanAddress(socket.remoteAddress));
    socket.destroy();
    return;
  }
  if (!remoteEnabled && !localConnect && !explicitConnect) {
    const trusted = pairedPeerRecord(peer.hostId);
    trusted.enabled = false;
    writeStore();
    pushState();
    socket.destroy();
    return;
  }
  if (!peerConnectionEnabled(peer) && !explicitConnect && !localConnect) {
    socket.destroy();
    return;
  }
  if (explicitConnect && !peerConnectionEnabled(peer)) {
    const trusted = pairedPeerRecord(peer.hostId);
    trusted.enabled = true;
    writeStore();
  }
  socket.openmouseSessionKey = pairingSecurity.deriveSessionKey(
    socket.openmouseEphemeral.privateKeyPem,
    socket.openmouseRemoteHello.ephemeralPublicKeyPem,
    transcript
  );
  promoteAuthenticatedSocket(peer, socket);
}

function handleWireFrame(socket, frame, setRemoteId) {
  if (frame && frame.type === 'sealed') {
    if (!socket.openmouseAuthenticated || !socket.openmouseSessionKey) {
      throw new Error('Encrypted frame arrived before authentication');
    }
    const sequence = Number(frame.seq);
    if (!Number.isSafeInteger(sequence) || sequence <= Number(socket.openmouseRecvSeq || 0)) {
      throw new Error('Repeated secure-frame sequence');
    }
    const clearFrame = pairingSecurity.openFrame(
      socket.openmouseSessionKey,
      frame,
      {
        transport: 'tcp',
        senderHostId: socket.openmouseRemoteId,
        receiverHostId: store.hostId,
      }
    );
    socket.openmouseRecvSeq = sequence;
    handleFrame(socket, clearFrame, setRemoteId);
    return;
  }
  const allowed = ['hello', 'auth', 'pair:request', 'pair:challenge', 'pair:confirm', 'pair:reject'];
  if (!frame || allowed.indexOf(frame.type) === -1 || socket.openmouseAuthenticated) {
    throw new Error('Unprotected application frame rejected');
  }
  handleFrame(socket, frame, setRemoteId);
}

function wireSocket(socket, outgoing, options) {
  if (!connectionsEnabled(store)) {
    socket.destroy();
    return;
  }
  let buffer = '';
  let remoteId = null;
  socket.openmouseOutgoing = !!outgoing;
  socket.openmouseExpectedHostId = options && options.expectedHostId || null;
  socket.openmouseOnWrongPeer = options && options.onWrongPeer || null;
  socket.openmouseEphemeral = pairingSecurity.createEphemeralKeyPair();
  socket.openmouseLocalHello = {
    publicKeyPem: store.identity.publicKeyPem,
    challenge: pairingSecurity.randomToken(24),
    ephemeralPublicKeyPem: socket.openmouseEphemeral.publicKeyPem,
    connectRequested: !!(options && options.manual),
  };
  socket.setNoDelay(true);
  sendPlainFrame(socket, 'hello', {
    peer: localInfo(),
    challenge: socket.openmouseLocalHello.challenge,
    ephemeralPublicKeyPem: socket.openmouseLocalHello.ephemeralPublicKeyPem,
    connectRequested: socket.openmouseLocalHello.connectRequested,
  });

  socket.on('data', chunk => {
    buffer += chunk.toString('utf8');
    let newline = buffer.indexOf('\n');
    while (newline !== -1) {
      const line = buffer.slice(0, newline).trim();
      buffer = buffer.slice(newline + 1);
      if (line) {
        try {
          handleWireFrame(socket, JSON.parse(line), id => {
            remoteId = id;
          });
        } catch (error) {
          log('FRAME_ERROR', error.message);
          socket.destroy();
        }
      }
      newline = buffer.indexOf('\n');
    }
  });

  socket.on('drain', () => {
    const id = peerIdForSocket(socket);
    if (id) {
      flushPendingCoord(id);
    }
  });

  socket.on('close', () => {
    if (remoteId && peers[remoteId] && peers[remoteId].socket === socket) {
      clearPeerCoordQueue(peers[remoteId]);
      peers[remoteId].socket = null;
      peers[remoteId].connectedAddress = null;
      if (inboundControlPeerId === remoteId) {
        endInboundControl(remoteId, 'socket closed');
      } else {
        releaseRemoteKeys(remoteId, 'socket closed');
        releaseRemoteMouse(remoteId, 'socket closed');
      }
      pushState();
    }
    if (remoteId && peers[remoteId] && peers[remoteId].pendingSocket === socket) {
      peers[remoteId].pendingSocket = null;
      const pairing = pairingSessionForPeer(remoteId);
      if (pairing && pairing.state !== 'paired' && pairing.state !== 'failed') {
        pairing.state = 'failed';
        pairing.message = 'The other Mac disconnected.';
        pairing.code = null;
        pushState();
      }
    }
  });

  socket.on('error', error => {
    log('SOCKET_ERROR', error.message);
  });
}

function handleFrame(socket, frame, setRemoteId) {
  if (!connectionsEnabled(store)) {
    socket.destroy();
    return;
  }
  if (frame.type === 'hello') {
    if (!frame.peer ||
        (socket.openmouseExpectedHostId &&
          frame.peer.hostId !== socket.openmouseExpectedHostId)) {
      log(
        'HELLO_WRONG_PEER',
        `expected=${socket.openmouseExpectedHostId || 'unknown'} got=${frame.peer && frame.peer.hostId || 'missing'}`
      );
      const tryNextAddress = socket.openmouseOnWrongPeer;
      socket.openmouseOnWrongPeer = null;
      socket.destroy();
      if (tryNextAddress) {
        tryNextAddress();
      }
      return;
    }
    const peer = rememberPeer(frame.peer, socket.remoteAddress);
    if (!peer) {
      return;
    }
    setRemoteId(peer.hostId);
    socket.openmouseRemoteId = peer.hostId;
    socket.openmouseRemoteHello = {
      publicKeyPem: frame.peer && frame.peer.publicKeyPem,
      challenge: String(frame.challenge || ''),
      ephemeralPublicKeyPem: String(frame.ephemeralPublicKeyPem || ''),
      connectRequested: !!frame.connectRequested,
    };
    const newAddress = cleanAddress(socket.remoteAddress);
    socket.setTimeout(0);
    if (!socket.openmouseRemoteHello.challenge ||
        !socket.openmouseRemoteHello.publicKeyPem ||
        !socket.openmouseRemoteHello.ephemeralPublicKeyPem ||
        socket.openmouseRemoteHello.publicKeyPem !== peer.publicKeyPem) {
      log('HELLO_REJECTED', `${peer.nickname} missing or inconsistent identity`);
      socket.destroy();
      return;
    }
    peer.pendingSocket = socket;
    const pairing = pairingSessionForPeer(peer.hostId);
    if (pairing && pairing.initiator) {
      pairing.socket = socket;
      sendPairRequest(pairing);
    } else if (isPeerPaired(peer)) {
      sendSocketAuth(socket);
    } else if (peer.identityChanged) {
      log('IDENTITY_CHANGED', `${peer.nickname} blocked until forgotten`);
      socket.destroy();
    }
    pushState();
    return;
  }

  if (frame.type === 'auth') {
    handleSocketAuth(socket, frame);
    return;
  }

  if (frame.type === 'pair:request') {
    handlePairRequest(socket, frame);
    return;
  }

  if (frame.type === 'pair:challenge') {
    handlePairChallenge(socket, frame);
    return;
  }

  if (frame.type === 'pair:confirm') {
    handlePairConfirm(socket, frame);
    return;
  }

  if (frame.type === 'pair:reject') {
    const senderId = socket.openmouseRemoteId;
    const session = senderId && pairingSessionForPeer(senderId);
    if (session && session.pairingId === String(frame.pairingId || '')) {
      failPairing(senderId, frame.reason || 'Pairing was rejected.');
    }
    return;
  }

  if (frame.type === 'clipboard') {
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      applyRemoteClipboard(senderId, frame);
    }
    return;
  }

  if (frame.type === 'connection:pause') {
    const senderId = peerIdForSocket(socket);
    const trusted = senderId && pairedPeerRecord(senderId);
    if (trusted) {
      trusted.enabled = false;
      writeStore();
      pushState();
      setImmediate(() => {
        if (!socket.destroyed) {
          socket.destroy();
        }
      });
    }
    return;
  }

  if (frame.type === 'control:start') {
    startInboundControl(socket);
    return;
  }

  if (frame.type === 'control:ping') {
    startInboundControl(socket);
    return;
  }

  if (frame.type === 'latency:ping') {
    sendFrame(socket, 'latency:pong', {
      id: frame.id,
      sentAt: frame.sentAt,
    });
    return;
  }

  if (frame.type === 'latency:pong') {
    handleLatencyPong(socket, frame);
    return;
  }

  if (frame.type === 'control:end') {
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      endInboundControl(senderId, 'peer ended');
    }
    return;
  }

  if (frame.type === 'shortcut') {
    if (!frame.test && !acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (frame.test) {
      log('REMOTE_SHORTCUT_TEST_RECV', `${frame.name} from ${peerName(senderId)}`);
    }
    performRemoteShortcut(frame.name, senderId);
    return;
  }

  if (frame.type === 'coord') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      remoteMouseMove(senderId, frame.x, frame.y, frame.seq, frame.sampleAt);
    }
  } else if (frame.type === 'md') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      remoteMouseButton(senderId, 'down', frame.button, frame.x, frame.y);
    }
  } else if (frame.type === 'mu') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      remoteMouseButton(senderId, 'up', frame.button, frame.x, frame.y);
    }
  } else if (frame.type === 'kd') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      remoteKeyDown(senderId, frame);
    }
  } else if (frame.type === 'ku') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    const senderId = peerIdForSocket(socket);
    if (senderId) {
      remoteKeyUp(senderId, frame);
    }
  } else if (frame.type === 'wh') {
    if (!acceptRemoteInput(socket, frame.type)) {
      return;
    }
    robot.scrollMouse(frame.x || 0, frame.y || 0);
  }
}

function performRemoteShortcut(name, senderId) {
  if (name === 'command-tab') {
    performRemoteAppSwitch(senderId, false);
  } else if (name === 'command-shift-tab') {
    performRemoteAppSwitch(senderId, true);
  } else if (name === 'control-up') {
    performRemoteKeyChord(senderId, ['control'], 'up');
  } else if (name === 'control-down') {
    performRemoteKeyChord(senderId, ['control'], 'down');
  } else if (name === 'control-left') {
    performRemoteKeyChord(senderId, ['control'], 'left');
  } else if (name === 'control-right') {
    performRemoteKeyChord(senderId, ['control'], 'right');
  } else if (name === CLIPBOARD_COPY_SHORTCUT) {
    performRemoteClipboardCopy(senderId);
  } else if (name === CLIPBOARD_PASTE_SHORTCUT) {
    performRemoteClipboardPaste(senderId);
  }
}

function mouseButtonName(button) {
  if (button === 'back' || button === 'x1' || button === 'button4' || button === 3) {
    return 'back';
  }
  if (button === 'forward' || button === 'x2' || button === 'button5' || button === 4) {
    return 'forward';
  }
  if (button === 'right' || button === 2) {
    return 'right';
  }
  if (button === 'middle' || button === 1) {
    return 'middle';
  }
  return 'left';
}

function scrollPayload(data) {
  const deltaX = Number((data && data.deltaX) || 0);
  const deltaY = Number((data && data.deltaY) || 0);
  if (data && data.shiftKey && deltaX === 0 && deltaY !== 0) {
    return { x: deltaY, y: 0 };
  }
  return { x: deltaX, y: deltaY };
}

function numericMouseCoordinate(value, fallback) {
  const number = Number(value);
  return Number.isFinite(number) ? Math.round(number) : Math.round(fallback || 0);
}

function nativeMouseSendPath() {
  return path.join(__dirname, 'bin', 'openmouse-mousesend');
}

function startNativeMouseSend() {
  if (process.platform !== 'darwin' || nativeMouseSend) {
    return !!nativeMouseSend;
  }

  const helper = nativeMouseSendPath();
  if (!fs.existsSync(helper)) {
    log('MOUSESEND_MISSING', helper);
    return false;
  }

  nativeMouseSend = childProcess.spawn(helper, [], {
    stdio: ['pipe', 'ignore', 'pipe'],
  });

  nativeMouseSend.stderr.on('data', chunk => {
    const message = chunk.toString('utf8').trim();
    if (message) {
      log('MOUSESEND_ERROR', message);
    }
  });

  nativeMouseSend.on('error', error => {
    log('MOUSESEND_START_ERROR', error.message);
  });

  nativeMouseSend.on('exit', (code, signal) => {
    if (nativeMouseSend) {
      log('MOUSESEND_EXIT', `code=${code} signal=${signal || ''}`);
    }
    nativeMouseSend = null;
  });

  log('MOUSESEND_START', helper);
  return true;
}

function stopNativeMouseSend() {
  if (!nativeMouseSend) {
    return;
  }
  const proc = nativeMouseSend;
  nativeMouseSend = null;
  try {
    proc.stdin.write('quit 0 0 left\n');
    proc.stdin.end();
  } catch (error) {
    proc.kill();
  }
  log('MOUSESEND_STOP', 'stopped');
}

function sendNativeMouseCommand(action, x, y, button) {
  if (!startNativeMouseSend() || !nativeMouseSend || !nativeMouseSend.stdin.writable) {
    return false;
  }

  const safeX = numericMouseCoordinate(x, 0);
  const safeY = numericMouseCoordinate(y, 0);
  const line = `${action} ${safeX} ${safeY} ${mouseButtonName(button)}\n`;
  try {
    nativeMouseSend.stdin.write(line);
    return true;
  } catch (error) {
    log('MOUSESEND_WRITE_ERROR', error.message);
    return false;
  }
}

function currentMousePos() {
  try {
    return robot.getMousePos();
  } catch (error) {
    return CENTER;
  }
}

function createRemoteMotionStats() {
  const now = Date.now();
  return {
    startedAt: now,
    lastLogAt: now,
    arrivals: 0,
    renders: 0,
    interpolated: 0,
    coalesced: 0,
    stale: 0,
    seqSkipped: 0,
    lastSeq: null,
    lastArrivalAt: 0,
    arrivalGapTotal: 0,
    arrivalGapSamples: 0,
    arrivalGapMax: 0,
    arrivalGapsOverFrame: 0,
    lastSourceSampleAt: 0,
    sourceGapTotal: 0,
    sourceGapSamples: 0,
    sourceGapMax: 0,
    sourceGapsOverFrame: 0,
    lastRenderAt: 0,
    renderGapTotal: 0,
    renderGapSamples: 0,
    renderGapMax: 0,
    renderGapsOverFrame: 0,
  };
}

function noteMotionGap(stats, prefix, previousAt, now) {
  if (!previousAt || !Number.isFinite(now) || now < previousAt) {
    return;
  }
  const gap = now - previousAt;
  stats[`${prefix}GapTotal`] += gap;
  stats[`${prefix}GapSamples`] += 1;
  stats[`${prefix}GapMax`] = Math.max(stats[`${prefix}GapMax`], gap);
  if (gap > MOTION_FRAME_GAP_MS) {
    stats[`${prefix}GapsOverFrame`] += 1;
  }
}

function noteRemoteMotionArrival(state, seq, sampleAt) {
  const stats = state.motionStats;
  const now = Date.now();
  noteMotionGap(stats, 'arrival', stats.lastArrivalAt, now);
  stats.lastArrivalAt = now;
  stats.arrivals += 1;

  const numericSeq = Number(seq);
  if (Number.isSafeInteger(numericSeq) && numericSeq >= 0) {
    if (stats.lastSeq !== null && numericSeq > stats.lastSeq + 1) {
      stats.seqSkipped += numericSeq - stats.lastSeq - 1;
    }
    if (stats.lastSeq === null || numericSeq > stats.lastSeq) {
      stats.lastSeq = numericSeq;
    }
  }

  const numericSampleAt = Number(sampleAt);
  if (Number.isFinite(numericSampleAt) && numericSampleAt > 0) {
    noteMotionGap(stats, 'source', stats.lastSourceSampleAt, numericSampleAt);
    stats.lastSourceSampleAt = numericSampleAt;
  }
}

function noteRemoteMotionRender(state, interpolated) {
  const stats = state.motionStats;
  const now = Date.now();
  noteMotionGap(stats, 'render', stats.lastRenderAt, now);
  stats.lastRenderAt = now;
  stats.renders += 1;
  if (interpolated) {
    stats.interpolated += 1;
  }
}

function motionGapSummary(stats, prefix) {
  const samples = stats[`${prefix}GapSamples`];
  return {
    average: samples ? stats[`${prefix}GapTotal`] / samples : null,
    maximum: stats[`${prefix}GapMax`],
    overFrame: stats[`${prefix}GapsOverFrame`],
    samples,
  };
}

function remoteMotionStatsSnapshot(stats) {
  return {
    durationMs: Math.max(0, Date.now() - stats.startedAt),
    arrivals: stats.arrivals,
    renders: stats.renders,
    interpolated: stats.interpolated,
    coalesced: stats.coalesced,
    stale: stats.stale,
    seqSkipped: stats.seqSkipped,
    source: motionGapSummary(stats, 'source'),
    arrival: motionGapSummary(stats, 'arrival'),
    render: motionGapSummary(stats, 'render'),
  };
}

function formatMotionGap(summary) {
  if (!summary.samples) {
    return 'n/a';
  }
  return `${summary.average.toFixed(1)}/${summary.maximum.toFixed(1)}ms>${MOTION_FRAME_GAP_MS}=${summary.overFrame}`;
}

function logRemoteMotionStats(senderId, reason) {
  const state = remoteMouseStates[senderId];
  if (!state || !state.motionStats || state.motionStats.arrivals < 2) {
    return;
  }
  const snapshot = remoteMotionStatsSnapshot(state.motionStats);
  log(
    'MOTION_TRACE',
    `${peerName(senderId)} ${reason} duration=${snapshot.durationMs}ms recv=${snapshot.arrivals} render=${snapshot.renders} interpolated=${snapshot.interpolated} coalesced=${snapshot.coalesced} stale=${snapshot.stale} seqSkipped=${snapshot.seqSkipped} sourceAvg/max=${formatMotionGap(snapshot.source)} arrivalAvg/max=${formatMotionGap(snapshot.arrival)} renderAvg/max=${formatMotionGap(snapshot.render)}`
  );
  state.motionStats.lastLogAt = Date.now();
}

function maybeLogRemoteMotionStats(senderId, state) {
  const now = Date.now();
  if (state.motionStats.arrivals >= 2 &&
      now - state.motionStats.lastLogAt >= MOTION_TRACE_INTERVAL_MS) {
    logRemoteMotionStats(senderId, 'active');
  }
}

function remoteMouseState(senderId) {
  if (!remoteMouseStates[senderId]) {
    const pos = currentMousePos();
    remoteMouseStates[senderId] = {
      x: pos.x,
      y: pos.y,
      buttons: {},
      pendingMove: null,
      moveImmediate: null,
      moveTimer: null,
      lastCoordSeq: null,
      motionStats: createRemoteMotionStats(),
    };
  }
  return remoteMouseStates[senderId];
}

function activeRemoteMouseButton(state) {
  return ['left', 'right', 'middle', 'back', 'forward'].find(button => state.buttons[button]);
}

function applyRemoteMouseMove(senderId, x, y, interpolated) {
  const state = remoteMouseState(senderId);
  state.x = numericMouseCoordinate(x, state.x);
  state.y = numericMouseCoordinate(y, state.y);

  const button = activeRemoteMouseButton(state);
  const sent = button
    ? sendNativeMouseCommand('drag', state.x, state.y, button)
    : sendNativeMouseCommand('move', state.x, state.y, 'left');

  if (!sent) {
    if (button && typeof robot.dragMouse === 'function') {
      robot.dragMouse(state.x, state.y);
    } else {
      robot.moveMouse(state.x, state.y);
    }
  }
  oldPos = { x: state.x, y: state.y };
  noteRemoteMotionRender(state, !!interpolated);
}

function clearRemoteMouseMoveSchedule(state) {
  if (state.moveImmediate) {
    clearImmediate(state.moveImmediate);
    state.moveImmediate = null;
  }
  if (state.moveTimer) {
    clearTimeout(state.moveTimer);
    state.moveTimer = null;
  }
}

function scheduleRemoteMouseMove(senderId, state) {
  if (state.moveImmediate || state.moveTimer || !state.pendingMove) {
    return;
  }
  if (!state.motionStats.renders) {
    state.moveImmediate = setImmediate(() => {
      state.moveImmediate = null;
      flushPendingRemoteMouseMove(senderId);
    });
    return;
  }
  state.moveTimer = setTimeout(() => {
    state.moveTimer = null;
    flushPendingRemoteMouseMove(senderId);
  }, REMOTE_RENDER_INTERVAL_MS);
}

function interpolatedRemoteCoordinate(current, target, progress) {
  if (progress >= 1 || Math.abs(target - current) <= 0.01) {
    return target;
  }
  return current + ((target - current) * progress);
}

function flushPendingRemoteMouseMove(senderId, force) {
  const state = remoteMouseStates[senderId];
  if (!state || !state.pendingMove) {
    return false;
  }
  const move = state.pendingMove;
  clearRemoteMouseMoveSchedule(state);
  if (move.replaced) {
    state.motionStats.coalesced += move.replaced;
    move.replaced = 0;
  }

  if (force || !state.motionStats.renders) {
    state.pendingMove = null;
    applyRemoteMouseMove(senderId, move.x, move.y, false);
    return true;
  }

  const now = Date.now();
  let elapsed = now - move.startedAt;
  if (elapsed < REMOTE_RENDER_INTERVAL_MS) {
    move.startedAt -= REMOTE_RENDER_INTERVAL_MS - elapsed;
    elapsed = REMOTE_RENDER_INTERVAL_MS;
  }
  const progress = Math.min(1, elapsed / move.settleMs);
  const nextX = interpolatedRemoteCoordinate(move.startX, move.x, progress);
  const nextY = interpolatedRemoteCoordinate(move.startY, move.y, progress);
  const reachedTarget = nextX === move.x && nextY === move.y;

  if (reachedTarget) {
    state.pendingMove = null;
  }
  applyRemoteMouseMove(senderId, nextX, nextY, !reachedTarget);
  if (!reachedTarget) {
    scheduleRemoteMouseMove(senderId, state);
  }
  return true;
}

function remoteMouseMove(senderId, x, y, seq, sampleAt) {
  const state = remoteMouseState(senderId);
  noteRemoteMotionArrival(state, seq, sampleAt);
  const numericSeq = Number(seq);
  const hasSequence = Number.isSafeInteger(numericSeq) && numericSeq >= 0;
  if (hasSequence && state.lastCoordSeq !== null && numericSeq <= state.lastCoordSeq) {
    state.motionStats.stale += 1;
    maybeLogRemoteMotionStats(senderId, state);
    return false;
  }
  if (hasSequence) {
    state.lastCoordSeq = numericSeq;
  }

  const targetX = numericMouseCoordinate(x, state.x);
  const targetY = numericMouseCoordinate(y, state.y);
  const distance = Math.max(Math.abs(targetX - state.x), Math.abs(targetY - state.y));
  state.pendingMove = {
    x: targetX,
    y: targetY,
    replaced: state.pendingMove ? (state.pendingMove.replaced || 0) + 1 : 0,
    startedAt: Date.now(),
    startX: state.x,
    startY: state.y,
    settleMs: distance >= REMOTE_RENDER_FAST_DISTANCE_PX
      ? REMOTE_RENDER_FAST_SETTLE_MS
      : REMOTE_RENDER_SMOOTH_SETTLE_MS,
  };
  scheduleRemoteMouseMove(senderId, state);
  maybeLogRemoteMotionStats(senderId, state);
  return true;
}

function resetRemoteMouseStream(senderId) {
  const state = remoteMouseState(senderId);
  clearRemoteMouseMoveSchedule(state);
  state.pendingMove = null;
  state.lastCoordSeq = null;
  state.motionStats = createRemoteMotionStats();
}

function remoteMouseButton(senderId, action, button, x, y) {
  flushPendingRemoteMouseMove(senderId, true);
  const state = remoteMouseState(senderId);
  const normalizedButton = mouseButtonName(button);
  state.x = numericMouseCoordinate(x, state.x);
  state.y = numericMouseCoordinate(y, state.y);

  if (action === 'down') {
    state.buttons[normalizedButton] = 1;
  }

  if (!sendNativeMouseCommand(action, state.x, state.y, normalizedButton)) {
    try {
      robot.mouseToggle(action, normalizedButton);
    } catch (error) {
      log('REMOTE_MOUSE_TOGGLE_ERROR', `${normalizedButton}: ${error.message}`);
    }
  }

  if (action === 'up') {
    delete state.buttons[normalizedButton];
  }

  oldPos = { x: state.x, y: state.y };
}

function releaseRemoteMouse(senderId, reason) {
  const state = remoteMouseStates[senderId];
  if (!state) {
    return;
  }
  flushPendingRemoteMouseMove(senderId, true);
  logRemoteMotionStats(senderId, reason || 'released');
  clearRemoteMouseMoveSchedule(state);
  state.pendingMove = null;

  Object.keys(state.buttons || {}).forEach(button => {
    if (!sendNativeMouseCommand('up', state.x, state.y, button)) {
      try {
        robot.mouseToggle('up', button);
      } catch (error) {
        log('REMOTE_MOUSE_RELEASE_ERROR', `${button}: ${error.message}`);
      }
    }
  });
  delete remoteMouseStates[senderId];
  log('REMOTE_MOUSE_RELEASED', `${peerName(senderId)} ${reason || ''}`.trim());
}

function keyNameFromFrame(frame) {
  if (frame && frame.k) {
    return normalizeRobotKey(frame.k);
  }
  return keyNameFromCode(Number(frame && frame.c));
}

function remoteKeyState(senderId) {
  if (!remoteKeyStates[senderId]) {
    remoteKeyStates[senderId] = {
      down: {},
      keyCodes: {},
      transientModifiersByKey: {},
      synthesizedKeyUps: {},
    };
  }
  return remoteKeyStates[senderId];
}

function legacyModifierNames(frame) {
  if (!frame || !frame.m || frame.modifier) {
    return [];
  }
  return frame.m
    .map(code => possibleModifiers[code])
    .filter(Boolean)
    .filter((name, index, names) => names.indexOf(name) === index);
}

function isModifierName(name) {
  return MODIFIER_NAMES.indexOf(name) !== -1;
}

function activeRemoteModifiers(state) {
  return MODIFIER_NAMES.filter(name => state.down[name]);
}

function uniqueModifiers(mods) {
  return (mods || [])
    .filter(Boolean)
    .filter((name, index, names) => names.indexOf(name) === index);
}

function sendMacKeyCode(name, mods) {
  const code = MAC_KEY_CODES[name];
  if (code === undefined) {
    return false;
  }

  const uniqueMods = uniqueModifiers(mods);
  if (!uniqueMods.length) {
    return sendNativeKeyCommand('tap', code, []);
  }

  uniqueMods.forEach((mod, index) => {
    const modCode = MAC_MODIFIER_KEY_CODES[mod];
    if (modCode !== undefined) {
      sendNativeKeyCommand('down', modCode, uniqueMods.slice(0, index + 1));
    }
  });
  const sent = sendNativeKeyCommand('tap', code, uniqueMods);
  uniqueMods.slice().reverse().forEach((mod, index, reversed) => {
    const modCode = MAC_MODIFIER_KEY_CODES[mod];
    if (modCode !== undefined) {
      const stillDown = reversed.slice(index + 1).reverse();
      sendNativeKeyCommand('up', modCode, stillDown);
    }
  });
  return sent;
}

function sendModifiedKey(name, mods) {
  const sent = sendMacKeyCode(name, mods);
  log(sent ? 'REMOTE_MOD_KEY' : 'REMOTE_MOD_KEY_FAIL', `${name} mods=${(mods || []).join('+') || 'none'}`);
  return sent;
}

function remoteKeyDown(senderId, frame) {
  const name = keyNameFromFrame(frame);
  if (!name || name === 'unknown') {
    return;
  }

  const state = remoteKeyState(senderId);
  const macCode = macKeyCodeFromFrame(frame);
  const transientMods = legacyModifierNames(frame);
  if (isModifierName(name)) {
    state.down[name] = 1;
    if (macCode !== undefined) {
      state.keyCodes[name] = macCode;
    }
    const sent = macCode !== undefined && sendNativeKeyCommand('down', macCode, activeRemoteModifiers(state));
    if (!sent) {
      try {
        robot.keyToggle(name, 'down');
      } catch (error) {
        log('REMOTE_KEY_DOWN_ERROR', `${name}: ${error.message}`);
      }
    }
    log('REMOTE_MOD_DOWN', `${peerName(senderId)} ${name}`);
    return;
  }

  if (transientMods.length && !state.transientModifiersByKey[name]) {
    state.transientModifiersByKey[name] = transientMods;
    transientMods.forEach(mod => {
      state.down[mod] = 1;
      const modCode = MAC_MODIFIER_KEY_CODES[mod];
      if (modCode !== undefined) {
        state.keyCodes[mod] = modCode;
        sendNativeKeyCommand('down', modCode, activeRemoteModifiers(state));
      }
    });
  }

  const mods = activeRemoteModifiers(state);
  if (macCode !== undefined && sendNativeKeyCommand('down', macCode, mods)) {
    state.down[name] = 1;
    state.keyCodes[name] = macCode;
    return;
  }

  try {
    robot.keyToggle(name, 'down');
    state.down[name] = 1;
  } catch (error) {
    log('REMOTE_KEY_DOWN_ERROR', `${name}: ${error.message}`);
  }
}

function remoteKeyUp(senderId, frame) {
  const name = keyNameFromFrame(frame);
  if (!name || name === 'unknown') {
    return;
  }

  const state = remoteKeyStates[senderId];
  if (!state) {
    return;
  }

  if (isModifierName(name)) {
    const macCode = state.keyCodes[name] !== undefined ? state.keyCodes[name] : macKeyCodeFromFrame(frame);
    delete state.down[name];
    delete state.keyCodes[name];
    const sent = macCode !== undefined && sendNativeKeyCommand('up', macCode, activeRemoteModifiers(state));
    if (!sent) {
      try {
        robot.keyToggle(name, 'up');
      } catch (error) {
        log('REMOTE_KEY_UP_ERROR', `${name}: ${error.message}`);
      }
    }
    log('REMOTE_MOD_UP', `${peerName(senderId)} ${name}`);
    return;
  }

  const macCode = state.keyCodes[name] !== undefined ? state.keyCodes[name] : macKeyCodeFromFrame(frame);
  const mods = activeRemoteModifiers(state);
  const sent = macCode !== undefined && sendNativeKeyCommand('up', macCode, mods);
  if (!sent && state.down[name]) {
    try {
      robot.keyToggle(name, 'up');
    } catch (error) {
      log('REMOTE_KEY_UP_ERROR', `${name}: ${error.message}`);
    }
  }
  delete state.down[name];
  delete state.keyCodes[name];

  const transientMods = state.transientModifiersByKey[name] || [];
  delete state.transientModifiersByKey[name];
  transientMods.slice().reverse().forEach(mod => {
    const modCode = state.keyCodes[mod] !== undefined ? state.keyCodes[mod] : MAC_MODIFIER_KEY_CODES[mod];
    delete state.down[mod];
    delete state.keyCodes[mod];
    const released = modCode !== undefined && sendNativeKeyCommand('up', modCode, activeRemoteModifiers(state));
    if (!released) {
      try {
        robot.keyToggle(mod, 'up');
      } catch (error) {
        log('REMOTE_KEY_UP_ERROR', `${mod}: ${error.message}`);
      }
    }
  });
}

function releaseRemoteKeyName(state, name) {
  const macCode = state.keyCodes[name] !== undefined ? state.keyCodes[name] : macKeyCodeFromName(name);
  let mods = activeRemoteModifiers(state);
  if (isModifierName(name)) {
    delete state.down[name];
    delete state.keyCodes[name];
    mods = activeRemoteModifiers(state);
  }

  const sent = macCode !== undefined && sendNativeKeyCommand('up', macCode, mods);
  if (!sent) {
    try {
      robot.keyToggle(name, 'up');
    } catch (error) {
      log('REMOTE_KEY_RELEASE_ERROR', `${name}: ${error.message}`);
    }
  }

  if (!isModifierName(name)) {
    delete state.down[name];
    delete state.keyCodes[name];
  }
}

function releaseRemoteKeys(senderId, reason) {
  const state = remoteKeyStates[senderId];
  const keys = state ? Object.keys(state.down) : [];
  keys
    .filter(name => ['command', 'control', 'alt', 'shift'].indexOf(name) === -1)
    .reverse()
    .forEach(name => releaseRemoteKeyName(state, name));
  MODIFIER_NAMES
    .slice()
    .reverse()
    .filter(name => state && state.down[name])
    .forEach(name => releaseRemoteKeyName(state, name));
  if (state || keys.length) {
    delete remoteKeyStates[senderId];
    log('REMOTE_KEYS_RELEASED', `${peerName(senderId)} ${reason || ''}`.trim());
  }
}

function heldShortcutMods(senderId, requiredMods) {
  const state = senderId && remoteKeyStates[senderId];
  if (!state) {
    return null;
  }
  if (!requiredMods.every(mod => state.down[mod])) {
    return null;
  }
  return uniqueModifiers(activeRemoteModifiers(state).concat(requiredMods));
}

function performRemoteAppSwitch(senderId, reverse) {
  log('REMOTE_SHORTCUT_RECV', reverse ? 'command-shift-tab' : 'command-tab');
  const mods = reverse ? ['command', 'shift'] : ['command'];
  const heldMods = heldShortcutMods(senderId, mods);
  const sent = heldMods
    ? sendNativeKeyCommand('tap', MAC_KEY_CODES.tab, heldMods)
    : sendMacKeyCode('tab', mods);
  if (sent) {
    log('REMOTE_SHORTCUT_DONE', reverse ? 'command-shift-tab' : 'command-tab');
  }
}

function performRemoteKeyChord(senderId, mods, key) {
  log('REMOTE_SHORTCUT_RECV', `${mods.join('+')}-${key}`);
  const heldMods = heldShortcutMods(senderId, mods);
  const code = MAC_KEY_CODES[key];
  const sent = heldMods && code !== undefined
    ? sendNativeKeyCommand('tap', code, heldMods)
    : sendMacKeyCode(key, mods);
  if (sent) {
    log('REMOTE_SHORTCUT_DONE', `${mods.join('+')}-${key}`);
  }
}

function releaseRemoteModifiersExcept(senderId, keepMods) {
  const state = senderId && remoteKeyStates[senderId];
  if (!state) {
    return;
  }
  MODIFIER_NAMES
    .slice()
    .reverse()
    .filter(name => keepMods.indexOf(name) === -1 && state.down[name])
    .forEach(name => releaseRemoteKeyName(state, name));
}

function performRemoteClipboardCopy(senderId) {
  releaseRemoteModifiersExcept(senderId, ['command']);
  performRemoteKeyChord(senderId, ['command'], 'c');
  if (senderId) {
    setTimeout(() => {
      syncClipboardToPeer(senderId, 'shortcut-copy', true);
    }, CLIPBOARD_COPY_DELAY_MS);
  }
}

function performRemoteClipboardPaste(senderId) {
  releaseRemoteModifiersExcept(senderId, ['command']);
  performRemoteKeyChord(senderId, ['command'], 'v');
}

function peerIdForSocket(socket) {
  const ids = Object.keys(peers);
  for (let i = 0; i < ids.length; i += 1) {
    if (peers[ids[i]].socket === socket) {
      return ids[i];
    }
  }
  return null;
}

function peerName(hostId) {
  return peers[hostId] ? peers[hostId].nickname : hostId;
}

function startInboundControl(socket) {
  const senderId = peerIdForSocket(socket);
  if (!connectionsEnabled(store) || !senderId) {
    return;
  }
  if (mode === 'offscreen') {
    log('CONTROL_START_IGNORED', `${peerName(senderId)} while controlling ${peerName(activePeerId)}`);
    return;
  }

  const alreadyReceiving = mode === 'receiving' && inboundControlPeerId === senderId;
  if (!alreadyReceiving) {
    log('CONTROL_START', peerName(senderId));
  }
  mode = 'receiving';
  inboundControlPeerId = senderId;
  inboundControlUntil = Date.now() + REMOTE_INPUT_TIMEOUT_MS;
  oldPos = robot.getMousePos();
  if (!alreadyReceiving) {
    resetRemoteMouseStream(senderId);
    pushState();
  }
}

function endInboundControl(senderId, reason) {
  if (inboundControlPeerId !== senderId) {
    return;
  }
  log('CONTROL_END', `${peerName(senderId)} ${reason || ''}`.trim());
  releaseRemoteKeys(senderId, reason || 'control ended');
  releaseRemoteMouse(senderId, reason || 'control ended');
  syncClipboardToPeer(senderId, 'control-end', true);
  inboundControlPeerId = null;
  inboundControlUntil = 0;
  mode = 'onscreen';
  oldPos = robot.getMousePos();
  edgeSuppressedUntil = Date.now() + 600;
  pushState();
}

function acceptRemoteInput(socket, frameType) {
  const senderId = peerIdForSocket(socket);
  if (!senderId) {
    return false;
  }

  return acceptRemoteInputFromPeer(senderId, frameType);
}

function acceptRemoteInputFromPeer(senderId, frameType) {
  const peer = peers[senderId];
  if (!connectionsEnabled(store) ||
      !isPeerPaired(peer) ||
      !peer.socket ||
      !peer.socket.openmouseAuthenticated ||
      mode !== 'receiving' ||
      inboundControlPeerId !== senderId) {
    const now = Date.now();
    if (now - lastIgnoredInputLogAt > 5000) {
      log('REMOTE_INPUT_IGNORED', `${frameType} from ${peerName(senderId)} without active control session`);
      lastIgnoredInputLogAt = now;
    }
    return false;
  }

  inboundControlUntil = Date.now() + REMOTE_INPUT_TIMEOUT_MS;
  return true;
}

function sendToPeer(hostId, type, payload) {
  const peer = peers[hostId];
  if (!connectionsEnabled(store) ||
      !peer ||
      !isPeerPaired(peer) ||
      !peer.socket ||
      peer.socket.destroyed) {
    return false;
  }
  return sendFrame(peer.socket, type, payload);
}

function sendInputToPeer(hostId, type, payload) {
  const sent = sendToPeer(hostId, type, payload);
  if (sent) {
    noteInputFrameSent(hostId, type);
  }
  return sent;
}

function handleLatencyPong(socket, frame) {
  const senderId = peerIdForSocket(socket);
  const peer = senderId && peers[senderId];
  if (!peer || !frame || !frame.sentAt) {
    return;
  }

  const sentAt = Number(frame.sentAt);
  if (!Number.isFinite(sentAt)) {
    return;
  }

  const latency = ensurePeerMetrics(peer).latency;
  if (latency.pendingId && frame.id && frame.id !== latency.pendingId) {
    return;
  }

  const now = Date.now();
  const rttMs = Math.max(0, now - sentAt);
  latency.pendingId = null;
  latency.pendingSentAt = 0;
  latency.rttMs = rttMs;
  latency.minRttMs = latency.minRttMs === null ? rttMs : Math.min(latency.minRttMs, rttMs);
  latency.maxRttMs = latency.maxRttMs === null ? rttMs : Math.max(latency.maxRttMs, rttMs);
  latency.avgRttMs = latency.avgRttMs === null
    ? rttMs
    : ((latency.avgRttMs * latency.samples) + rttMs) / (latency.samples + 1);
  latency.samples += 1;
  latency.received += 1;
  latency.consecutiveMisses = 0;
  latency.lastPongAt = now;
  pushState();
}

function probePeerLatency(peer) {
  if (!peer || !peer.socket || peer.socket.destroyed) {
    return;
  }

  const latency = ensurePeerMetrics(peer).latency;
  const now = Date.now();
  if (latency.pendingId && now - latency.pendingSentAt >= LATENCY_PROBE_TIMEOUT_MS) {
    latency.pendingId = null;
    latency.pendingSentAt = 0;
    latency.missed += 1;
    latency.consecutiveMisses += 1;
    log('LATENCY_MISS', `${peer.nickname} missed=${latency.consecutiveMisses}/${LATENCY_MAX_CONSECUTIVE_MISSES}`);
    if (latency.consecutiveMisses >= LATENCY_MAX_CONSECUTIVE_MISSES) {
      log('LATENCY_STALE', `${peer.nickname} closing stale socket after ${latency.consecutiveMisses} missed probes`);
      peer.socket.destroy();
      return;
    }
  }
  if (latency.pendingId) {
    return;
  }

  const id = `${now}-${Math.random().toString(36).slice(2)}`;
  latency.pendingId = id;
  latency.pendingSentAt = now;
  latency.lastProbeAt = now;
  latency.sent += 1;
  sendFrame(peer.socket, 'latency:ping', { id, sentAt: now });
}

function probeConnectedPeerLatencies() {
  Object.keys(peers).forEach(hostId => {
    probePeerLatency(peers[hostId]);
  });
}

function startLatencyProbes() {
  if (latencyProbeTimer) {
    return;
  }
  latencyProbeTimer = setInterval(probeConnectedPeerLatencies, LATENCY_PROBE_INTERVAL_MS);
}

function clipboardText() {
  if (!clipboard || !clipboard.readText) {
    return '';
  }
  try {
    return clipboard.readText() || '';
  } catch (error) {
    log('CLIPBOARD_READ_ERROR', error.message);
    return '';
  }
}

function clipboardHash(text) {
  return crypto.createHash('sha256').update(String(text || '')).digest('hex');
}

function syncClipboardToPeer(hostId, reason, force) {
  const peer = peers[hostId];
  if (!peer || !peer.socket || peer.socket.destroyed) {
    return false;
  }

  const text = clipboardText();
  const bytes = Buffer.byteLength(text, 'utf8');
  if (bytes > MAX_CLIPBOARD_TEXT_BYTES) {
    log('CLIPBOARD_SKIP', `${peerName(hostId)} ${reason} ${bytes} bytes`);
    return false;
  }

  const hash = clipboardHash(text);
  if (!force && peer.lastClipboardSentHash === hash) {
    return false;
  }

  peer.lastClipboardSentHash = hash;
  lastLocalClipboardHash = hash;
  const sent = sendToPeer(hostId, 'clipboard', {
    text,
    hash,
    sourceHostId: store.hostId,
  });
  if (sent) {
    log('CLIPBOARD_SEND', `${peerName(hostId)} ${reason} ${bytes} bytes`);
  }
  return sent;
}

function broadcastClipboard(reason) {
  Object.keys(peers).forEach(hostId => {
    syncClipboardToPeer(hostId, reason, false);
  });
}

function applyRemoteClipboard(senderId, frame) {
  if (!clipboard || !clipboard.writeText || frame.sourceHostId === store.hostId) {
    return;
  }

  const text = String(frame.text || '');
  const bytes = Buffer.byteLength(text, 'utf8');
  if (bytes > MAX_CLIPBOARD_TEXT_BYTES) {
    log('CLIPBOARD_RECV_SKIP', `${peerName(senderId)} ${bytes} bytes`);
    return;
  }

  const hash = frame.hash || clipboardHash(text);
  if (hash === lastLocalClipboardHash) {
    return;
  }

  try {
    clipboard.writeText(text);
    lastLocalClipboardHash = hash;
    if (peers[senderId]) {
      peers[senderId].lastClipboardSentHash = hash;
    }
    log('CLIPBOARD_RECV', `${peerName(senderId)} ${bytes} bytes`);
    pushState();
  } catch (error) {
    log('CLIPBOARD_WRITE_ERROR', error.message);
  }
}

function startClipboardSync() {
  lastLocalClipboardHash = clipboardHash(clipboardText());
  log('CLIPBOARD', 'boundary sync enabled');
}

function targetForBoundary(edge, sourceDisplayId, pointerPosition) {
  if (!connectionsEnabled(store)) {
    return null;
  }
  const targets = displayTargets();
  const sourceId = String(sourceDisplayId || defaultSourceDisplayIdForEdge(edge) || '');
  const localBounds = localWorkspaceBounds();
  const candidates = targets.filter(target => {
    const peer = peers[target.hostId];
    return target.edge === edge &&
      String(target.sourceDisplayId || '') === sourceId &&
      peer &&
      peer.socket &&
      !peer.socket.destroyed;
  }).map(target => ({
    target,
    projectedRect: adjacentRect(
      localBounds,
      peerWorkspaceBounds(target.hostId, target.bounds),
      edge,
      target.crossOffset
    ),
  }));
  const selected = selectEdgeCandidate(
    candidates,
    edge,
    pointerPosition || {
      x: localBounds.x + (localBounds.width / 2),
      y: localBounds.y + (localBounds.height / 2),
    }
  );
  return selected && selected.target || null;
}

function valueInRange(value, start, end) {
  return value >= start && value < end;
}

function pointerInDisplayAxis(pos, bounds, edge) {
  if (edge === 'left' || edge === 'right') {
    return valueInRange(pos.y, bounds.y, boundsBottom(bounds));
  }
  return valueInRange(pos.x, bounds.x, boundsRight(bounds));
}

function pointerAtDisplayEdge(pos, bounds, edge) {
  if (!pointerInDisplayAxis(pos, bounds, edge)) {
    return false;
  }

  if (edge === 'right') {
    return pos.x >= boundsRight(bounds) - 2;
  }
  if (edge === 'left') {
    return pos.x <= bounds.x;
  }
  if (edge === 'top') {
    return pos.y <= bounds.y;
  }
  if (edge === 'bottom') {
    return pos.y >= boundsBottom(bounds) - 2;
  }
  return false;
}

function nearCoordinate(a, b) {
  return Math.abs(a - b) <= 2;
}

function hasAdjacentLocalDisplay(display, edge, pos, displays) {
  const bounds = display.bounds;
  return (displays || []).some(other => {
    if (!other || other.id === display.id) {
      return false;
    }
    const otherBounds = other.bounds;
    if (edge === 'left') {
      return nearCoordinate(bounds.x, boundsRight(otherBounds)) &&
        valueInRange(pos.y, otherBounds.y, boundsBottom(otherBounds));
    }
    if (edge === 'right') {
      return nearCoordinate(boundsRight(bounds), otherBounds.x) &&
        valueInRange(pos.y, otherBounds.y, boundsBottom(otherBounds));
    }
    if (edge === 'top') {
      return nearCoordinate(bounds.y, boundsBottom(otherBounds)) &&
        valueInRange(pos.x, otherBounds.x, boundsRight(otherBounds));
    }
    if (edge === 'bottom') {
      return nearCoordinate(boundsBottom(bounds), otherBounds.y) &&
        valueInRange(pos.x, otherBounds.x, boundsRight(otherBounds));
    }
    return false;
  });
}

function localBoundaryForPointer(pos) {
  const displays = localDisplays();
  const edges = ['right', 'left', 'top', 'bottom'];
  const candidates = [];

  displays.forEach(display => {
    const bounds = normalizeBounds(display.bounds);
    edges.forEach(edge => {
      if (!pointerAtDisplayEdge(pos, bounds, edge)) {
        return;
      }
      if (hasAdjacentLocalDisplay(display, edge, pos, displays)) {
        return;
      }
      candidates.push({
        edge,
        display,
        bounds,
        target: targetForBoundary(edge, display.id, pos),
      });
    });
  });

  return candidates.find(candidate => candidate.target) || candidates[0] || null;
}

function peerWorkspaceBounds(hostId, fallbackBounds) {
  const peer = peers[hostId];
  const displays = peer && peer.displays && peer.displays.length
    ? peer.displays
    : [];
  return displayUnion(displays) || normalizeBounds(fallbackBounds);
}

function nativeKeyTapPath() {
  return path.join(__dirname, 'bin', 'openmouse-keytap');
}

function nativeKeySendPath() {
  return path.join(__dirname, 'bin', 'openmouse-keysend');
}

function startNativeKeySend() {
  if (process.platform !== 'darwin' || nativeKeySend) {
    return !!nativeKeySend;
  }

  const helper = nativeKeySendPath();
  if (!fs.existsSync(helper)) {
    log('KEYSEND_MISSING', helper);
    return false;
  }

  nativeKeySend = childProcess.spawn(helper, [], {
    stdio: ['pipe', 'ignore', 'pipe'],
  });

  nativeKeySend.stderr.on('data', chunk => {
    const message = chunk.toString('utf8').trim();
    if (message) {
      log('KEYSEND_ERROR', message);
    }
  });

  nativeKeySend.on('error', error => {
    log('KEYSEND_START_ERROR', error.message);
  });

  nativeKeySend.on('exit', (code, signal) => {
    if (nativeKeySend) {
      log('KEYSEND_EXIT', `code=${code} signal=${signal || ''}`);
    }
    nativeKeySend = null;
  });

  log('KEYSEND_START', helper);
  return true;
}

function stopNativeKeySend() {
  if (!nativeKeySend) {
    return;
  }
  const proc = nativeKeySend;
  nativeKeySend = null;
  try {
    proc.stdin.write('quit 0\n');
    proc.stdin.end();
  } catch (error) {
    proc.kill();
  }
  log('KEYSEND_STOP', 'stopped');
}

function sendNativeKeyCommand(action, code, mods) {
  if (!startNativeKeySend() || !nativeKeySend || !nativeKeySend.stdin.writable) {
    return false;
  }

  const modText = (mods || []).filter(Boolean).join(',');
  const line = modText ? `${action} ${code} ${modText}\n` : `${action} ${code}\n`;
  try {
    nativeKeySend.stdin.write(line);
    return true;
  } catch (error) {
    log('KEYSEND_WRITE_ERROR', error.message);
    return false;
  }
}

function handleNativeKeyTapLine(line) {
  let frame;
  try {
    frame = JSON.parse(line);
  } catch (error) {
    log('KEYTAP_PARSE_ERROR', error.message);
    return;
  }

  if (frame.type === 'ready') {
    nativePointerCaptureReady = !!frame.pointer;
    if (nativePointerCaptureReady && mode === 'offscreen' && activePeerId) {
      robot.moveMouse(CENTER.x, CENTER.y);
      oldPos = { x: CENTER.x, y: CENTER.y };
      log(
        'POINTER_CAPTURE_READY',
        `native deltas enabled cursorLocked=${!!frame.cursorLocked} cursorError=${Number(frame.cursorError) || 0}`
      );
    }
    return;
  }
  if (frame.type === 'pointer') {
    if (mode === 'offscreen' && activePeerId) {
      applyOffscreenPointerDelta(frame.dx, frame.dy);
    }
    return;
  }
  if (frame.type === 'scroll') {
    if (mode === 'offscreen' && activePeerId) {
      sendInputToPeer(activePeerId, 'wh', {
        x: numericMouseCoordinate(frame.x, 0),
        y: numericMouseCoordinate(frame.y, 0),
      });
    }
    return;
  }
  if (frame.type === 'mouse') {
    if (mode === 'offscreen' && activePeerId &&
        (frame.action === 'down' || frame.action === 'up')) {
      sendInputToPeer(activePeerId, frame.action === 'down' ? 'md' : 'mu', {
        button: mouseButtonName(frame.button),
        x: remoteX,
        y: remoteY,
      });
    }
    return;
  }
  if (frame.type === 'shortcut') {
    const code = shortcutReleaseCode(frame.name);
    if (code) {
      suppressKeyup(code);
    }
    sendRemoteShortcut(frame.name);
    return;
  }
  if (frame.type !== 'key') {
    return;
  }
  if (frame.action === 'down') {
    handleCapturedKeyDown(frame);
  } else if (frame.action === 'up') {
    handleCapturedKeyUp(frame);
  }
}

function startNativeKeyTap() {
  if (process.platform !== 'darwin') {
    return false;
  }
  if (nativeKeyTap) {
    return true;
  }

  const helper = nativeKeyTapPath();
  if (!fs.existsSync(helper)) {
    log('KEYTAP_MISSING', helper);
    return false;
  }

  nativeKeyTapBuffer = '';
  nativePointerCaptureReady = false;
  nativeKeyTap = childProcess.spawn(helper, [], {
    stdio: ['ignore', 'pipe', 'pipe'],
  });

  nativeKeyTap.stdout.on('data', chunk => {
    nativeKeyTapBuffer += chunk.toString('utf8');
    let newline = nativeKeyTapBuffer.indexOf('\n');
    while (newline !== -1) {
      const line = nativeKeyTapBuffer.slice(0, newline).trim();
      nativeKeyTapBuffer = nativeKeyTapBuffer.slice(newline + 1);
      if (line) {
        handleNativeKeyTapLine(line);
      }
      newline = nativeKeyTapBuffer.indexOf('\n');
    }
  });

  nativeKeyTap.stderr.on('data', chunk => {
    const message = chunk.toString('utf8').trim();
    if (message) {
      log('KEYTAP_ERROR', message);
    }
  });

  nativeKeyTap.on('error', error => {
    nativePointerCaptureReady = false;
    log('KEYTAP_START_ERROR', error.message);
  });

  nativeKeyTap.on('exit', (code, signal) => {
    if (nativeKeyTap) {
      log('KEYTAP_EXIT', `code=${code} signal=${signal || ''}`);
    }
    nativeKeyTap = null;
    nativeKeyTapBuffer = '';
    nativePointerCaptureReady = false;
    if (mode === 'offscreen' && activePeerId) {
      eventCapture.start({ focus: true });
    }
    updateRemoteShortcuts();
  });

  log('KEYTAP_START', helper);
  return true;
}

function stopNativeKeyTap() {
  if (!nativeKeyTap) {
    return;
  }
  const proc = nativeKeyTap;
  nativeKeyTap = null;
  nativeKeyTapBuffer = '';
  nativePointerCaptureReady = false;
  proc.kill();
  log('KEYTAP_STOP', 'stopped');
}

function startOffscreen(boundary, pos, target) {
  if (!connectionsEnabled(store)) {
    return;
  }
  if (!accessibilityTrusted(false)) {
    const now = Date.now();
    if (now - lastControlBlockedLogAt > 1500) {
      log('CONTROL_BLOCKED', 'Accessibility permission is required to move the local cursor');
      lastControlBlockedLogAt = now;
    }
    pushState();
    return;
  }
  if (target.permissions && target.permissions.accessibility === false) {
    const now = Date.now();
    if (now - lastControlBlockedLogAt > 1500) {
      log('CONTROL_BLOCKED', `Accessibility permission is required on ${target.peerNickname || target.nickname}`);
      lastControlBlockedLogAt = now;
    }
    pushState();
    return;
  }

  const edge = boundary.edge;
  const localBounds = normalizeBounds(boundary.bounds);
  const targetBounds = normalizeBounds(target.bounds || { x: 0, y: 0, width: target.screen.width, height: target.screen.height });
  const remoteBounds = peerWorkspaceBounds(target.hostId, targetBounds);
  activePeerId = target.hostId;
  activeTargetId = target.targetId;
  activeEdge = edge;
  activeLocalBounds = localBounds;
  activeRemoteBounds = remoteBounds;
  mode = 'offscreen';
  releaseMatcher.reset();
  suppressedKeyups = {};
  log('OFFSCREEN_START', `${edge} local=${pos.x},${pos.y} source=${localBounds.x},${localBounds.y},${localBounds.width}x${localBounds.height} remote=${target.nickname} target=${targetBounds.x},${targetBounds.y},${targetBounds.width}x${targetBounds.height} workspace=${remoteBounds.x},${remoteBounds.y},${remoteBounds.width}x${remoteBounds.height}`);

  if (edge === 'right' || edge === 'left') {
    const percentY = Math.max(0, Math.min(1, (pos.y - localBounds.y) / Math.max(1, localBounds.height)));
    remoteY = targetBounds.y + (percentY * targetBounds.height);
    remoteX = edge === 'right' ? targetBounds.x : targetBounds.x + targetBounds.width - 1;
  } else {
    const percentX = Math.max(0, Math.min(1, (pos.x - localBounds.x) / Math.max(1, localBounds.width)));
    remoteX = targetBounds.x + (percentX * targetBounds.width);
    remoteY = edge === 'bottom' ? targetBounds.y : targetBounds.y + targetBounds.height - 1;
  }

  remoteX = Math.max(remoteBounds.x, Math.min(remoteBounds.x + remoteBounds.width - 1, remoteX));
  remoteY = Math.max(remoteBounds.y, Math.min(remoteBounds.y + remoteBounds.height - 1, remoteY));
  const nativeTapStarted = startNativeKeyTap();
  robot.moveMouse(CENTER.x, CENTER.y);
  const afterCenter = robot.getMousePos();
  if (Math.abs(afterCenter.x - CENTER.x) > 4 || Math.abs(afterCenter.y - CENTER.y) > 4) {
    log('MOUSE_MOVE_BLOCKED', `wanted=${CENTER.x},${CENTER.y} got=${afterCenter.x},${afterCenter.y} accessibility=${accessibilityTrusted(false)}`);
  }
  oldPos = { x: CENTER.x, y: CENTER.y };
  eventCapture.start({ focus: !nativeTapStarted });
  sendToPeer(activePeerId, 'control:start', { edge });
  beginCoordStream(activePeerId, { x: remoteX, y: remoteY });
  syncClipboardToPeer(activePeerId, 'control-start', true);
  nextControlHeartbeatAt = 0;
  updateRemoteShortcuts();
  pushState();
}

function returnPointInsideLocalBoundary(localBounds, edge) {
  const bounds = normalizeBounds(localBounds);
  const minX = bounds.x;
  const minY = bounds.y;
  const maxX = bounds.x + bounds.width - 1;
  const maxY = bounds.y + bounds.height - 1;
  const point = {
    x: Math.floor(bounds.x + (bounds.width / 2)),
    y: Math.floor(bounds.y + (bounds.height / 2)),
  };

  if (edge === 'right') {
    point.x = clampCoordinate(bounds.x + bounds.width - RETURN_EDGE_REARM_INSET, minX, maxX);
  } else if (edge === 'left') {
    point.x = clampCoordinate(bounds.x + RETURN_EDGE_REARM_INSET, minX, maxX);
  } else if (edge === 'top') {
    point.y = clampCoordinate(bounds.y + RETURN_EDGE_REARM_INSET, minY, maxY);
  } else if (edge === 'bottom') {
    point.y = clampCoordinate(bounds.y + bounds.height - RETURN_EDGE_REARM_INSET, minY, maxY);
  }

  return point;
}

function returnOnscreen() {
  const localBounds = activeLocalBounds || localWorkspaceBounds();
  const returnPoint = returnPointInsideLocalBoundary(localBounds, activeEdge);
  const x = returnPoint.x;
  const y = returnPoint.y;

  const peerId = activePeerId;
  if (peerId) {
    clearPeerCoordQueue(peers[peerId]);
    sendToPeer(peerId, 'control:end');
    syncClipboardToPeer(peerId, 'return-onscreen', true);
  }

  mode = 'onscreen';
  activePeerId = null;
  activeTargetId = null;
  activeEdge = null;
  activeLocalBounds = null;
  activeRemoteBounds = null;
  nextControlHeartbeatAt = 0;
  releaseMatcher.reset();
  suppressedKeyups = {};
  Object.keys(modifiers).forEach(code => {
    delete modifiers[code];
  });
  stopNativeKeyTap();
  eventCapture.stop();
  updateRemoteShortcuts();
  robot.moveMouse(x, y);
  oldPos = { x, y };
  edgeSuppressedUntil = Date.now() + 600;
  log('ONSCREEN_RETURN', `${x},${y}`);
  pushState();
}

function releaseRemoteControl() {
  if (mode !== 'offscreen' || !activePeerId) {
    return;
  }
  const peerId = activePeerId;
  returnOnscreen();
  log('RELEASE', `returned from ${peerId}`);
}

function suppressKeyup(code) {
  suppressedKeyups[code] = (suppressedKeyups[code] || 0) + 1;
}

function shouldSuppressKeyup(code) {
  if (!suppressedKeyups[code]) {
    return false;
  }
  suppressedKeyups[code] -= 1;
  if (!suppressedKeyups[code]) {
    delete suppressedKeyups[code];
  }
  return true;
}

function applyOffscreenPointerDelta(dx, dy) {
  dx = Number(dx);
  dy = Number(dy);
  if (mode !== 'offscreen' || !activePeerId ||
      !Number.isFinite(dx) || !Number.isFinite(dy) ||
      (dx === 0 && dy === 0)) {
    return false;
  }

  const peer = peers[activePeerId];
  const remoteBounds = activeRemoteBounds ||
    (peer && peer.screen && { x: 0, y: 0, width: peer.screen.width, height: peer.screen.height });
  if (!peer || !remoteBounds) {
    returnOnscreen();
    return false;
  }

  remoteX += dx;
  remoteY += dy;

  if ((activeEdge === 'right' && remoteX < remoteBounds.x) ||
      (activeEdge === 'left' && remoteX >= remoteBounds.x + remoteBounds.width) ||
      (activeEdge === 'top' && remoteY >= remoteBounds.y + remoteBounds.height) ||
      (activeEdge === 'bottom' && remoteY < remoteBounds.y)) {
    returnOnscreen();
    return false;
  }

  remoteX = Math.max(remoteBounds.x, Math.min(remoteBounds.x + remoteBounds.width - 1, remoteX));
  remoteY = Math.max(remoteBounds.y, Math.min(remoteBounds.y + remoteBounds.height - 1, remoteY));
  sendCoordToPeer(activePeerId, { x: remoteX, y: remoteY });
  return true;
}

function pollPointer() {
  if (mode === 'receiving') {
    if (Date.now() > inboundControlUntil) {
      endInboundControl(inboundControlPeerId, 'timeout');
    }
    return;
  }

  if (mode === 'offscreen' && activePeerId) {
    const now = Date.now();
    if (now >= nextControlHeartbeatAt) {
      sendToPeer(activePeerId, 'control:ping');
      nextControlHeartbeatAt = now + CONTROL_HEARTBEAT_MS;
    }
    if (nativePointerCaptureReady) {
      return;
    }
  }

  const pos = robot.getMousePos();
  if (pos.x === oldPos.x && pos.y === oldPos.y) {
    return;
  }
  oldPos = pos;

  if (mode === 'onscreen') {
    if (Date.now() < edgeSuppressedUntil) {
      return;
    }
    const boundary = localBoundaryForPointer(pos);
    const target = boundary && boundary.target;
    if (boundary) {
      const now = Date.now();
      if (now - lastEdgeLogAt > 500) {
        const bounds = boundary.bounds;
        log('EDGE', `${boundary.edge} display=${boundary.display.name || boundary.display.id} pos=${pos.x},${pos.y} bounds=${bounds.x},${bounds.y},${bounds.width}x${bounds.height} target=${target ? target.nickname : 'none'}`);
        lastEdgeLogAt = now;
      }
    }
    if (target) {
      startOffscreen(boundary, pos, target);
    }
    return;
  }

  const dx = pos.x - CENTER.x;
  const dy = pos.y - CENTER.y;
  if (dx === 0 && dy === 0) {
    return;
  }

  if (applyOffscreenPointerDelta(dx, dy) && mode === 'offscreen') {
    robot.moveMouse(CENTER.x, CENTER.y);
    oldPos = { x: CENTER.x, y: CENTER.y };
  }
}

function startPointerControl() {
  if (pointerTimer) {
    return;
  }
  pointerTimer = setInterval(pollPointer, POINTER_POLL_MS);
}

function rememberModifierDown(code, macCode) {
  const name = possibleModifiers[code];
  if (!name) {
    return false;
  }

  const alreadyActive = isModifierActive(name);
  modifiers[code] = 1;
  if (activePeerId && !alreadyActive) {
    sendInputToPeer(activePeerId, 'kd', { c: code, k: name, mac: macCode, modifier: true });
  }
  return true;
}

function rememberModifierUp(code, macCode) {
  const name = possibleModifiers[code];
  if (!name) {
    return false;
  }

  const wasActive = isModifierActive(name);
  delete modifiers[code];
  if (activePeerId && wasActive && !isModifierActive(name)) {
    sendInputToPeer(activePeerId, 'ku', { c: code, k: name, mac: macCode, modifier: true });
  }
  return true;
}

function capturedShortcutForCode(code) {
  const clipboardShortcut = clipboardShortcutForCode(code, activeModifierNames());
  if (clipboardShortcut) {
    return clipboardShortcut;
  }
  if (code === 9 && (modifiers[91] || modifiers[93])) {
    return modifiers[16] ? 'command-shift-tab' : 'command-tab';
  }
  if ((code === 37 || code === 38 || code === 39 || code === 40) && modifiers[17]) {
    const direction = {
      37: 'left',
      38: 'up',
      39: 'right',
      40: 'down',
    }[code];
    return `control-${direction}`;
  }
  return null;
}

function shortcutReleaseCode(name) {
  if (name === CLIPBOARD_COPY_SHORTCUT) {
    return 67;
  }
  if (name === CLIPBOARD_PASTE_SHORTCUT) {
    return 86;
  }
  if (name === 'command-tab' || name === 'command-shift-tab') {
    return 9;
  }
  if (name === 'control-left') {
    return 37;
  }
  if (name === 'control-up') {
    return 38;
  }
  if (name === 'control-right') {
    return 39;
  }
  if (name === 'control-down') {
    return 40;
  }
  return 0;
}

function handleCapturedKeyDown(event) {
  const code = keyCodeFromEvent(event);
  const name = keyNameFromEvent(event);
  const macCode = macKeyCodeFromEvent(event);
  const nativeTap = isNativeKeyTapEvent(event);
  if (!code && !name) {
    return;
  }

  if (activePeerId) {
    const release = releaseMatcher.push(code);
    const passPartialRelease = shouldPassPartialReleaseKey(code, release);
    if (release.consume && !passPartialRelease) {
      suppressKeyup(code);
    }
    if (release.matched) {
      releaseRemoteControl();
      return;
    }
    if (release.consume && !passPartialRelease) {
      return;
    }
  } else {
    releaseMatcher.reset();
  }

  if (rememberModifierDown(code, macCode)) {
    return;
  }

  if (activePeerId) {
    const shortcut = capturedShortcutForCode(code);
    if (shortcut && (!nativeTap || isClipboardTransferShortcut(shortcut))) {
      sendRemoteShortcut(shortcut);
      suppressKeyup(code);
      return;
    }
  }

  if (activePeerId) {
    sendInputToPeer(activePeerId, 'kd', { c: code, k: name, mac: macCode });
  }
}

function handleCapturedKeyUp(event) {
  const code = keyCodeFromEvent(event);
  const name = keyNameFromEvent(event);
  const macCode = macKeyCodeFromEvent(event);
  if (shouldSuppressKeyup(code)) {
    return;
  }
  if (rememberModifierUp(code, macCode)) {
    return;
  }
  if (activePeerId) {
    sendInputToPeer(activePeerId, 'ku', { c: code, k: name, mac: macCode });
  }
}

function wireEventCapture() {
  eventCapture.on('mousedown', button => {
    if (activePeerId) {
      sendInputToPeer(activePeerId, 'md', {
        button: mouseButtonName(button),
        x: remoteX,
        y: remoteY,
      });
    }
  });
  eventCapture.on('mouseup', button => {
    if (activePeerId) {
      sendInputToPeer(activePeerId, 'mu', {
        button: mouseButtonName(button),
        x: remoteX,
        y: remoteY,
      });
    }
  });
  eventCapture.on('keydown', handleCapturedKeyDown);
  eventCapture.on('keyup', handleCapturedKeyUp);
  eventCapture.on('scroll', data => {
    if (activePeerId) {
      sendInputToPeer(activePeerId, 'wh', scrollPayload(data));
    }
  });
  eventCapture.on('shortcut', name => {
    if (nativeKeyTap) {
      return;
    }
    sendRemoteShortcut(name);
  });
}

function sendRemoteShortcut(name) {
  if (!activePeerId) {
    return;
  }
  const now = Date.now();
  if (name === lastRemoteShortcutName && now - lastRemoteShortcutAt < SHORTCUT_DEDUP_MS) {
    return;
  }
  lastRemoteShortcutName = name;
  lastRemoteShortcutAt = now;
  if (name === CLIPBOARD_PASTE_SHORTCUT) {
    syncClipboardToPeer(activePeerId, 'shortcut-paste', true);
  }
  sendShortcutToPeer(activePeerId, name, false);
}

function sendShortcutToPeer(hostId, name, test) {
  const sent = sendToPeer(hostId, 'shortcut', { name, test: !!test });
  if (sent && !test) {
    noteInputFrameSent(hostId, 'shortcut');
  }
  log(test ? 'REMOTE_SHORTCUT_TEST_SEND' : 'REMOTE_SHORTCUT_SEND', `${peerName(hostId)} ${name} ${sent ? 'sent' : 'not connected'}`);
  return sent;
}

function registerRemoteShortcuts() {
  if (remoteShortcutsRegistered || !globalShortcut) {
    return;
  }

  const commandTab = globalShortcut.register('Command+Tab', () => {
    sendRemoteShortcut('command-tab');
  });
  const commandShiftTab = globalShortcut.register('Command+Shift+Tab', () => {
    sendRemoteShortcut('command-shift-tab');
  });
  const controlTab = globalShortcut.register('Control+Tab', () => {
    sendRemoteShortcut('command-tab');
  });
  const controlShiftTab = globalShortcut.register('Control+Shift+Tab', () => {
    sendRemoteShortcut('command-shift-tab');
  });
  const controlUp = globalShortcut.register('Control+Up', () => {
    sendRemoteShortcut('control-up');
  });
  const controlDown = globalShortcut.register('Control+Down', () => {
    sendRemoteShortcut('control-down');
  });
  const controlLeft = globalShortcut.register('Control+Left', () => {
    sendRemoteShortcut('control-left');
  });
  const controlRight = globalShortcut.register('Control+Right', () => {
    sendRemoteShortcut('control-right');
  });
  const clipboardCopy = globalShortcut.register('Command+Alt+C', () => {
    sendRemoteShortcut(CLIPBOARD_COPY_SHORTCUT);
  });
  const clipboardPaste = globalShortcut.register('Command+Alt+V', () => {
    sendRemoteShortcut(CLIPBOARD_PASTE_SHORTCUT);
  });

  remoteShortcutsRegistered = commandTab || commandShiftTab || controlTab || controlShiftTab ||
    controlUp || controlDown || controlLeft || controlRight || clipboardCopy || clipboardPaste;
  log('SHORTCUTS', `Command+Tab=${commandTab ? 'registered' : 'failed'} Command+Shift+Tab=${commandShiftTab ? 'registered' : 'failed'} Control+Tab=${controlTab ? 'registered' : 'failed'} Control+Shift+Tab=${controlShiftTab ? 'registered' : 'failed'} Control+Up=${controlUp ? 'registered' : 'failed'} Control+Down=${controlDown ? 'registered' : 'failed'} Control+Left=${controlLeft ? 'registered' : 'failed'} Control+Right=${controlRight ? 'registered' : 'failed'} Command+Alt+C=${clipboardCopy ? 'registered' : 'failed'} Command+Alt+V=${clipboardPaste ? 'registered' : 'failed'}`);
}

function unregisterRemoteShortcuts() {
  if (!remoteShortcutsRegistered || !globalShortcut) {
    return;
  }
  globalShortcut.unregister('Command+Tab');
  globalShortcut.unregister('Command+Shift+Tab');
  globalShortcut.unregister('Control+Tab');
  globalShortcut.unregister('Control+Shift+Tab');
  globalShortcut.unregister('Control+Up');
  globalShortcut.unregister('Control+Down');
  globalShortcut.unregister('Control+Left');
  globalShortcut.unregister('Control+Right');
  globalShortcut.unregister('Command+Alt+C');
  globalShortcut.unregister('Command+Alt+V');
  remoteShortcutsRegistered = false;
  log('SHORTCUTS', 'unregistered');
}

function updateRemoteShortcuts() {
  if (mode === 'offscreen' && activePeerId && !nativeKeyTap) {
    registerRemoteShortcuts();
  } else {
    unregisterRemoteShortcuts();
  }
}

function setConnectionsEnabled(enabled) {
  const next = !!enabled;
  if (connectionsEnabled(store) === next) {
    pushState();
    return;
  }

  store.autoConnect = next;
  writeStore();
  if (!next) {
    if (mode === 'offscreen') {
      releaseRemoteControl();
    } else if (mode === 'receiving' && inboundControlPeerId) {
      endInboundControl(inboundControlPeerId, 'connections paused');
    }
    disconnectConnections({
      peers,
      clearPeer: clearPeerCoordQueue,
    });
    Object.keys(connecting).forEach(hostId => {
      connecting[hostId] = false;
    });
    Object.keys(peers).forEach(hostId => {
      destroyPendingSocket(peers[hostId]);
    });
    Object.keys(pairingSessions).forEach(hostId => {
      const pairing = pairingSessions[hostId];
      if (pairing.state !== 'paired') {
        pairing.state = 'failed';
        pairing.code = null;
        pairing.message = 'Pairing stopped because nearby connections were paused.';
      }
    });
    log('CONNECTIONS', 'paused');
  } else {
    log('CONNECTIONS', 'enabled');
    broadcastPresence();
    checkPreferredPeerPaths();
  }
  pushState();
}

ipcMain.on('state:get', event => {
  event.sender.send('state', snapshot());
});

ipcMain.on('connections:set', (event, enabled) => {
  setConnectionsEnabled(enabled);
});

ipcMain.on('peer:place', (event, payload) => {
  if (!payload || !payload.edge || (!payload.hostId && !payload.targetId)) {
    return;
  }
  const layoutId = payload.targetId || payload.hostId;
  const sourceDisplayId = payload.sourceDisplayId ||
    defaultSourceDisplayIdForEdge(payload.edge);
  if (payload.targetId) {
    const parsed = parseTargetId(payload.targetId);
    removeLayoutsForHost(parsed.hostId, layoutId);
  }
  store.layout[layoutId] = {
    edge: payload.edge,
    crossOffset: Number.isFinite(Number(payload.crossOffset)) ? Number(payload.crossOffset) : 0,
    sourceDisplayId,
    updatedAt: Date.now(),
  };
  writeStore();
  broadcastPresence();
  pushState();
});

ipcMain.on('peer:connect', (event, hostId) => {
  const trusted = pairedPeerRecord(hostId);
  if (trusted) {
    trusted.enabled = true;
    writeStore();
  }
  connectPeer(hostId, { manual: true });
  pushState();
});

ipcMain.on('peer:disconnect', (event, hostId) => {
  const peer = peers[hostId];
  const trusted = pairedPeerRecord(hostId);
  if (trusted) {
    trusted.enabled = false;
    writeStore();
  }
  if (peer && peer.socket) {
    const notified = sendFrame(peer.socket, 'connection:pause', {});
    if (notified && peer.socket.end) {
      peer.socket.end();
    } else {
      peer.socket.destroy();
    }
  }
  destroyPendingSocket(peer);
  pushState();
});

ipcMain.on('peer:pair', (event, hostId) => {
  startPairing(hostId);
});

ipcMain.on('peer:pair:confirm', (event, hostId) => {
  confirmPairing(hostId);
});

ipcMain.on('peer:pair:reject', (event, hostId) => {
  failPairing(hostId, 'Pairing was rejected on this Mac.', true);
});

ipcMain.on('shortcut:test', (event, payload) => {
  const hostId = payload && payload.hostId;
  if (!hostId || !peers[hostId]) {
    return;
  }
  sendShortcutToPeer(hostId, 'command-tab', true);
  pushState();
});

ipcMain.on('peer:forget', (event, hostId) => {
  const peer = peers[hostId];
  if (peer) {
    clearPeerCoordQueue(peer);
  }
  if (peer && peer.socket) {
    peer.socket.destroy();
  }
  destroyPendingSocket(peer);
  delete pairingSessions[hostId];
  delete store.pairedPeers[hostId];
  forgetPeerLayout(hostId);
  if (peer) {
    peer.identityChanged = false;
    peer.socket = null;
  }
  writeStore();
  pushState();
});

ipcMain.on('nickname:set', (event, nickname) => {
  const clean = String(nickname || '').trim();
  if (!clean) {
    return;
  }
  store.nickname = clean;
  writeStore();
  broadcastPresence();
  pushState();
});

ipcMain.on('permission:accessibility:request', () => {
  const trusted = accessibilityTrusted(true);
  log('ACCESSIBILITY_REQUEST', trusted ? 'trusted' : 'not trusted yet');
  pushState();
});

ipcMain.on('releaseShortcut:set', (event, shortcut) => {
  const sequence = parseKeySequence(shortcut);
  if (!sequence.length) {
    pushState();
    return;
  }
  store.releaseSequence = sequenceEquals(sequence, LEGACY_DEFAULT_RELEASE_SEQUENCE)
    ? DEFAULT_RELEASE_SEQUENCE.slice()
    : sequence;
  writeStore();
  releaseMatcher.reset();
  suppressedKeyups = {};
  pushState();
});

app.on('ready', () => {
  store = readStore();
  pairingSecurity.ensureIdentity(store);
  writeStore();
  if (app.dock && app.dock.hide) {
    app.dock.hide();
  }
  createMenuBarItem();
  electronScreen.on('display-added', invalidateDisplayCache);
  electronScreen.on('display-removed', invalidateDisplayCache);
  electronScreen.on('display-metrics-changed', invalidateDisplayCache);
  createWindow();
  wireEventCapture();
  startTcpServer();
  startDiscovery();
  startPointerControl();
  startNativeKeySend();
  startNativeMouseSend();
  startClipboardSync();
  startLatencyProbes();
  startPreferredPathChecks();
  const trusted = accessibilityTrusted(true);
  log('ACCESSIBILITY', trusted ? 'trusted' : 'not trusted');
  setInterval(pushState, 2000);
});

app.on('window-all-closed', () => {});

app.on('will-quit', () => {
  stopNativeKeyTap();
  stopNativeKeySend();
  stopNativeMouseSend();
  unregisterRemoteShortcuts();
  if (latencyProbeTimer) {
    clearInterval(latencyProbeTimer);
  }
  if (preferredPathTimer) {
    clearInterval(preferredPathTimer);
  }
  Object.keys(peers).forEach(hostId => {
    clearPeerCoordQueue(peers[hostId]);
  });
});

app.on('activate', () => {
  showMainWindow();
});
