/* dock.js — dockable workspace.
   Three docks (left, right, bottom) around a center area. Panels can be
   resized (splitters), collapsed, hidden, and moved between docks via the
   panel header context menu. Layouts persist per mode. */
'use strict';

import { h, clear } from './dom.js';
import { showMenu } from './widgets.js';

const MIN_DOCK = 170;

export class Dock {
  /**
   * registry: { panelId: { title, icon, create(): {el, body} } }
   * layout:   { left: {size, panels: []}, right: {...}, bottom: {...}, hidden: [] }
   */
  constructor(root, { registry, layout, defaults, onChange }) {
    this.root = root;
    this.registry = registry;
    this.defaults = JSON.parse(JSON.stringify(defaults));
    this.layout = layout || JSON.parse(JSON.stringify(defaults));
    this.onChange = onChange || (() => {});
    this.instances = new Map();   // panelId -> {el, body, ...}
    this.centerEl = h('div.dock-center');
    this.zen = false;
    this._els = {};
  }

  panel(id) {
    if (!this.instances.has(id)) {
      const def = this.registry[id];
      if (!def) return null;
      const inst = def.create();
      inst.el.querySelector('.panel-header')?.addEventListener('contextmenu', e => {
        e.preventDefault();
        showMenu(this._panelMenu(id), e.clientX, e.clientY);
      });
      this.instances.set(id, inst);
    }
    return this.instances.get(id);
  }

  _panelMenu(id) {
    const loc = this._locOf(id);
    const move = to => ({
      label: `Move to ${to} dock`, icon: to === 'left' ? 'sidebarL' : to === 'right' ? 'sidebarR' : 'chevronD',
      disabled: loc === to,
      action: () => this.movePanel(id, to),
    });
    return [
      { head: this.registry[id]?.title || id },
      move('left'), move('right'), move('bottom'),
      { sep: true },
      { label: 'Hide panel', icon: 'eyeOff', action: () => this.hidePanel(id) },
      { label: 'Reset layout', icon: 'refresh', action: () => this.resetLayout() },
    ];
  }

  _locOf(id) {
    for (const d of ['left', 'right', 'bottom'])
      if ((this.layout[d]?.panels || []).includes(id)) return d;
    return null;
  }

  movePanel(id, to) {
    for (const d of ['left', 'right', 'bottom']) {
      const arr = this.layout[d].panels;
      const i = arr.indexOf(id);
      if (i >= 0) arr.splice(i, 1);
    }
    this.layout.hidden = (this.layout.hidden || []).filter(x => x !== id);
    this.layout[to].panels.push(id);
    this.render();
    this.onChange(this.layout);
  }

  hidePanel(id) {
    for (const d of ['left', 'right', 'bottom']) {
      const arr = this.layout[d].panels;
      const i = arr.indexOf(id);
      if (i >= 0) arr.splice(i, 1);
    }
    (this.layout.hidden = this.layout.hidden || []).push(id);
    this.render();
    this.onChange(this.layout);
  }

  showPanel(id, dock) {
    this.layout.hidden = (this.layout.hidden || []).filter(x => x !== id);
    if (!this._locOf(id)) this.layout[dock || 'right'].panels.push(id);
    this.render();
    this.onChange(this.layout);
  }

  togglePanel(id) {
    if (this._locOf(id)) this.hidePanel(id); else this.showPanel(id);
  }

  visiblePanels() {
    return ['left', 'right', 'bottom'].flatMap(d => this.layout[d].panels);
  }

  resetLayout() {
    this.layout = JSON.parse(JSON.stringify(this.defaults));
    this.render();
    this.onChange(this.layout);
  }

  setZen(on) {
    this.zen = on;
    this.render();
  }

  render() {
    clear(this.root);
    const row = h('div.dock-row');
    const buildDock = (name, vertical) => {
      const cfg = this.layout[name];
      const ids = (cfg?.panels || []).filter(id => this.registry[id]);
      if (!ids.length || this.zen) return null;
      const dock = h('div.dock-side', {
        class: vertical ? '' : 'dock-bottom',
        style: vertical ? { width: cfg.size + 'px', flexDirection: 'column' }
                        : { height: cfg.size + 'px', flexDirection: 'row' },
      });
      for (const id of ids) {
        const inst = this.panel(id);
        if (!inst) continue;
        inst.el.style.flex = '1 1 0';
        if (!vertical) { inst.el.style.borderTop = 'none'; inst.el.style.borderLeft = '1px solid var(--border)'; }
        dock.append(inst.el);
      }
      this._els[name] = dock;
      return dock;
    };

    const left = buildDock('left', true);
    const right = buildDock('right', true);
    const bottom = buildDock('bottom', false);

    const centerCol = h('div.dock-col', { style: { flex: '1', minWidth: '0' } });
    this.centerEl.style.flex = '1';
    centerCol.append(this.centerEl);
    if (bottom) {
      centerCol.append(this._splitter('bottom', false), bottom);
    }

    if (left) row.append(left, this._splitter('left', true));
    row.append(centerCol);
    if (right) row.append(this._splitter('right', true), right);
    this.root.append(row);
  }

  _splitter(dockName, vertical) {
    const sp = h(`div.splitter.${vertical ? 'v' : 'h'}`, { role: 'separator', 'aria-label': `Resize ${dockName} dock` });
    sp.addEventListener('pointerdown', e => {
      e.preventDefault();
      sp.classList.add('dragging');
      sp.setPointerCapture(e.pointerId);
      const cfg = this.layout[dockName];
      const start = vertical ? e.clientX : e.clientY;
      const startSize = cfg.size;
      const el = this._els[dockName];
      const onMove = ev => {
        const d = (vertical ? ev.clientX : ev.clientY) - start;
        const dir = dockName === 'left' ? 1 : -1;
        const max = vertical ? window.innerWidth * 0.5 : window.innerHeight * 0.6;
        cfg.size = Math.round(Math.min(max, Math.max(MIN_DOCK, startSize + d * dir)));
        if (el) { if (vertical) el.style.width = cfg.size + 'px'; else el.style.height = cfg.size + 'px'; }
      };
      const onUp = () => {
        sp.classList.remove('dragging');
        sp.removeEventListener('pointermove', onMove);
        sp.removeEventListener('pointerup', onUp);
        this.onChange(this.layout);
      };
      sp.addEventListener('pointermove', onMove);
      sp.addEventListener('pointerup', onUp);
    });
    return sp;
  }
}

/** Layout persistence helper. */
export function loadLayout(modeId, defaults) {
  try {
    const raw = localStorage.getItem('rc.layout.' + modeId);
    if (!raw) return null;
    const saved = JSON.parse(raw);
    // drop panels that no longer exist; keep structure sane
    for (const d of ['left', 'right', 'bottom']) {
      if (!saved[d]) saved[d] = JSON.parse(JSON.stringify(defaults[d] || { size: 300, panels: [] }));
    }
    return saved;
  } catch { return null; }
}
export function saveLayout(modeId, layout) {
  localStorage.setItem('rc.layout.' + modeId, JSON.stringify(layout));
}
