/**
 * GIF Encoder Module for Offscreen Document
 *
 * Handles GIF encoding using the gifenc library in the offscreen document context.
 * This module provides frame-by-frame GIF encoding with palette quantization.
 */

import { GIFEncoder, quantize, applyPalette } from 'gifenc';
import { MessageTarget, OFFSCREEN_MESSAGE_TYPES } from '@/common/message-types';

// ============================================================================
// Types
// ============================================================================

interface GifEncoderState {
  encoder: ReturnType<typeof GIFEncoder> | null;
  width: number;
  height: number;
  frameCount: number;
  isInitialized: boolean;
}

interface GifAddFrameMessage {
  target: MessageTarget;
  type: typeof OFFSCREEN_MESSAGE_TYPES.GIF_ADD_FRAME;
  imageData: number[];
  width: number;
  height: number;
  delay: number;
  maxColors?: number;
}

interface GifFinishMessage {
  target: MessageTarget;
  type: typeof OFFSCREEN_MESSAGE_TYPES.GIF_FINISH;
}

interface GifResetMessage {
  target: MessageTarget;
  type: typeof OFFSCREEN_MESSAGE_TYPES.GIF_RESET;
}

type GifMessage = GifAddFrameMessage | GifFinishMessage | GifResetMessage;

interface GifMessageResponse {
  success: boolean;
  error?: string;
  frameCount?: number;
  gifData?: number[];
  byteLength?: number;
}

// ============================================================================
// State
// ============================================================================

const state: GifEncoderState = {
  encoder: null,
  width: 0,
  height: 0,
  frameCount: 0,
  isInitialized: false,
};

// ============================================================================
// Handlers
// ============================================================================

function initializeEncoder(width: number, height: number): void {
  state.encoder = GIFEncoder();
  state.width = width;
  state.height = height;
  state.frameCount = 0;
  state.isInitialized = true;
}

function addFrame(
  imageData: Uint8ClampedArray,
  width: number,
  height: number,
  delay: number,
  maxColors: number = 256,
): void {
  // Initialize encoder on first frame
  if (!state.isInitialized || state.width !== width || state.height !== height) {
    initializeEncoder(width, height);
  }

  if (!state.encoder) {
    throw new Error('GIF encoder not initialized');
  }

  // Quantize colors to create palette
  const palette = quantize(imageData, maxColors, { format: 'rgb444' });

  // Map pixels to palette indices
  const indexedPixels = applyPalette(imageData, palette, 'rgb444');

  // Write frame to encoder
  state.encoder.writeFrame(indexedPixels, width, height, {
    palette,
    delay,
    dispose: 2, // Restore to background color
  });

  state.frameCount++;
}

function finishEncoding(): Uint8Array {
  if (!state.encoder) {
    throw new Error('GIF encoder not initialized');
  }

  state.encoder.finish();
  const bytes = state.encoder.bytes();

  // Reset state after finishing
  resetEncoder();

  return bytes;
}

function resetEncoder(): void {
  if (state.encoder) {
    state.encoder.reset();
  }
  state.encoder = null;
  state.width = 0;
  state.height = 0;
  state.frameCount = 0;
  state.isInitialized = false;
}

// ============================================================================
// Message Handler
// ============================================================================

function isGifMessage(message: unknown): message is GifMessage {
  if (!message || typeof message !== 'object') return false;
  const msg = message as Record<string, unknown>;
  if (msg.target !== MessageTarget.Offscreen) return false;

  const gifTypes: string[] = [
    OFFSCREEN_MESSAGE_TYPES.GIF_ADD_FRAME,
    OFFSCREEN_MESSAGE_TYPES.GIF_FINISH,
    OFFSCREEN_MESSAGE_TYPES.GIF_RESET,
  ];

  return gifTypes.includes(msg.type as string);
}

export function handleGifMessage(
  message: unknown,
  sendResponse: (response: GifMessageResponse) => void,
): boolean {
  if (!isGifMessage(message)) {
    return false;
  }

  try {
    switch (message.type) {
      case OFFSCREEN_MESSAGE_TYPES.GIF_ADD_FRAME: {
        const { imageData, width, height, delay, maxColors } = message;
        const clampedData = new Uint8ClampedArray(imageData);
        addFrame(clampedData, width, height, delay, maxColors);
        sendResponse({
          success: true,
          frameCount: state.frameCount,
        });
        break;
      }

      case OFFSCREEN_MESSAGE_TYPES.GIF_FINISH: {
        const gifBytes = finishEncoding();
        sendResponse({
          success: true,
          gifData: Array.from(gifBytes),
          byteLength: gifBytes.byteLength,
        });
        break;
      }

      case OFFSCREEN_MESSAGE_TYPES.GIF_RESET: {
        resetEncoder();
        sendResponse({ success: true });
        break;
      }

      default:
        sendResponse({ success: false, error: `Unknown GIF message type` });
    }
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error('GIF encoder error:', errorMessage);
    sendResponse({ success: false, error: errorMessage });
  }

  return true;
}

console.log('GIF encoder module loaded');
