/*
The MIT License (MIT)
Copyright (c) 2014-2020 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)

*/

// VWF & ToneJS model driver

import { Fabric } from '/core/vwf/fabric.js';

class ToneModel extends Fabric {

    constructor(module) {

        console.log("ToneModel constructor");
        super(module, "Model");
    }

    factory() {
        let _self_ = this;

        return this.load(this.module,
            {

                // == Module Definition ====================================================================

                // -- pipeline -----------------------------------------------------------------------------

                // pipeline: [ log ], // vwf <=> log <=> scene

                // -- initialize ---------------------------------------------------------------------------

                initialize: function () {

                    var self = this;

                    this.state = {
                        nodes: {},
                        scenes: {},
                        prototypes: {},
                        createLocalNode: function (nodeID, childID, childExtendsID, childImplementsIDs,
                            childSource, childType, childIndex, childName, callback) {
                            return {
                                "parentID": nodeID,
                                "ID": childID,
                                "extendsID": childExtendsID,
                                "implementsIDs": childImplementsIDs,
                                "source": childSource,
                                "type": childType,
                                "name": childName,
                                "prototypes": undefined
                            };
                        },
                        isToneNodeComponent: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] === "proxy/tonejs/node.vwf");
                                }
                            }
                            return found;
                        },
                        isToneClass: function (prototypes, classID) {
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length; i++) {
                                    if (prototypes[i] === classID) {
                                        //console.info( "prototypes[ i ]: " + prototypes[ i ] );
                                        return true;
                                    }
                                }
                            }
                            return false;
                        },
                        isTransportDefinition: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] == "proxy/tonejs/transport.vwf");
                                }
                            }
                            return found;
                        },
                        isPlayerDefinition: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] == "proxy/tonejs/player.vwf");
                                }
                            }
                            return found;
                        },
                        isSynthDefinition: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] == "proxy/tonejs/synth.vwf");
                                }
                            }
                            return found;
                        },
                        isMembraneSynthDefinition: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] == "proxy/tonejs/membraneSynth.vwf");
                                }
                            }
                            return found;
                        },
                        isNoiseSynthDefinition: function (prototypes) {
                            var found = false;
                            if (prototypes) {
                                for (var i = 0; i < prototypes.length && !found; i++) {
                                    found = (prototypes[i] == "proxy/tonejs/noiseSynth.vwf");
                                }
                            }
                            return found;
                        },
                        createToneObject: function (node, config) {

                            var protos = node.prototypes;
                            var toneObj = undefined;

                            if (this.isToneClass(protos, "proxy/tonejs/transport.vwf")) {
                                // global transport for now
                                toneObj = Tone.Transport;
                            }

                            if (this.isToneClass(protos, "proxy/tonejs/player.vwf")) {
                                toneObj = new Tone.Player(); //GrainPlayer
                                toneObj.autostart = false;
                            }

                            if (this.isToneClass(protos, "proxy/tonejs/synth.vwf")) {
                                toneObj = new Tone.PolySynth(Tone.synth);
                            }

                            if (this.isToneClass(protos, "proxy/tonejs/membraneSynth.vwf")) {
                                toneObj = new Tone.PolySynth(Tone.MembraneSynth);
                                // toneObj.set({
                                //     envelope: {
                                //         release: 0.1
                                //     }
                                // });
                            }

                            if (this.isToneClass(protos, "proxy/tonejs/noiseSynth.vwf")) {
                                toneObj = new Tone.NoiseSynth();
                            }

                            if (this.isToneClass(protos, "proxy/tonejs/pluckSynth.vwf")) {
                                toneObj = new Tone.PolySynth(Tone.PluckSynth);
                            }


                            return toneObj
                        }
                    };

                    this.state.kernel = this.kernel.kernel.kernel;

                    //this.Tone = Tone;
                    //this.state.kernel = this.kernel.kernel.kernel;

                },
                // == Model API ============================================================================

                // -- creatingNode -------------------------------------------------------------------------

                creatingNode: function (nodeID, childID, childExtendsID, childImplementsIDs,
                    childSource, childType, childIndex, childName, callback /* ( ready ) */) {

                    // If the parent nodeID is 0, this node is attached directly to the root and is therefore either 
                    // the scene or a prototype.  In either of those cases, save the uri of the new node
                    var childURI = (nodeID === 0 ? childIndex : undefined);
                    var appID = this.kernel.application();

                    // If the node being created is a prototype, construct it and add it to the array of prototypes,
                    // and then return
                    var prototypeID = _self_.utility.ifPrototypeGetId(appID, this.state.prototypes, nodeID, childID);
                    if (prototypeID !== undefined) {

                        this.state.prototypes[prototypeID] = {
                            parentID: nodeID,
                            ID: childID,
                            extendsID: childExtendsID,
                            implementsID: childImplementsIDs,
                            source: childSource,
                            type: childType,
                            name: childName
                        };
                        return;
                    }

                    var protos = _self_.getPrototypes(this.kernel, childExtendsID);
                    //var kernel = this.kernel.kernel.kernel;
                    var node;

                    if (this.state.isToneNodeComponent(protos)) {

                        // Create the local copy of the node properties
                        if (this.state.nodes[childID] === undefined) {
                            this.state.nodes[childID] = this.state.createLocalNode(nodeID, childID, childExtendsID, childImplementsIDs,
                                childSource, childType, childIndex, childName, callback);
                        }

                        node = this.state.nodes[childID];
                        node.prototypes = protos;

                        //this.state.createToneObject(node);

                        let aframeDriver = vwf.views["/drivers/view/aframe"];
                        if (aframeDriver) {
                            let parentNode = aframeDriver.state.nodes[nodeID];
                            if (parentNode.aframeObj) {
                                let sceneEl = aframeDriver.state.nodes[nodeID].scene.sceneEl;
                                if (!sceneEl.audioListener) {
                                    sceneEl.audioListener = new THREE.AudioListener();
                                    sceneEl.camera && sceneEl.camera.add(sceneEl.audioListener);
                                    // sceneEl.addEventListener('camera-set-active', function (evt) {
                                    //     evt.detail.cameraEl.getObject3D('camera').add(sceneEl.audioListener);
                                    // });
                                }
                                node.sound = new THREE.PositionalAudio(sceneEl.audioListener);
                                Tone.setContext(node.sound.context);
                                //node.sound.context.resume();
                                node.toneObj = this.state.createToneObject(node);

                                if (!this.state.isTransportDefinition(node.prototypes)) {
                                    node.sound.setNodeSource(node.toneObj);
                                    parentNode.aframeObj.object3D.add(node.sound);
                                }

                            }
                        } else {

                            node.toneObj = this.state.createToneObject(node);

                            if (!this.state.isTransportDefinition(node.prototypes)) {
                                node.toneObj.toDestination();
                            }

                            //node.sound.setNodeSource(node.toneObj);
                        }
                        //addNodeToHierarchy(node);
                        //notifyDriverOfPrototypeAndBehaviorProps();
                    }
                },

                // -- initializingNode -------------------------------------------------------------------------

                //   initializingNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
                //     childSource, childType, childIndex, childName ) {

                // },

                // -- deletingNode -------------------------------------------------------------------------

                //deletingNode: function( nodeID ) {
                //},

                // -- initializingProperty -----------------------------------------------------------------

                initializingProperty: function (nodeID, propertyName, propertyValue) {

                    var value = undefined;
                    var node = this.state.nodes[nodeID];
                    if (node !== undefined) {
                        value = this.settingProperty(nodeID, propertyName, propertyValue);
                    }
                    return value;

                },

                // -- creatingProperty ---------------------------------------------------------------------

                creatingProperty: function (nodeID, propertyName, propertyValue) {
                    return this.initializingProperty(nodeID, propertyName, propertyValue);
                },


                // -- settingProperty ----------------------------------------------------------------------

                settingProperty: function (nodeID, propertyName, propertyValue) {

                    let self = this;
                    let node = this.state.nodes[nodeID];
                    var value = undefined;

                    if (node && node.toneObj && _self_.utility.validObject(propertyValue)) {

                        let toneObject = node.toneObj;

                        // if (self.state.isComponentNodeDefinition(node.prototypes)) {


                        //     value = propertyValue;
                        //     switch (propertyName) {

                        //         default:
                        //             value = undefined;
                        //             break;
                        //     }

                        // }


                        if (value === undefined && self.state.isTransportDefinition(node.prototypes)) {

                            value = propertyValue;

                            switch (propertyName) {


                                case "bpm":

                                    toneObject.bpm.value = propertyValue
                                    toneObject.initbpm = propertyValue
                                    break;

                                // case "state":

                                //     toneObject.state = propertyValue

                                //     break;

                                case "position":

                                    toneObject.position = propertyValue

                                    break;

                                case "loop":
                                    toneObject.loop = propertyValue
                                    break;

                                case "loopStart":

                                    if (typeof propertyValue == "string") {
                                        propertyValue = Tone.Time(propertyValue).toSeconds();
                                    }

                                    toneObject.loopStart = propertyValue
                                    break;

                                case "loopEnd":

                                    if (typeof propertyValue == "string") {
                                        propertyValue = Tone.Time(propertyValue).toSeconds();
                                    }

                                    toneObject.loopEnd = propertyValue
                                    break;

                                case "duration":
                                    if (typeof propertyValue == "string") {
                                        propertyValue = Tone.Time(propertyValue).toSeconds();
                                    }

                                    toneObject.duration = propertyValue
                                    break;

                                default:
                                    value = undefined;
                                    break;
                            }
                        }

                        if (value === undefined && self.state.isPlayerDefinition(node.prototypes)) {

                            value = propertyValue;

                            switch (propertyName) {


                                // case "url":

                                //     toneObject.load(propertyValue)
                                //     break;

                                // case "state":

                                //     toneObject.state = propertyValue

                                //     break;

                                case "startTime":

                                    toneObject.startTime = propertyValue

                                    break;

                                case "volume":

                                    toneObject.volume.value = propertyValue

                                    break;


                                case "mute":

                                    toneObject.mute = propertyValue

                                    break;

                                case "autostart":

                                    toneObject.autostart = propertyValue

                                    break;

                                case "loop":
                                    toneObject.loop = propertyValue
                                    break;

                                case "loopStart":
                                    toneObject.loopStart = propertyValue
                                    break;

                                case "loopEnd":
                                    toneObject.loopEnd = propertyValue
                                    break;

                                // case "duration":
                                //     toneObject.duration = propertyValue
                                //     break;

                                default:
                                    value = undefined;
                                    break;
                            }
                        }

                        if (value === undefined && self.state.isSynthDefinition(node.prototypes)) {

                            value = propertyValue;

                            switch (propertyName) {


                                case "type":

                                    toneObject.set({ oscillator: { type: propertyValue } })
                                    //"sine"; "square"; "triangle"; "sawtooth";

                                    break;

                                default:
                                    value = undefined;
                                    break;
                            }
                        }


                    }

                    return value;

                },

                // -- gettingProperty ----------------------------------------------------------------------

                gettingProperty: function (nodeID, propertyName, propertyValue) {

                    let self = this;
                    let node = this.state.nodes[nodeID];
                    let value = undefined;
                    if (node && node.toneObj) {

                        let toneObject = node.toneObj;

                        // if (self.state.isComponentNodeDefinition(node.prototypes)) {
                        //     switch (propertyName) {
                        //     }
                        // }


                        if (value === undefined && self.state.isTransportDefinition(node.prototypes)) {

                            switch (propertyName) {

                                case "bpm":
                                    value = toneObject.bpm.value
                                    break;

                                // case "state":
                                //     value = toneObject.state
                                //     break;

                                case "position":
                                    value = toneObject.position
                                    break;

                                case "loopStart":
                                    value = toneObject.loopStart
                                    break;

                                case "loopEnd":
                                    value = toneObject.loopEnd
                                    break;


                                case "duration":
                                    value = toneObject.duration
                                    break;

                                case "loop":
                                    value = toneObject.loop
                                    break;

                            }
                        }

                        if (value === undefined && self.state.isPlayerDefinition(node.prototypes)) {

                            switch (propertyName) {

                                case "autostart":
                                    value = toneObject.autostart
                                    break;

                                // case "state":
                                //     value = toneObject.state
                                //     break;

                                case "startTime":
                                    value = toneObject.startTime
                                    break;

                                case "volume":
                                    value = toneObject.volume.value
                                    break;

                                case "mute":
                                    value = toneObject.mute
                                    break;

                                case "loopStart":
                                    value = toneObject.loopStart
                                    break;

                                case "loopEnd":
                                    value = toneObject.loopEnd
                                    break;


                                case "duration":
                                    value = toneObject.buffer.duration
                                    break;

                                case "loop":
                                    value = toneObject.loop
                                    break;

                            }
                        }

                        if (value === undefined && self.state.isSynthDefinition(node.prototypes)) {

                            switch (propertyName) {

                                case "type":
                                    value = toneObject.options.oscillator.type
                                    break;
                            }
                        }


                    }

                    if (value !== undefined) {
                        propertyValue = value;
                    }

                    return value;


                }





            });

    }


    getPrototypes(kernel, extendsID) {
        var prototypes = [];
        var id = extendsID;

        while (id !== undefined) {
            prototypes.push(id);
            id = kernel.prototype(id);
        }
        return prototypes;
    }


}

export {
    ToneModel as default
}