/**
 * Generated by Verge3D Puzzles v.4.2.0
 * Tue, 21 Mar 2023 15:49:35 GMT
 * Prefer not editing this file as your changes may get overridden once Puzzles are saved.
 * Check out https://www.soft8soft.com/docs/manual/en/introduction/Using-JavaScript.html
 * for the information on how to add your own JavaScript to Verge3D apps.
 */
function createPL() {

// global variables/constants used by puzzles' functions

var LIST_NONE = '<none>';

var _pGlob = {};

_pGlob.objCache = {};
_pGlob.fadeAnnotations = true;
_pGlob.pickedObject = '';
_pGlob.hoveredObject = '';
_pGlob.mediaElements = {};
_pGlob.loadedFile = '';
_pGlob.states = [];
_pGlob.percentage = 0;
_pGlob.openedFile = '';
_pGlob.openedFileMeta = {};
_pGlob.xrSessionAcquired = false;
_pGlob.xrSessionCallbacks = [];
_pGlob.screenCoords = new v3d.Vector2();
_pGlob.intervalTimers = {};
_pGlob.customEvents = new v3d.EventDispatcher();
_pGlob.eventListeners = [];

_pGlob.AXIS_X = new v3d.Vector3(1, 0, 0);
_pGlob.AXIS_Y = new v3d.Vector3(0, 1, 0);
_pGlob.AXIS_Z = new v3d.Vector3(0, 0, 1);
_pGlob.MIN_DRAG_SCALE = 10e-4;
_pGlob.SET_OBJ_ROT_EPS = 1e-8;

_pGlob.vec2Tmp = new v3d.Vector2();
_pGlob.vec2Tmp2 = new v3d.Vector2();
_pGlob.vec3Tmp = new v3d.Vector3();
_pGlob.vec3Tmp2 = new v3d.Vector3();
_pGlob.vec3Tmp3 = new v3d.Vector3();
_pGlob.vec3Tmp4 = new v3d.Vector3();
_pGlob.eulerTmp = new v3d.Euler();
_pGlob.eulerTmp2 = new v3d.Euler();
_pGlob.quatTmp = new v3d.Quaternion();
_pGlob.quatTmp2 = new v3d.Quaternion();
_pGlob.colorTmp = new v3d.Color();
_pGlob.mat4Tmp = new v3d.Matrix4();
_pGlob.planeTmp = new v3d.Plane();
_pGlob.raycasterTmp = new v3d.Raycaster();

var PL = {};
// backward compatibility
v3d.PL = v3d.puzzles = PL;

PL.procedures = PL.procedures || {};




PL.execInitPuzzles = function(options) {
    // always null, should not be available in "init" puzzles
    var appInstance = null;
    // app is more conventional than appInstance (used in exec script and app templates)
    var app = null;

    var _initGlob = {};
    _initGlob.percentage = 0;
    _initGlob.output = {
        initOptions: {
            fadeAnnotations: true,
            useBkgTransp: false,
            preserveDrawBuf: false,
            useCompAssets: false,
            useFullscreen: true,
            useCustomPreloader: false,
            preloaderStartCb: function() {},
            preloaderProgressCb: function() {},
            preloaderEndCb: function() {},
        }
    }

    // provide the container's id to puzzles that need access to the container
    _initGlob.container = options !== undefined && 'container' in options
            ? options.container : "";

    

    
    return _initGlob.output;
}

PL.init = function(appInstance, initOptions) {

// app is more conventional than appInstance (used in exec script and app templates)
var app = appInstance;

initOptions = initOptions || {};

if ('fadeAnnotations' in initOptions) {
    _pGlob.fadeAnnotations = initOptions.fadeAnnotations;
}



// utility functions envoked by the HTML puzzles
function getElements(ids, isParent) {
    var elems = [];
    if (Array.isArray(ids) && ids[0] != 'CONTAINER' && ids[0] != 'WINDOW' &&
        ids[0] != 'DOCUMENT' && ids[0] != 'BODY' && ids[0] != 'QUERYSELECTOR') {
        for (var i = 0; i < ids.length; i++)
            elems.push(getElement(ids[i], isParent));
    } else {
        elems.push(getElement(ids, isParent));
    }
    return elems;
}

function getElement(id, isParent) {
    var elem;
    if (Array.isArray(id) && id[0] == 'CONTAINER') {
        if (appInstance !== null) {
            elem = appInstance.container;
        } else if (typeof _initGlob !== 'undefined') {
            // if we are on the initialization stage, we still can have access
            // to the container element
            var id = _initGlob.container;
            if (isParent) {
                elem = parent.document.getElementById(id);
            } else {
                elem = document.getElementById(id);
            }
        }
    } else if (Array.isArray(id) && id[0] == 'WINDOW') {
        if (isParent)
            elem = parent;
        else
            elem = window;
    } else if (Array.isArray(id) && id[0] == 'DOCUMENT') {
        if (isParent)
            elem = parent.document;
        else
            elem = document;
    } else if (Array.isArray(id) && id[0] == 'BODY') {
        if (isParent)
            elem = parent.document.body;
        else
            elem = document.body;
    } else if (Array.isArray(id) && id[0] == 'QUERYSELECTOR') {
        if (isParent)
            elem = parent.document.querySelector(id);
        else
            elem = document.querySelector(id);
    } else {
        if (isParent)
            elem = parent.document.getElementById(id);
        else
            elem = document.getElementById(id);
    }
    return elem;
}

// getHTMLElemAttribute puzzle
function getHTMLElemAttribute(attr, id, isParent) {
    var elem = getElement(id, isParent);
    return elem ? elem[attr]: '';
}

/**
 * Retreive standard accessible textures for MeshNodeMaterial or MeshStandardMaterial.
 * If "collectSameNameMats" is true then all materials in the scene with the given name will
 * be used for collecting textures, otherwise will be used only the first found material (default behavior).
 */
function matGetEditableTextures(matName, collectSameNameMats) {

    var mats = [];
    if (collectSameNameMats) {
        mats = v3d.SceneUtils.getMaterialsByName(appInstance, matName);
    } else {
        var firstMat = v3d.SceneUtils.getMaterialByName(appInstance, matName);
        if (firstMat !== null) {
            mats = [firstMat];
        }
    }

    var textures = mats.reduce(function(texArray, mat) {
        var matTextures = [];
        switch (mat.type) {
            case 'MeshNodeMaterial':
                matTextures = Object.values(mat.nodeTextures);
                break;

            case 'MeshStandardMaterial':
                matTextures = [
                    mat.map, mat.lightMap, mat.aoMap, mat.emissiveMap,
                    mat.bumpMap, mat.normalMap, mat.displacementMap,
                    mat.roughnessMap, mat.metalnessMap, mat.alphaMap, mat.envMap
                ]
                break;

            default:
                console.error('matGetEditableTextures: Unknown material type ' + mat.type);
                break;
        }

        Array.prototype.push.apply(texArray, matTextures);
        return texArray;
    }, []);

    return textures.filter(function(elem) {
        // check Texture type exactly
        return elem && (elem.constructor == v3d.Texture
                || elem.constructor == v3d.CompressedTexture
                || elem.constructor == v3d.DataTexture
                || elem.constructor == v3d.CanvasTexture
                || elem.constructor == v3d.VideoTexture);
    });
}

/**
 * Replace accessible textures for MeshNodeMaterial or MeshStandardMaterial
 */
function matReplaceEditableTexture(mat, oldTex, newTex) {

    switch (mat.type) {
        case 'MeshNodeMaterial':
            for (var name in mat.nodeTextures) {
                if (mat.nodeTextures[name] == oldTex) {
                    mat.nodeTextures[name] = newTex;
                }
            }

            break;

        case 'MeshStandardMaterial':

            var texNames = ['map', 'lightMap', 'aoMap', 'emissiveMap',
                            'bumpMap', 'normalMap', 'displacementMap', 'roughnessMap',
                            'metalnessMap', 'alphaMap', 'envMap'];

            texNames.forEach(function(name) {
                if (mat[name] == oldTex) {
                    mat[name] = newTex;
                }
            });

            break;

        default:
            console.error('matReplaceEditableTexture: Unsupported material type ' + mat.type);
            break;
    }

    // inherit some save params
    newTex.encoding = oldTex.encoding;
    newTex.wrapS = oldTex.wrapS;
    newTex.wrapT = oldTex.wrapT;

}

function _checkListenersSame(target0, type0, listener0, optionsOrUseCapture0,
        target1, type1, listener1, optionsOrUseCapture1) {
    const capture0 = Boolean(optionsOrUseCapture0 instanceof Object
            ? optionsOrUseCapture0.capture : optionsOrUseCapture0);
    const capture1 = Boolean(optionsOrUseCapture1 instanceof Object
            ? optionsOrUseCapture1.capture : optionsOrUseCapture1);
    return target0 === target1 && type0 === type1 && listener0 === listener1
            && capture0 === capture1;
}

/**
 * Add the specified event listener to the specified target. This function also
 * stores listener data for easier disposing.
 */
function bindListener(target, type, listener, optionsOrUseCapture) {
    const alreadyExists = _pGlob.eventListeners.some(elem => {
        return _checkListenersSame(elem.target, elem.type, elem.listener,
                elem.optionsOrUseCapture, target, type, listener,
                optionsOrUseCapture);
    });

    if (!alreadyExists) {
        target.addEventListener(type, listener, optionsOrUseCapture);
        _pGlob.eventListeners.push({ target, type, listener, optionsOrUseCapture });
    }
}

// replaceTexture puzzle
function replaceTexture(matName, texName, texUrlOrElem, doCb) {

    const textures = matGetEditableTextures(matName, true).filter(function(elem) {
        return elem.name == texName;
    });

    if (!textures.length)
        return;

    const mats = v3d.SceneUtils.getMaterialsByName(appInstance, matName);

    if (texUrlOrElem instanceof Promise) {

        texUrlOrElem.then(function(response) {
           processImageUrl(response);
        }, function(error) {});

    } else if (typeof texUrlOrElem == 'string') {

        processImageUrl(texUrlOrElem);

    /**
     * NOTE: not checking for the MediaHTML5 constructor, because otherwise this
     * puzzle would always provide the code that's not needed most of the time
     */
    } else if (texUrlOrElem instanceof Object && texUrlOrElem.source
            instanceof HTMLVideoElement) {

        processVideo(texUrlOrElem.source);

    } else if (texUrlOrElem instanceof HTMLCanvasElement) {

        processCanvas(texUrlOrElem);

    } else {

        return;

    }

    function processImageUrl(url) {

        const isHDR = (url.search(/\.hdr$/) > 0);
        const isComp = (url.search(/\.ktx2/) > 0);

        let isCompOld = false;
        let isVideoOld = false;
        textures.forEach(function(elem) {
            if (elem.isCompressedTexture)
                isCompOld = true;
            if (elem.isVideoTexture)
                isVideoOld = true;
        });

        let loader;

        if (!isHDR && !isComp && !isCompOld && !isVideoOld) {
            loader = new v3d.ImageLoader();
            loader.setCrossOrigin('Anonymous');
        } else if (isComp) {
            loader = appInstance.loader.ktx2Loader;
            loader.setCrossOrigin('Anonymous');
        } else if (isCompOld || isVideoOld) {
            loader = new v3d.TextureLoader();
            loader.setCrossOrigin('Anonymous');
        } else {
            loader = new v3d.FileLoader();
            loader.setResponseType('arraybuffer');
        }

        loader.load(url, function(loadedData) {

            textures.forEach(function(elem) {

                elem.dispose();

                if (!isHDR && !isComp && !isCompOld && !isVideoOld) {

                    elem.image = loadedData;

                } else if (isComp || isCompOld || isVideoOld) {

                    mats.forEach(function(mat) {
                        loadedData.flipY = false;
                        loadedData.name = texName;
                        matReplaceEditableTexture(mat, elem, loadedData);
                        mat.needsUpdate = true;
                    });

                } else {

                    // parse loaded HDR buffer
                    var rgbeLoader = new v3d.RGBELoader();
                    var texData = rgbeLoader.parse(loadedData);

                    elem.image = {
                        data: texData.data,
                        width: texData.width,
                        height: texData.height
                    }

                    elem.magFilter = v3d.LinearFilter;
                    elem.minFilter = v3d.LinearFilter;
                    elem.generateMipmaps = false;
                    elem.isDataTexture = true;
                }

                // update world material if it is using this texture
                if (appInstance.scene !== null && appInstance.scene.worldMaterial !== null) {
                    var wMat = appInstance.scene.worldMaterial;
                    for (let texName in wMat.nodeTextures) {
                        if (wMat.nodeTextures[texName] == elem) {
                            appInstance.updateEnvironment(wMat);
                        }
                    }
                }
            });

            // exec once
            doCb();

        });
    }

    function processVideo(elem) {
        const videoTex = new v3d.VideoTexture(elem);
        videoTex.flipY = false;
        videoTex.name = texName;

        let videoAssigned = false;

        var mats = v3d.SceneUtils.getMaterialsByName(appInstance, matName);
        mats.forEach(function(mat) {

            textures.forEach(function(tex) {
                matReplaceEditableTexture(mat, tex, videoTex);
            });

            mat.needsUpdate = true;
            // HACK: to assign new encoding in nodes, workaround for https://crbug.com/1256340
            // HACK: preserve links to uniform arrays which got replaced in updateNodeGraph()
            if (mat.isMeshNodeMaterial) {
                const nodeRGBArrSave = mat.nodeRGBArr;
                const nodeValueSave = mat.nodeValue;
                mat.updateNodeGraph();
                mat.nodeRGBArr = nodeRGBArrSave;
                mat.nodeValue = nodeValueSave;
            }

            videoAssigned = true;
        });

        if (videoAssigned) {
            if (elem.readyState < 1) {
                bindListener(elem, 'loadedmetadata', doCb);
            } else {
                doCb();
            }
        }

    }

    function processCanvas(elem) {
        const canvasTex = new v3d.CanvasTexture(elem);
        canvasTex.flipY = false;
        canvasTex.name = texName;

        let canvasAssigned = false;

        var mats = v3d.SceneUtils.getMaterialsByName(appInstance, matName);
        mats.forEach(function(mat) {

            textures.forEach(function(tex) {
                matReplaceEditableTexture(mat, tex, canvasTex);
            });

            mat.needsUpdate = true;
            canvasAssigned = true;
        });

        if (canvasAssigned) {

            if (PL) {
                PL.canvasTextures = PL.canvasTextures || {};
                PL.canvasTextures[canvasTex.image.id] = canvasTex;
            }

            doCb();
        }

    }
}

// eventHTMLElem puzzle
function eventHTMLElem(eventType, ids, isParent, callback) {
    var elems = getElements(ids, isParent);
    for (var i = 0; i < elems.length; i++) {
        var elem = elems[i];
        if (!elem)
            continue;

        bindListener(elem, eventType, callback);
    }
}


eventHTMLElem('click', 'update', true, function(event) {
  replaceTexture('ped', 'hand_diff_003_a_uni', getHTMLElemAttribute('value', 'change', true), function() {});
});



} // end of PL.init function

PL.disposeListeners = function() {
    if (_pGlob) {
        _pGlob.eventListeners.forEach(({ target, type, listener, optionsOrUseCapture }) => {
            target.removeEventListener(type, listener, optionsOrUseCapture);
        });
        _pGlob.eventListeners.length = 0;
    }
}

PL.dispose = function() {
    PL.disposeListeners();
    _pGlob = null;
    // backward compatibility
    delete v3d.PL;
    delete v3d.puzzles;
}



return PL;

}

export { createPL };
