import { Material, PBRMaterial, UnlitMaterial } from "../rendering/index.js";
export class MaterialLoader {
    type = "material";
    canLoad(request) {
        return /\.(?:mat|material)\.json(?:\?.*)?$/i.test(request.url);
    }
    async load(request, context) {
        context.throwIfAborted(request.url);
        if (typeof fetch !== "function") {
            throw new Error("MaterialLoader requires fetch");
        }
        const response = await fetch(request.url, { signal: request.signal });
        if (!response.ok) {
            throw new Error(`Material request failed with ${response.status}`);
        }
        const parsed = (await response.json());
        if (parsed.model !== "unlit" && parsed.model !== "pbr") {
            throw new Error(`Unsupported material model for ${request.url}`);
        }
        const descriptor = {
            url: request.url,
            name: parsed.name,
            model: parsed.model,
            properties: parseProperties(parsed.properties, request.url)
        };
        return {
            ...descriptor,
            createMaterial: () => createMaterialFromDescriptor(descriptor)
        };
    }
}
export function createMaterialFromDescriptor(descriptor) {
    if (descriptor.model === "unlit") {
        return new UnlitMaterial(toUnlitOptions(descriptor));
    }
    return new PBRMaterial(toPBRMaterialOptions(descriptor));
}
function toUnlitOptions(descriptor) {
    const color = readVec4(descriptor.properties, ["color", "baseColor", "baseColorFactor"]);
    return {
        name: descriptor.name,
        color: color ?? undefined,
        renderState: readRenderState(descriptor.properties)
    };
}
function toPBRMaterialOptions(descriptor) {
    const properties = descriptor.properties;
    return {
        name: descriptor.name,
        baseColor: readVec4(properties, ["baseColor", "baseColorFactor"]) ?? undefined,
        metallic: readNumber(properties, ["metallic", "metallicFactor"]),
        roughness: readNumber(properties, ["roughness", "roughnessFactor"]),
        emissiveColor: readVec3(properties, ["emissiveColor", "emissiveFactor"]) ?? undefined,
        emissiveStrength: readNumber(properties, ["emissiveStrength"]),
        clearcoatFactor: readNumber(properties, ["clearcoatFactor"]),
        clearcoatRoughnessFactor: readNumber(properties, ["clearcoatRoughnessFactor"]),
        transmissionFactor: readNumber(properties, ["transmissionFactor"]),
        diffuseTransmissionFactor: readNumber(properties, ["diffuseTransmissionFactor"]),
        diffuseTransmissionColorFactor: readVec3(properties, ["diffuseTransmissionColorFactor"]) ?? undefined,
        volumeThicknessFactor: readNumber(properties, ["volumeThicknessFactor"]),
        volumeAttenuationDistance: readNumber(properties, ["volumeAttenuationDistance"]),
        volumeAttenuationColor: readVec3(properties, ["volumeAttenuationColor"]) ?? undefined,
        ior: readNumber(properties, ["ior"]),
        specularFactor: readNumber(properties, ["specularFactor"]),
        specularColorFactor: readVec3(properties, ["specularColorFactor"]) ?? undefined,
        sheenColorFactor: readVec3(properties, ["sheenColorFactor"]) ?? undefined,
        sheenRoughnessFactor: readNumber(properties, ["sheenRoughnessFactor"]),
        anisotropyStrength: readNumber(properties, ["anisotropyStrength"]),
        anisotropyRotation: readNumber(properties, ["anisotropyRotation"]),
        iridescenceFactor: readNumber(properties, ["iridescenceFactor"]),
        iridescenceIor: readNumber(properties, ["iridescenceIor"]),
        iridescenceThicknessMinimum: readNumber(properties, ["iridescenceThicknessMinimum"]),
        iridescenceThicknessMaximum: readNumber(properties, ["iridescenceThicknessMaximum"]),
        dispersion: readNumber(properties, ["dispersion"]),
        renderState: readRenderState(properties)
    };
}
function parseProperties(value, url) {
    if (value === undefined)
        return {};
    if (!isRecord(value) || Array.isArray(value)) {
        throw new Error(`Material properties for ${url} must be an object`);
    }
    return value;
}
function readNumber(properties, keys) {
    const value = readFirst(properties, keys);
    if (value === undefined)
        return undefined;
    if (typeof value !== "number") {
        throw new Error(`Material property ${keys[0]} must be a number`);
    }
    return value;
}
function readVec3(properties, keys) {
    const value = readFirst(properties, keys);
    if (value === undefined)
        return undefined;
    if (!Array.isArray(value) || value.length !== 3) {
        throw new Error(`Material property ${keys[0]} must be a vec3`);
    }
    return [readFinite(value[0], keys[0]), readFinite(value[1], keys[0]), readFinite(value[2], keys[0])];
}
function readVec4(properties, keys) {
    const value = readFirst(properties, keys);
    if (value === undefined)
        return undefined;
    if (!Array.isArray(value) || value.length !== 4) {
        throw new Error(`Material property ${keys[0]} must be a vec4`);
    }
    return [readFinite(value[0], keys[0]), readFinite(value[1], keys[0]), readFinite(value[2], keys[0]), readFinite(value[3], keys[0])];
}
function readRenderState(properties) {
    const value = properties.renderState;
    if (value === undefined)
        return undefined;
    if (!isRecord(value) || Array.isArray(value)) {
        throw new Error("Material property renderState must be an object");
    }
    return value;
}
function readFirst(properties, keys) {
    for (const key of keys) {
        if (Object.hasOwn(properties, key))
            return properties[key];
    }
    return undefined;
}
function readFinite(value, label) {
    if (typeof value !== "number" || !Number.isFinite(value)) {
        throw new Error(`Material property ${label} must contain finite numbers`);
    }
    return value;
}
function isRecord(value) {
    return typeof value === "object" && value !== null;
}
//# sourceMappingURL=MaterialLoader.js.map