import {SimpleJSONLoader} from './SimpleJSONLoader'
import {ThreeViewer} from '../../viewer'
import {getEmptyMeta, SerializationMetaType, ThreeSerialization} from '../../utils/serialization'
import {IMaterial} from '../../core'

export class JSONMaterialLoader extends SimpleJSONLoader {
    // todo remove spread after node 22 widespread support
    static SupportedJSONTypes = ()=>['Material', ...[...ThreeSerialization.SerializableMaterials.values()].flatMap(t => [t.TYPE, ...t.TypeAlias || []])]
    static SupportedJSONExtensions = ()=>['mat', ...[...ThreeSerialization.SerializableMaterials.values()].map(t => t.TypeSlug)]

    viewer?: ThreeViewer

    /**
     * Find any existing material with the same uuid and update it instead of creating a new one
     * This is a global flag to toggle this behavior on or off
     * @default false
     */
    static FindExistingMaterial = false

    async parseAsync(json: Record<string, any>): Promise<any> {
        if (!this.viewer) throw 'Viewer not set in JSONMaterialLoader.'
        let mat = undefined
        if (JSONMaterialLoader.FindExistingMaterial && json.uuid && this.viewer) {
            // find any existing mat with the same uuid.
            mat = this.viewer.materialManager.findMaterial(json.uuid)
        }
        return await JSONMaterialLoader.DeserializeMaterialJSON(json, this.viewer, undefined, mat)
    }

    static async DeserializeMaterialJSON(json: any, viewer: ThreeViewer, meta?: SerializationMetaType, obj?: IMaterial|IMaterial[]) {
        meta = meta || getEmptyMeta()
        const json2 = {...json}
        if (json.images) {
            if (Array.isArray(json.images)) meta.images = Object.fromEntries(json.images.map((i: any) => [i.uuid, i]))
            else meta.images = json.images
            delete json2.images
        }
        if (json.textures) {
            if (Array.isArray(json.textures)) meta.textures = Object.fromEntries(json.textures.map((t: any) => [t.uuid, t]))
            else meta.textures = json.textures
            delete json2.textures
        }
        if (json.materials) {
            if (Array.isArray(json.materials)) meta.materials = Object.fromEntries(json.materials.map((m: any) => [m.uuid, m]))
            else meta.materials = json.materials
            delete json2.materials
        }
        const resources = await viewer.loadConfigResources(meta)
        return ThreeSerialization.Deserialize(json2, obj || undefined, resources)
    }
}
