import { supportedFormats } from '$lib/format-readers';
import { jDataView } from './jdataview';
import { Pattern } from './pattern';

/**
 * Render the embroidery pattern file to the provided canvas and update views.
 * @param {string} filename - The name of the file.
 * @param {ProgressEvent<FileReader>} evt - The file load event.
 * @param {HTMLCanvasElement} canvas - Canvas to render the pattern.
 * @param {HTMLElement} colorView - Element to display colors.
 * @param {HTMLElement} stitchesView - Element to display stitch count.
 * @param {HTMLElement} sizeView - Element to display size.
 * @param {{stitches: string, dimensions: string}} localizedStrings - Localized labels.
 */
function renderFile(
  filename,
  evt,
  canvas,
  colorView,
  stitchesView,
  sizeView,
  localizedStrings,
) {
  const fileExtension = filename.toLowerCase().split('.').pop();
  const arrayBuffer = evt.target?.result;

  if (!(fileExtension && arrayBuffer)) {
    throw new Error('Invalid file extension or file data');
  }

  const view = new jDataView(arrayBuffer, 0, evt.total || 0);
  const pattern = new Pattern();

  const formatReader = supportedFormats[fileExtension];
  if (!formatReader || typeof formatReader.read !== 'function') {
    throw new Error(`Unsupported file format: ${fileExtension}`);
  }

  // @ts-ignore
  formatReader.read(view, pattern);

  pattern.moveToPositive();
  pattern.drawShapeTo(canvas);
  pattern.drawColorsTo(colorView);
  pattern.drawStitchesCountTo(stitchesView, localizedStrings.stitches);
  pattern.drawSizeValuesTo(sizeView, localizedStrings.dimensions);
}

/**
 * Display a generic abort message.
 * @param {HTMLElement} errorMessageRef - Element to display the message.
 */
function renderAbortMessage(errorMessageRef) {
  errorMessageRef.textContent = 'Render aborted!';
}

/**
 * Display a detailed error message based on error type.
 * @param {string} errorName - The name of the error.
 * @param {HTMLElement} errorMessageRef - Element to display the message.
 */
function renderErrorMessage(errorName, errorMessageRef) {
  /** @type {string} */
  let message;

  switch (errorName) {
    case 'NotFoundError':
      message =
        'The file could not be found at the time the read was processed.';
      break;
    case 'SecurityError':
      message =
        '<p>A file security error occurred. This can be due to:</p>' +
        '<ul>' +
        '<li>Accessing certain files deemed unsafe for Web applications.</li>' +
        '<li>Performing too many read calls on file resources.</li>' +
        '<li>The file has changed on disk since the user selected it.</li>' +
        '</ul>';
      break;
    case 'NotReadableError':
      message =
        'The file cannot be read. This can occur if the file is open in another application.';
      break;
    case 'EncodingError':
      message = 'The length of the data URL for the file is too long.';
      break;
    default:
      message = 'Something went wrong!';
      break;
  }

  errorMessageRef.innerHTML = message;
}

/**
 * Read a file and render its pattern to canvas with error handling.
 * @param {File} fileObject - The file to read.
 * @param {HTMLCanvasElement} canvas - The canvas to render on.
 * @param {HTMLElement} errorMessageRef - Element to show error messages.
 * @param {HTMLElement} colorView - Element to display colors.
 * @param {HTMLElement} stitchesView - Element to display stitch count.
 * @param {HTMLElement} sizeView - Element to display size.
 * @param {{stitches: string, dimensions: string}} localizedStrings - Localized strings.
 * @returns {string} Empty string after starting file read.
 */
export default function renderFileToCanvas(
  fileObject,
  canvas,
  errorMessageRef,
  colorView,
  stitchesView,
  sizeView,
  localizedStrings,
) {
  const reader = new FileReader();

  reader.onloadend = (evt) =>
    renderFile(
      fileObject.name,
      evt,
      canvas,
      colorView,
      stitchesView,
      sizeView,
      localizedStrings,
    );

  reader.onabort = () => renderAbortMessage(errorMessageRef);
  reader.onerror = (evt) =>
    renderErrorMessage(
      // @ts-ignore
      evt.target.error?.name || 'UnknownError',
      errorMessageRef,
    );

  if (fileObject) {
    reader.readAsArrayBuffer(fileObject);
  }

  return '';
}
