external/gridded-data/zarr/zarr.js

import * as divisors from "../../../modules/visualize/divisors.js";

/**
 * Zarrita-based Zarr data format loader
 * Provides modern Zarr functionality using zarrita library
 * @external ZarritaLoader
 */

let zarrLibraries = null;
let isZarrLoaded = false;

/**
 * Loads zarrita Zarr library
 * @method load
 * @memberof ZarritaLoader
 * @param {Object} [options] - Loading options
 * @returns {Promise<Object>} Promise resolving to loaded zarrita library
 */
async function load(options = {}) {
  if (isZarrLoaded) {
    return zarrLibraries;
  }

  try {
    // Load zarrita from CDN
    const zarr = await import('https://cdn.jsdelivr.net/npm/zarrita/+esm');

    // Load numcodecs for decompression
    const numcodecs = await import('https://cdn.jsdelivr.net/npm/numcodecs/+esm');

    // Load fzstd for Zstd decompression (common in Zarr v3 or modern datasets)
    const fzstd = await import('https://cdn.jsdelivr.net/npm/fzstd/+esm');

    // Load pako for Zlib support (useful for some Zarr stores)
    let pako = (typeof window !== 'undefined' && window.pako) || globalThis.pako;
    if (!pako) {
      try {
        const pakoModule = await import('https://cdn.skypack.dev/pako');
        pako = pakoModule.default || pakoModule;
      } catch (e) {
        console.warn('Failed to load pako for Zarr:', e);
      }
    }

    zarrLibraries = {
      zarr: zarr,
      numcodecs: numcodecs,
      fzstd: fzstd,
      pako: pako,
      version: 'zarrita'
    };

    // Expose globally for compatibility with some utils
    const globalObj = typeof window !== 'undefined' ? window : globalThis;
    if (!globalObj.numcodecs) globalObj.numcodecs = numcodecs;
    if (!globalObj.fzstd) globalObj.fzstd = fzstd;
    if (!globalObj.pako && pako) globalObj.pako = pako;

    isZarrLoaded = true;

    console.log('Zarrita Zarr library and dependencies loaded successfully');

    return zarrLibraries;

  } catch (error) {
    console.error('Failed to load zarrita or dependencies:', error);
    throw new Error('Unable to load zarrita library');
  }
}

/**
 * Utility function for creating Zarr stores and arrays using zarrita
 * @param {string} url - Base URL for the Zarr store
 * @returns {Object} Zarrita utilities
 */
export function createZarrStore(url) {
  const libraries = zarrLibraries || { zarr: null };

  return {
    /**
     * Create a fetch store for the given URL
     */
    createStore: (storeUrl = url) => {
      if (!libraries.zarr) {
        throw new Error('Zarrita library not loaded');
      }
      return new libraries.zarr.FetchStore(storeUrl);
    },

    /**
     * Open a Zarr array from the store
     */
    openArray: async (storeUrl = url, options = {}) => {
      if (!libraries.zarr) {
        throw new Error('Zarrita library not loaded');
      }
      const store = new libraries.zarr.FetchStore(storeUrl);
      return await libraries.zarr.open(store, { kind: 'array', ...options });
    },

    /**
     * Get full array data
     */
    getArrayData: async (array) => {
      if (!libraries.zarr) {
        throw new Error('Zarrita library not loaded');
      }
      return await libraries.zarr.get(array);
    },

    /**
     * Get array chunk
     */
    getChunk: async (array, indices) => {
      return await array.getChunk(indices);
    },

    /**
     * Get array region
     */
    getRegion: async (array, region) => {
      if (!libraries.zarr) {
        throw new Error('Zarrita library not loaded');
      }
      return await libraries.zarr.get(array, region);
    }
  };
}

/**
 * Checks if Zarr libraries are loaded
 * @method isLoaded
 * @memberof ZarritaLoader
 * @returns {boolean} True if libraries are loaded
 */
function isLoaded() {
  return isZarrLoaded && zarrLibraries !== null;
}

/**
 * Gets information about the Zarr loader
 * @method getInfo
 * @memberof ZarritaLoader
 * @returns {Object} Information about supported features
 */
function getInfo() {
  return {
    name: 'Zarr',
    description: 'Zarr storage format (v2/v3) using Zarrita',
    version: '2.0+',
    features: [
      'Chunked arrays',
      'Compression (Blosc, Zlib, Gzip)',
      'Cloud storage support',
      'Metadata storage',
      'Multi-dimensional arrays'
    ],
    dependencies: [
      'zarrita (Zarr implementation)',
      'numcodecs (Compression codecs)',
      'pako (Zlib/Gzip support)'
    ]
  };
}

export {
  load,
  isLoaded,
  getInfo
};

export default {
  load,
  isLoaded,
  getInfo,
  createZarrStore
};