// ---------------------------------------------------------------------------
// MonoDisplayRenderer - tick-based canvas renderer
// ---------------------------------------------------------------------------

import { MonoDisplayParser } from "./parser";
import { MonoDisplayRenderer } from "./renderer";

/** Optional constructor arguments for MonoDisplayDriver */
export interface MonoDisplayDriverOptions {
  /** CSS colour for on-pixels. Default "#ffffff" */
  onColor?: string;
  /** CSS colour for off-pixels / background. Default "#000000" */
  offColor?: string;
  /**
   * Scale factor - each logical pixel → NxN canvas pixels.
   * Keep at 1 and use CSS to stretch for crisp upscaling.
   */
  scale?: number;
  /** Default display width for elements without inherent size. Default 120. */
  displayWidth?: number;
  /** Default display height. Default 60. */
  displayHeight?: number;
  /**
   * Render rate in ticks per second. All animations and scrolls are driven by this.
   * Animation updateInterval, scroll speed, etc. are relative to this tick rate.
   * Default 25.
   */
  fps?: number;
  /** Loop animations. Default true. */
  loop?: boolean;
  /** Error handler. Default: throw. */
  onError?: (err: Error) => void;
  /**
   * Clock source for CurrentTime elements. Called once per render tick.
   * Default: `() => new Date()` (system time).
   * Override to freeze or mock the clock, e.g. `() => new Date('2024-01-01T12:00:00Z')`.
   */
  now?: () => Date;
}


// ---------------------------------------------------------------------------
// MonoDisplayDriver - public API surface
// ---------------------------------------------------------------------------

/**
 * Top-level driver. Attach to a canvas by ID, call load() with an async
 * factory that returns a .bin ArrayBuffer.
 *
 * @example
 * ```ts
 * const driver = new MonoDisplayDriver("canvas_root", { fps: 25 });
 * driver.load(() => loadBinFile("display.bin"));
 * ```
 */
export class MonoDisplayDriver {
  private canvas: HTMLCanvasElement;
  private opts: Required<MonoDisplayDriverOptions>;
  private parser: MonoDisplayParser;
  private renderer: MonoDisplayRenderer | null = null;

  constructor(canvasId: string, options: MonoDisplayDriverOptions = {}) {
    const el = document.getElementById(canvasId);
    if (!el || el.tagName !== "CANVAS") throw new Error(`#${canvasId} is not a <canvas>`);
    this.canvas = el as HTMLCanvasElement;
    this.opts = {
      onColor: options.onColor ?? "#EC0",
      offColor: options.offColor ?? "#000000",
      scale: options.scale ?? 1,
      displayWidth: options.displayWidth ?? 120,
      displayHeight: options.displayHeight ?? 60,
      fps: options.fps ?? 25,
      loop: options.loop ?? true,
      onError: options.onError ?? ((e: Error) => { throw e; }),
      now: options.now ?? (() => new Date()),
    };
    this.parser = new MonoDisplayParser();
  }

  async load(loader: () => Promise<ArrayBuffer>): Promise<void> {
    try {
      const buffer = await loader();
      const file = this.parser.parse(buffer);
      if (!this.renderer) this.renderer = new MonoDisplayRenderer(this.canvas, this.opts);
      this.renderer.render(file);
    } catch (err) {
      this.opts.onError(err instanceof Error ? err : new Error(String(err)));
    }
  }

  setSize(w: number, h: number) {
    this.opts.displayHeight = h;
    this.opts.displayWidth = w;
  }

  stop(): void { this.renderer?.stop(); }
}



