// =============================================================================
// Mono Display File Format - type definitions
// Spec: Mono Display File Format Specification
// =============================================================================

// ---------------------------------------------------------------------------
// Enums
// ---------------------------------------------------------------------------

export enum FileVersion {
  /** Illegal - must not appear in a valid file */
  Illegal = 0,
  /** Current specification version */
  V1 = 1,
}

export enum SectionType {
  /** List of drawn elements - always shown */
  ElementsAlways = 1,
  /** List of drawn elements - shown only within a POSIX timestamp window */
  ElementsTimespan = 2,
  /** U8G2-format custom font used by draw elements in this file */
  CustomFont = 32,
}
export type SectionTypeName = keyof typeof SectionType;

export const SectionTypeToString = Object.fromEntries(
  (Object.keys(SectionType) as SectionTypeName[])
    .filter(k => isNaN(Number(k)))
    .map(k => [SectionType[k], k])
) as Record<SectionType, SectionTypeName>;

export const StringToSectionType = Object.fromEntries(
  (Object.keys(SectionType) as SectionTypeName[])
    .filter(k => isNaN(Number(k)))
    .map(k => [k, SectionType[k]])
) as Record<SectionTypeName, SectionType>;

export enum ElementType {
  Image2D = 1,
  Animation = 2,
  HorizontalScroll = 3,
  VerticalScroll = 4,
  Line = 5,
  ClippedText = 16,
  HScrollText = 17,
  VScrollText = 18,
  CurrentTime = 32,
}

export type ElementTypeName = keyof typeof ElementType;

export const ElementTypeToString = Object.fromEntries(
  (Object.keys(ElementType) as ElementTypeName[])
    .filter(k => isNaN(Number(k)))
    .map(k => [ElementType[k], k])
) as Record<ElementType, ElementTypeName>;

export const StringToElementType = Object.fromEntries(
  (Object.keys(ElementType) as ElementTypeName[])
    .filter(k => isNaN(Number(k)))
    .map(k => [k, ElementType[k]])
) as Record<ElementTypeName, ElementType>;
// ---------------------------------------------------------------------------
// Section flags (section types 1 and 2)
// ---------------------------------------------------------------------------

/** Bit-field flags for ElementsAlways / ElementsTimespan sections. */
export interface MonoFormatSectionFlags {
  /** Bit 0 - render elements onto the front-side buffer. */
  drawFront?: boolean;
  /** Bit 1 - render elements onto the back-side buffer. */
  drawBack?: boolean;
  /** Bit 2 - clear targeted buffer(s) before drawing this section's elements. */
  clearBuffer?: boolean;
  // Bits 3-15: reserved; writers MUST write 0.
}

// ---------------------------------------------------------------------------
// Scroll element flags (HorizontalScroll / VerticalScroll / HScrollText)
// ---------------------------------------------------------------------------

export interface MonoFormatScrollElementFlags {
  /** Bit 0 - wrap content endlessly. */
  endless?: boolean;
  /** Bit 1 - reverse scroll direction. */
  invertDirection?: boolean;
  /** Bit 2 - pad at start edge (left for H-scroll, top for V-scroll). */
  padStart?: boolean;
  /** Bit 3 - pad at end edge (right for H-scroll, bottom for V-scroll). */
  padEnd?: boolean;
  // Bits 4-7: reserved.
}

// ---------------------------------------------------------------------------
// CurrentTime element flags
// ---------------------------------------------------------------------------

export interface MonoFormatCurrentTimeFlags {
  /** Bit 0 - use 12-hour clock (default 24-hour). */
  clock12h?: boolean;
  /** Bit 1 - show hours. */
  showHours?: boolean;
  /** Bit 2 - show minutes. */
  showMinutes?: boolean;
  /** Bit 3 - show seconds. */
  showSeconds?: boolean;
  // Bits 4-15: reserved.
}

// ---------------------------------------------------------------------------
// Line element flags
// ---------------------------------------------------------------------------

export interface MonoFormatLineFlags {
  /** Bit 0 - invert pixel values along the line. */
  invertPixels?: boolean;
  // Bits 1-7: reserved.
}

// ---------------------------------------------------------------------------
// Font index helpers
// ---------------------------------------------------------------------------

/**
 * Font index encoding:
 *   0 .. 0x7FFF  built-in font (device-specific; may not exist)
 *   0x8000+      custom font embedded in this file
 *                  0x8000 = first CustomFont section
 *                  0x8001 = second, etc.
 */
export type FontIndex = number;

export const CUSTOM_FONT_BASE = 0x8000;

/** Return true when fontIndex refers to a custom (in-file) font. */
export function isCustomFont(fontIndex: FontIndex): boolean {
  return fontIndex >= CUSTOM_FONT_BASE;
}

/** Extract zero-based position of a custom font within the file. */
export function customFontOrdinal(fontIndex: FontIndex): number {
  return fontIndex - CUSTOM_FONT_BASE;
}

// ---------------------------------------------------------------------------
// Section descriptors  (MonoDisplayFile / builder input)
// ---------------------------------------------------------------------------

export interface CustomFontDescriptor {
  /** Raw U8G2 font data. */
  fontData: Uint8Array;
}


// ---------------------------------------------------------------------------
// MonoFormat types  (MonoDisplayParser output → renderer input)
// Pixels are always unpacked (1 byte/pixel) after parsing.
// ---------------------------------------------------------------------------

export interface MonoFormatPixelImage {
  /** Unpacked: 1 byte per pixel, 0 = off, 1 = on, row-major. */
  pixels: Uint8Array;
  width: number;
  height: number;
}

export interface MonoFormatImage2D {
  type: ElementType.Image2D;
  xOffset: number;
  yOffset: number;
  image: MonoFormatPixelImage;
}

export interface MonoFormatAnimation {
  type: ElementType.Animation;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  updateInterval: number;
  frames: MonoFormatPixelImage[];
}


export interface MonoFormatHScroll {
  type: ElementType.HorizontalScroll;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  contentWidth: number;
  scrollSpeed: number;
  flags: MonoFormatScrollElementFlags;
  content: MonoFormatPixelImage;
}

export interface MonoFormatVScroll {
  type: ElementType.VerticalScroll;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  contentHeight: number;
  scrollSpeed: number;
  flags: MonoFormatScrollElementFlags;
  content: MonoFormatPixelImage;
}

export interface MonoFormatLine {
  type: ElementType.Line;
  xOrigin: number;
  yOrigin: number;
  xTarget: number;
  yTarget: number;
  lineStyle: number;
  invertPixels: boolean;
}

export interface MonoFormatClippedText {
  type: ElementType.ClippedText;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  fontIndex: FontIndex;
  text: string;
}

export interface MonoFormatHScrollText {
  type: ElementType.HScrollText;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  scrollSpeed: number;
  fontIndex: FontIndex;
  flags: MonoFormatScrollElementFlags;
  text: string;
}

export interface MonoFormatCurrentTime {
  type: ElementType.CurrentTime;
  xOffset: number;
  yOffset: number;
  width: number;
  height: number;
  fontIndex: FontIndex;
  /** UTC offset in minutes (signed). */
  utcOffsetMinutes: number;
  flags: MonoFormatCurrentTimeFlags;
}

export type MonoFormatElement =
  | MonoFormatImage2D
  | MonoFormatAnimation
  | MonoFormatHScroll
  | MonoFormatVScroll
  | MonoFormatLine
  | MonoFormatClippedText
  | MonoFormatHScrollText
  | MonoFormatCurrentTime;



export interface MonoFormatElementsAlways {
  sectionType: SectionType.ElementsAlways;
  flags: MonoFormatSectionFlags;
  elements: MonoFormatElement[];
}

export interface MonoFormatElementsTimespan {
  sectionType: SectionType.ElementsTimespan;
  flags: MonoFormatSectionFlags;
  startTimestamp: bigint; // POSIX seconds
  endTimestamp: bigint;
  elements: MonoFormatElement[];
}

export interface MonoFormatCustomFont {
  sectionType: SectionType.CustomFont;
  /** Raw U8G2 font data (actual bytes, not padded). */
  fontData: Uint8Array;
}

export type MonoFormatSection =
  | MonoFormatElementsAlways
  | MonoFormatElementsTimespan
  | MonoFormatCustomFont;

/** Top-level MonoDisplayParser output. */
export interface MonoFormatFile {
  sections: MonoFormatSection[];
}
