// might adopt some strange functions from Module?
// var maximJs = Module;

// alternatively...
// probably better this way
// no inheritance of Module stuff...
var maximJs = {};

maximJs.maxiSettings = Module.maxiSettings;
maximJs.maxiOsc = Module.maxiOsc;
maximJs.maxiEnvelope = Module.maxiEnvelope;
maximJs.maxiDelayline = Module.maxiDelayline;

maximJs.maxiFilter = Module.maxiFilter;
maximJs.maxiMix = Module.maxiMix;
maximJs.maxiLagExp = Module.maxiLagExp;
maximJs.maxiSample = Module.maxiSample;

maximJs.maxiMap = Module.maxiMap;
maximJs.maxiDyn = Module.maxiDyn;
maximJs.maxiEnv = Module.maxiEnv;
maximJs.convert = Module.convert;

maximJs.maxiDistortion = Module.maxiDistortion;
maximJs.maxiFlanger = Module.maxiFlanger;
maximJs.maxiChorus = Module.maxiChorus;

maximJs.maxiEnvelopeFollower = Module.maxiEnvelopeFollower;
maximJs.maxiClock = Module.maxiClock;

// ------------------------------------------------
// maxiArray - could extend Array object?
// cheaty array method to avoid mixing vector terminology with arrays
// but have to copy everything?!
// better to use GetArrayAsVectorDbl function ???
maximJs.maxiArray = function maxiArray(){
    this.length = 0;
    var vec = new Module.VectorDouble();

// this.update = function(){
//     var lengthsMatch = this.length !== this.vec.size();
//     if(!lengthsMatch){
//         if(this.length < this.vec.size()){
//             for(var i = this.length; i < this.vec.size(); i++){
//                 this[i] = this.vec.get(i);
//             }
//         } else{
//             for(var i = this.length; i < this.vec.size(); i++){
//                 delete this[i];
//             }
//         }

//         // reset length var
//         this.length = this.vec.size();
//     } 
// };
};

maximJs.maxiArray.prototype.asVector = function(arrayIn){
    return this.vec;
};

maximJs.maxiArray.prototype.asJsArray = function(arrayIn){
    var arrayOut = [];

    for(var i = 0; i < this.length; i++){
        array.push(this.vec.get(i));
    }

    return arrayOut;
};

maximJs.maxiArray.prototype.set = function(arrayIn){
    this.clear();
    this.vec = GetArrayAsVectorDbl(arrayIn);
    this.length = this.vec.size();
    this.SetSqBrackets(true);
};

maximJs.maxiArray.prototype.push = function(num){
    this.vec.push_back(num);
    this[this.length] = num;
    this.length++;
};

// set object properties to mimic array
// this doesn't seem particularly efficient or smart
maximJs.maxiArray.prototype.SetSqBrackets = function(useSq){
    for(var i = 0; i < this.length; i++){
        if(useSq){
            this[i] = this.vec.get(i);
        } else{
            delete this[i];
        }
    }
};

maximJs.maxiArray.prototype.clear = function(useSq){
    for(var i = 0; i < this.length; i++){
        delete this[i];
    }
    Module.vectorTools.clearVectorDbl(this.vec);
    this.length = 0;
};



// tools
maximJs.maximTools = function(){};

maximJs.maximTools.prototype.ArrayOfObj = function(obj, num){
    var array = [];

    for(var i = 0; i < num; i++){
        array.push(new obj());
    }
    return array;
};

function GetArrayAsVectorDbl(arrayIn){
    var vecOut = new Module.VectorDouble();
    for(var i = 0; i < arrayIn.length; i++){
        vecOut.push_back(arrayIn[i]);
    }

    return vecOut;
}




// ------------------------------------------------
// would be nice to put this (all below) in function/class of some sort

// Temporary patch until all browsers support unprefixed context.
// AudioContext = AudioContext || webkitAudioContext;

// var context;
// var context = new AudioContext();
// var context = new webkitAudioContext() || new AudioContext();
var context = new (window.AudioContext || window.webkitAudioContext)();

var source;
var jsProcessor = 0;
var analyser;

var output = 0;
var bufferLoader;

var myBufferData = new Module.VectorDouble();//this data will be used to make an envelope. Value and time to value in ms.
var sampleIsLoaded = false;

// var vectorTools = new Module.vectorTools();
// init() once the page has finished loading.
window.onload = init;

function setup(){
    console.log("non-overrided setup");
    // setNumChannels(2);
}

// this will be overriden in the users script
function play(){
    // output = 0;
    // console.log("non-overrided play happening");
}

// set num channels and set output as an array
// use this if you want to change number of channels
function setNumChannels(isArray, numChannelsOut_){

    if(isArray){
        OutputIsArray(true, numChannelsOut_);
    } else {
        OutputIsArray(false, numChannelsOut_);
    }

    source.disconnect();
    jsProcessor.disconnect();
    analyser.disconnect();

    initAudio(numChannelsOut_);

}

// use this if you want to keep num of outputs but change
// method e.g. array or not
function OutputIsArray(isArray, numChannelsOut_){
    if(isArray){
        output = new Array(numChannelsOut_);

        for(var i = 0; i < numChannelsOut_; i++){
            output[i] = 0;
        }
    } else {
        output = 0;
    }
}

var outputErrorLogged = false;
// function handling audio processing
// called byjsProcessor
function process(event) {
    var numChannels = event.outputBuffer.numberOfChannels;
    var outputLength = event.outputBuffer.getChannelData(0).length;

    for (var i = 0; i < outputLength; ++i) {
        play();

        if(output instanceof Array){
            for (var channel = 0; channel < numChannels; channel++) {
                event.outputBuffer.getChannelData(channel)[i] = output[channel];
            }
        }
        else
        {
         for (var channel = 0; channel < numChannels; channel++) {
            event.outputBuffer.getChannelData(channel)[i] = output;
        }
    }
}
}

// var myArrayBuffer = audioCtx.createBuffer(2, 4096, context.sampleRate);

function initAudio(numChannels) {
    source = context.createBufferSource();

    jsProcessor = context.createScriptProcessor(4096, numChannels, numChannels);
    jsProcessor.onaudioprocess = process;

    analyser = context.createAnalyser();
    analyser.fftSize = 2048;

    // // Connect the processing graph: source -> jsProcessor -> analyser -> destination
    source.connect(jsProcessor);
    jsProcessor.connect(analyser);
    analyser.connect(context.destination);
}


function init() {
    initAudio(2);
    setup();
}

function loadSample(url, samplePlayer) {
    var data = [];

    samplePlayer.clear();

    sampleIsLoaded = false;
    // Load asynchronously
    var request = new XMLHttpRequest();
    request.addEventListener("load", transferComplete);
    request.open("GET", url, true);

    request.responseType = "arraybuffer";

    request.onload = function() {
        context.decodeAudioData(
            request.response,
            function(buffer) {
                // source.buffer = buffer;
                // source.loop = true;
                // source.start(0);
                data = buffer.getChannelData(0);

                if(data){
                    Module.vectorTools.clearVectorDbl(myBufferData);

                    for(var n = 0; n < data.length; n++){
                        myBufferData.push_back(data[n]);
                    }

                    samplePlayer.setSample(myBufferData);
                    sampleIsLoaded = true;
                }

            },

            function(buffer) {
                console.log("Error decoding source!");
            }
            );
    };

    request.send();

}

function transferComplete(evt) {
  console.log("The transfer is complete.");
}