// Type definitions for ascii-engine.js (AsciiEngine SDK)
// Project: https://ascii-generator.eu/sdk.html
// MIT License — see LICENSE
//
// ascii-engine.js is a plain <script> global, not an ES/CommonJS module —
// this file just describes window.AsciiEngine's shape for editor
// autocompletion and type-checking. If you're using a bundler and want an
// import, declare it yourself, e.g.:
//   declare const AsciiEngine: typeof import("./ascii-engine");
// or reference this file via a triple-slash directive / tsconfig "types".

/** Any source `renderRamp`/`renderBraille`/`renderEdges` can read pixels from. */
type AsciiSource = CanvasImageSource;

interface RampFxOptions {
  /** Manual contrast, -100..100, applied after auto-contrast stretch. Default 0. */
  contrast?: number;
  /** Manual brightness offset, -100..100. Default 0. */
  brightness?: number;
  /** Flip dark/light. Default false. */
  invert?: boolean;
}

interface SourceSizeOptions {
  /** Override the source's intrinsic width (needed for <video>/VideoFrame). */
  srcWidth?: number;
  /** Override the source's intrinsic height (needed for <video>/VideoFrame). */
  srcHeight?: number;
  /** Width/height ratio of one monospace character. Default 0.55. */
  charAspect?: number;
}

interface RenderRampOptions extends RampFxOptions, SourceSizeOptions {
  /** Also return per-cell RGB + an HTML string with colored glyphs. Default false. */
  color?: boolean;
  /** Floyd-Steinberg dithering across the ramp's levels. Default false. */
  dither?: boolean;
}

interface RenderBrailleOptions extends RampFxOptions, SourceSizeOptions {}

interface RenderEdgesOptions extends SourceSizeOptions {
  contrast?: number;
  brightness?: number;
  /** Minimum average gradient magnitude for a cell to be drawn as an edge. Default 60. */
  edgeThreshold?: number;
}

interface RenderAudioGridOptions {
  /** Density ramp for bar/dot fill or the trace glyph (wave, uses the last/densest char). Default " .:-=+*#%@". */
  ramp?: string;
  /** Decorative rainbow hue gradient across columns (not tied to actual frequency in Hz). Default false. */
  color?: boolean;
  /** Sensitivity multiplier applied to raw 0-255 data before mapping to visual intensity, so quiet sounds still react visibly. Default 2.6. */
  gain?: number;
  /** Exponent applied after gain (values <1 compress the dynamic range, boosting quiet signals proportionally more than loud ones). Default 0.5. */
  curve?: number;
}

interface RenderValueGridOptions {
  /** Map each cell's value to a data-driven dim-amber -> warm-white heat gradient (not a decorative rainbow). Default false. */
  color?: boolean;
}

interface RenderResult {
  /** Full ASCII-art string, rows joined by "\n". */
  plain: string;
  /** One string per row. */
  rows: string[];
  /** Per-cell "rgb(r,g,b)" strings, only when `color: true` was passed to `renderRamp`; otherwise null. */
  colorGrid: string[][] | null;
  /** Pre-colored HTML (each glyph wrapped in a colored <span>), only when `color: true`; otherwise null. */
  html: string | null;
}

interface MeasureCharWidthOptions {
  /** Default '"Courier New", Courier, monospace'. */
  fontFamily?: string;
  /** Reference characters to measure the max width across. */
  refChars?: string[];
}

interface FitGridFontPxOptions extends MeasureCharWidthOptions {
  /** Skips measurement if provided. */
  charWidthRatio?: number;
  /** Default 1.05. */
  lineHeightRatio?: number;
}

interface DrawGridOptions {
  /** Default false. Requires `colorGrid` to also be set. */
  colorMode?: boolean;
  /** Per-cell "rgb(...)" strings from a `color: true` render result. */
  colorGrid?: string[][] | null;
  /** Fill color when not in color mode. Default '#ffc23d'. */
  monoColor?: string;
  /** Phosphor-style shadow blur. Default false. */
  glow?: boolean;
  /** 0-200, 100 = default blur amount. Only meaningful when `glow` is true. Default 100. */
  glowIntensity?: number;
  charWidthRatio?: number;
  lineHeightRatio?: number;
  fontFamily?: string;
}

interface DrawGridResult {
  /** The actual drawn rectangle (may be smaller than the input box). */
  x: number;
  y: number;
  width: number;
  height: number;
  /** Font size used, in px. */
  fontPx: number;
}

interface PostFxOptions {
  /** 0-100 intensity. Default 0. */
  scanlines?: number;
  /** Pixel spacing between scanlines. Default 3. */
  scanlineSpacing?: number;
  /** 0-100 intensity. Default 0. */
  vignette?: number;
  /** 0-20, horizontal pixel shift of the R/B channels. Default 0. */
  chromaticAberration?: number;
}

/** Controller returned by `openGifFrameSource`. */
interface GifFrameSource {
  /** Total number of frames in the GIF. */
  readonly frameCount: number;
  /** Index of the currently decoded frame. */
  readonly frameIndex: number;
  /** Current frame's display width in px. */
  readonly width: number;
  /** Current frame's display height in px. */
  readonly height: number;
  /** Returns the currently decoded frame — a drawable `VideoFrame`, pass it straight into `renderRamp`/`renderBraille`/`renderEdges`. */
  currentFrame(): VideoFrame | null;
  /** Display duration of the current frame in ms, if the GIF encodes it; otherwise null. */
  currentFrameDurationMs(): number | null;
  /** Decodes the next frame in the background, for live playback loops (call once per render tick). Fire-and-forget. */
  advance(): void;
  /** Decodes and awaits a specific frame by index — for export loops that need every frame in order. Don't mix with `advance()` concurrently. */
  seekFrame(index: number): Promise<VideoFrame | null>;
  /** Releases decoder resources. Always call when done. */
  close(): void;
}

interface GifEncoderOptions {
  width: number;
  height: number;
  workerScript: string;
  /** Default 2. */
  workers?: number;
  /** gif.js quality: lower = better/slower. Default 10. */
  quality?: number;
  /** Default '#000000'. */
  background?: string;
}

interface GifEncoder {
  /** Snapshots the given canvas's current pixels as the next frame. */
  addFrame(canvas: HTMLCanvasElement, delayMs?: number): void;
  /** Finalizes encoding and resolves to the animated GIF blob (`image/gif`). */
  render(): Promise<Blob>;
  /** Aborts encoding; safe to call even if `render()` was never called. */
  abort(): void;
}

interface Mp4EncoderOptions {
  /** Redraw this same canvas element before each `addFrame` call — it's captured live, not passed per-frame. */
  canvas: HTMLCanvasElement;
  /** Expected fps; timestamps are snapped to it. Default 10. */
  frameRate?: number;
  /** H.264 bitrate in bits/second. Default 1_000_000. */
  bitrate?: number;
}

interface Mp4Encoder {
  /** Reads `canvas`'s current pixels and advances an internal running timestamp by `delayMs` (default 100). */
  addFrame(delayMs?: number): Promise<void>;
  /** Stops accepting frames and resolves to the final `video/mp4` blob. */
  finalize(): Promise<Blob>;
  /** Cancels encoding and releases resources. */
  cancel(): Promise<void>;
}

interface AsciiEngineStatic {
  /** Semantic version of this engine build, e.g. "0.4.1". */
  readonly version: string;
  /** Default character width/height ratio (~0.55) used when `charAspect` isn't specified. */
  readonly DEFAULT_CHAR_ASPECT: number;
  /** True when the WebCodecs `ImageDecoder` API is available (Chrome/Edge), required by `openGifFrameSource`. */
  readonly isGifDecoderSupported: boolean;
  /** True when the WebCodecs `VideoEncoder` API is available (Chrome/Edge, Safari 16.4+), required by `createMp4Encoder`. */
  readonly isMp4EncoderSupported: boolean;

  /** Finds the [min, max] luminance bounds in a sample, for `stretch()`. */
  autoContrastRange(values: ArrayLike<number>): [number, number];
  /** Remaps a value from [lo, hi] to [0, 255], clamped. */
  stretch(v: number, lo: number, hi: number): number;

  /**
   * Converts an image/video/frame into an ASCII-art grid using a character
   * density ramp (darkest → brightest).
   * @param ramp Characters ordered from darkest/sparsest (index 0) to brightest/densest (last). Any length ≥ 1.
   */
  renderRamp(source: AsciiSource, cols: number, ramp: string, opts?: RenderRampOptions): RenderResult;

  /**
   * Like `renderRamp`, but renders via Unicode Braille characters (2×4 dot
   * sub-grid per glyph, ~4x effective resolution). Monochrome only.
   */
  renderBraille(source: AsciiSource, cols: number, opts?: RenderBrailleOptions): RenderResult;

  /**
   * Renders clean "line art" via Sobel edge detection: a directional
   * character (`|` `-` `/` `\`) where an edge is detected, a space elsewhere.
   */
  renderEdges(source: AsciiSource, cols: number, opts?: RenderEdgesOptions): RenderResult;

  /**
   * Renders live Web Audio `AnalyserNode` data as an ASCII grid: a classic
   * oscilloscope trace ("wave"), a spectrum analyzer ("bars"), or a
   * center-mirrored spectrum ("mirror"). Pure data-in/grid-out — this
   * function never touches microphones, `AudioContext`, or any audio API;
   * the caller owns that lifecycle entirely (permission, start/stop, cleanup).
   * @param data For "wave": time-domain samples (0-255, 128=silence), i.e.
   *   `analyser.getByteTimeDomainData()`. For "bars"/"mirror": frequency-domain
   *   magnitudes (0-255), i.e. `analyser.getByteFrequencyData()`.
   */
  renderAudioGrid(data: Uint8Array, cols: number, rows: number, mode?: "wave" | "wavefill" | "bars" | "mirror" | "circle" | "dots", opts?: RenderAudioGridOptions): RenderResult;

  /**
   * Renders a plain 2D grid of already-computed values (0-1) as an ASCII
   * grid — for generative-art sources (cellular automata, reaction-diffusion,
   * any simulation that's already a grid) with no pixels to sample, unlike
   * `renderRamp`/`renderBraille`/`renderEdges`. Pure data-in/grid-out — this
   * function never touches the simulation itself, the caller owns that.
   * @param grid `grid[row][col]`, each value clamped to 0-1.
   * @param ramp Density ramp, darkest/sparsest first (index 0) to brightest/densest (last).
   */
  renderValueGrid(grid: number[][], ramp: string, opts?: RenderValueGridOptions): RenderResult;

  /** Measures the widest glyph's rendered width/height ratio among reference characters. Cached. */
  measureMaxCharWidthRatio(opts?: MeasureCharWidthOptions): number;

  /** Largest font size (px) that fits a cols×rowCount grid inside a box without overflowing. */
  fitGridFontPx(cols: number, rowCount: number, boxWidthPx: number, boxHeightPx: number, opts?: FitGridFontPxOptions): number;

  /** Like `fitGridFontPx`, but for lines of text with variable character widths (e.g. FIGlet banners). */
  fitFontPxForLines(
    ctx: CanvasRenderingContext2D,
    lines: string[],
    availW: number,
    availH: number,
    lineHeightRatio?: number,
    fontFamily?: string
  ): number;

  /** Draws an ASCII grid (from a render* function's `.rows`) onto a canvas, centered and sized to fit a box. */
  drawGrid(
    ctx: CanvasRenderingContext2D,
    rows: string[],
    x: number,
    y: number,
    w: number,
    h: number,
    opts?: DrawGridOptions
  ): DrawGridResult | undefined;

  /** CRT-style post-processing (chromatic aberration, then scanlines, then vignette) on an already-drawn canvas region. */
  applyPostFx(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, opts?: PostFxOptions): void;

  /**
   * Opens an animated GIF for frame-by-frame decoding via WebCodecs.
   * Returns null if `isGifDecoderSupported` is false.
   */
  openGifFrameSource(file: File | Blob): Promise<GifFrameSource | null>;

  /**
   * Wraps the third-party gif.js library (`window.GIF`, load it yourself)
   * for encoding canvas frames into an animated GIF. Returns null if
   * `window.GIF` isn't loaded.
   */
  createGifEncoder(opts: GifEncoderOptions): GifEncoder | null;

  /**
   * Wraps the third-party Mediabunny library (`window.Mediabunny`, an ES
   * module — `import()` it yourself and assign the namespace to
   * `window.Mediabunny`) for encoding canvas frames into a real MP4 (H.264)
   * video. Returns null if that hasn't been done, or `isMp4EncoderSupported`
   * is false.
   */
  createMp4Encoder(opts: Mp4EncoderOptions): Mp4Encoder | null;
}

declare global {
  interface Window {
    AsciiEngine: AsciiEngineStatic;
  }
  const AsciiEngine: AsciiEngineStatic;
}

export {};
