/**
 * LoaderFactory - Auto-detect and load mesh files
 *
 * Provides a unified interface for loading different mesh formats.
 * Automatically detects file type and uses the appropriate loader.
 */

import { loadSTL, canLoadSTL } from './STLLoader.js';
import { loadOBJ, canLoadOBJ } from './OBJLoader.js';
import { loadVTP, canLoadVTP } from './VTPLoader.js';
import { loadGLTF, loadGLTFSimple, canLoadGLTF, isGLTFSupported } from './GLTFLoader.js';

/**
 * Supported format types
 */
export const FormatTypes = {
    STL: 'STL',
    OBJ: 'OBJ',
    VTP: 'VTP',
    GLTF: 'GLTF',
    GLB: 'GLB'
};

/**
 * Detect file format from filepath
 * @param {string} filepath - File path or URL
 * @returns {string|null} Format type or null if unknown
 */
export function detectFormat(filepath) {
    const lower = filepath.toLowerCase();

    if (lower.includes('.stl')) return FormatTypes.STL;
    if (lower.includes('.obj')) return FormatTypes.OBJ;
    if (lower.includes('.vtp')) return FormatTypes.VTP;
    if (lower.includes('.glb')) return FormatTypes.GLB;
    if (lower.includes('.gltf')) return FormatTypes.GLTF;

    return null;
}

/**
 * Check if a format requires special handling (e.g., GLTF needs renderer)
 * @param {string} format - Format type
 * @returns {boolean}
 */
export function requiresRenderer(format) {
    return format === FormatTypes.GLTF || format === FormatTypes.GLB;
}

/**
 * Check if a format supports textures
 * @param {string} format - Format type
 * @returns {boolean}
 */
export function supportsTextures(format) {
    return format === FormatTypes.GLTF || format === FormatTypes.GLB;
}

/**
 * Check if a format supports scalar fields
 * @param {string} format - Format type
 * @returns {boolean}
 */
export function supportsFields(format) {
    return format === FormatTypes.VTP;
}

/**
 * Fetch file data as ArrayBuffer
 * @param {string} filepath - URL to fetch
 * @returns {Promise<ArrayBuffer>}
 */
async function fetchFile(filepath) {
    const response = await fetch(filepath);
    if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return response.arrayBuffer();
}

/**
 * Load a mesh file with automatic format detection
 * @param {string} filepath - URL to the mesh file
 * @param {Object} vtk - VTK.js global object
 * @param {Object} options - Loading options
 * @param {Object} options.renderer - VTK renderer (required for GLTF with textures)
 * @param {boolean} options.withTextures - Load GLTF with full texture support (default: true)
 * @param {boolean} options.twoSidedLighting - Enable two-sided lighting for GLTF (default: true)
 * @returns {Promise<Object>} Result object with polyData/actors and metadata
 */
export async function loadMesh(filepath, vtk, options = {}) {
    const {
        renderer = null,
        withTextures = true,
        twoSidedLighting = true
    } = options;

    const format = detectFormat(filepath);

    if (!format) {
        throw new Error(`Unsupported file format: ${filepath}`);
    }

    console.log(`[LoaderFactory] Loading ${format} file:`, filepath);

    // GLTF/GLB with texture support requires renderer and URL-based loading
    if ((format === FormatTypes.GLTF || format === FormatTypes.GLB) && withTextures && renderer) {
        return loadGLTF(filepath, vtk, renderer, {
            applyTextureFixes: true,
            twoSidedLighting
        });
    }

    // For all other cases, fetch the file first
    const arrayBuffer = await fetchFile(filepath);

    switch (format) {
        case FormatTypes.STL:
            return loadSTL(arrayBuffer, vtk);

        case FormatTypes.OBJ:
            return loadOBJ(arrayBuffer, vtk);

        case FormatTypes.VTP:
            return loadVTP(arrayBuffer, vtk);

        case FormatTypes.GLTF:
        case FormatTypes.GLB:
            // Simple GLTF load without texture support
            return loadGLTFSimple(arrayBuffer, vtk);

        default:
            throw new Error(`No loader available for format: ${format}`);
    }
}

/**
 * Load multiple mesh files
 * @param {string[]} filepaths - Array of URLs to mesh files
 * @param {Object} vtk - VTK.js global object
 * @param {Object} options - Loading options (same as loadMesh)
 * @returns {Promise<Object[]>} Array of result objects
 */
export async function loadMeshes(filepaths, vtk, options = {}) {
    return Promise.all(filepaths.map(fp => loadMesh(fp, vtk, options)));
}

/**
 * Create a loader configuration for a specific format
 * Useful for manual loader selection
 * @param {string} format - Format type
 * @returns {Object} Loader configuration
 */
export function getLoaderConfig(format) {
    switch (format) {
        case FormatTypes.STL:
            return {
                load: loadSTL,
                canLoad: canLoadSTL,
                requiresRenderer: false,
                supportsTextures: false,
                supportsFields: false
            };
        case FormatTypes.OBJ:
            return {
                load: loadOBJ,
                canLoad: canLoadOBJ,
                requiresRenderer: false,
                supportsTextures: false,
                supportsFields: false
            };
        case FormatTypes.VTP:
            return {
                load: loadVTP,
                canLoad: canLoadVTP,
                requiresRenderer: false,
                supportsTextures: false,
                supportsFields: true
            };
        case FormatTypes.GLTF:
        case FormatTypes.GLB:
            return {
                load: loadGLTF,
                loadSimple: loadGLTFSimple,
                canLoad: canLoadGLTF,
                isSupported: isGLTFSupported,
                requiresRenderer: true,
                supportsTextures: true,
                supportsFields: false
            };
        default:
            return null;
    }
}

export default {
    loadMesh,
    loadMeshes,
    detectFormat,
    requiresRenderer,
    supportsTextures,
    supportsFields,
    getLoaderConfig,
    FormatTypes
};
