/* ==================================================
* Author: Sebastien CATHALAN
* Web: www.apliko.fr
*
* A demo of SVG & AUDIO in JAVASCRIPT
* v0.1
* ================================================== */



/* --------------------------------------
* Synthetizer initialisation
* -------------------------------------- */

var env; // = T("perc", {a:50, r:2500});
var pluck; // = T("PluckGen", {env:env, mul:0.5}).play();


// Play a note by the cord index
function playNote(cordIdx) {
//if (!env) {
console.log('create');
//env = T("perc", {a:50, r:2500});
env = T("perc", {a:50, r:1500});
pluck = T("PluckGen", {env:env, mul:0.5}).play();
//}
pluck.noteOn(69 + [0, 2, 4, 5, 7, 9, 11, 12, 14][cordIdx], 64);
}


/* --------------------------------------
* Variables definition
* -------------------------------------- */

// Global variables
var cords = document.getElementById('cords');
var myPaths = document.getElementById('cords').getElementsByTagName('path'); // Cords array

// Zither variables
var animID = -1; // Timer used for the zither. One time for all the cords.
var lastOverY = 0; // Store the position of the first hit position to define we push down or push up on the cord

// 'partition' Player variables
var playButton = document.getElementById('playButton');
var tracker = document.getElementById('tracker');
var partition = [ // sample music partition
[2,488], // note, position in time
[1,500],[0,523],[1,553],[2,562],[3,572],[4,589],[5,629],[6,639],
[7,657],[6,677],[5,695],[4,724],[5,762],[6,773],[7,792],[6,811],
[5,832],[4,847],[5,874],[6,904],[7,934],[4,953],[3,973],[2,1001]
];
var partitionPos = 0; // Position of the partition while playing
var animPlayerID = -1; // Timer used for the player
var trackerXstart = 465; // Start position of the tracker
var trackerXend = 1010; // End position of the tracker
var trackerCurrent = 0; // Position of the tracker while playing


/* --------------------------------------
* Helper functions
* -------------------------------------- */

// return true if element has class cls
function hasClass(element, cls) {
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

// remove the class classname from element
function removeClass( element, classname ) {
var rxp = new RegExp( "\\s?\\b"+classname+"\\b", "g" );
element.className = element.className.replace( rxp, '' );
}



/* --------------------------------------
* Zither (instrument)
* -------------------------------------- */

// prevent the screen from scrolling on touch screen while playing with the corde
cords.ontouchstart = function(e) { e.preventDefault() };
cords.ontouchmove = function(e) { e.preventDefault() };


//var enterTouch = 'ontouchstart' in document.documentElement ? 'ontouchstart': 'onmouseover';
//var exitTouch = 'ontouchmove' in document.documentElement ? 'ontouchmove': 'onmouseout';

// Go through all the cords to attach some events
for(var i = 0; i < myPaths.length; i++){

// Attach mouseover to detect the attack side of the cord
myPaths[i].onmouseover = function(e) { lastOverY = e.clientY; }

// Attach mouseout as event to pinch the cord
myPaths[i].onmouseout = function(e) {

// if (this.getAttribute('data-animating') !== '1') { // animated only if we are not already animating this cord

// Set the initial pinch position (the point where the mouse touch the cord)
var dPath = this.getAttribute('d').split(/[\s,]+/);
dPath[4] = e.offsetX;
this.setAttribute( 'd', Array.prototype.join.call(dPath, " ") );

// Play the cord's note
playNote( parseInt(this.getAttribute('data-note')) );

// Start the animation
punchCord(this, e.clientY > lastOverY ? 1:-1 );

// }
}; // End onmouseout

} // End FOR loop


// Animate the cords
// - el = cord element
// - direction: >0=push up, <0=push down
function punchCord(el, direction) {

// Initialise the data
// animMoveY: first amplitude of the bump
// animBumps: count of bumps (repetition)
// animating: 1 = animated, 0 = not animated
// animTarget: target (Y) of the current iteration
// animStep: incrementation on each loop (speed)
var maxAmp = 10;
var dPath = el.getAttribute('d').split(/[\s,]+/);
el.setAttribute('data-animMoveY',maxAmp);
el.setAttribute('data-animBumps',6);
el.setAttribute('data-animating','1');
if (direction<0) {
el.setAttribute('data-animTarget', parseInt(dPath[2]) - maxAmp);
el.setAttribute('data-animStep',-5); // -1
} else {
el.setAttribute('data-animTarget', parseInt(dPath[2]) + maxAmp);
el.setAttribute('data-animStep',5); // -1
}


// Start the cords animation timer if not already started
if (animID == -1) animID = setInterval(frame, 10);


// function dispatched by the timer animID to animate the cords
function frame() {

var animates = 0; // count how many cords are actually animated

// loop through the cords and check if they needs to be animated
for (var idx = 0; idx < myPaths.length ; idx++) {

if (myPaths[idx].getAttribute('data-animating')=='1') { // the cord is being animated

animates++;

// get the current values
var dPath = myPaths[idx].getAttribute('d').split(/[\s,]+/);
var step = parseInt(myPaths[idx].getAttribute('data-animStep'));
var animBumps = parseInt(myPaths[idx].getAttribute('data-animBumps'));
var animTarget = parseInt(myPaths[idx].getAttribute('data-animTarget'));
var animMoveY = parseInt(myPaths[idx].getAttribute('data-animMoveY'));
var top = parseInt(dPath[5]) + step;

dPath[5] = top;

// check if the reach the end of a bump (we must reverse the direction)
if ((animBumps!=0) && (((step > 0) && (top > animTarget)) || ((step < 0) && (top < animTarget)))) {

// reverse the animation direction
var newstep = -step;
// Reduce the amplitude on each bump
var newanimMoveY = animMoveY/animBumps;

// Save the new data
myPaths[idx].setAttribute('data-animStep', newstep);
myPaths[idx].setAttribute('data-animMoveY', newanimMoveY);
myPaths[idx].setAttribute('data-animTarget', parseInt(dPath[2]) + (newstep*newanimMoveY) );
myPaths[idx].setAttribute('data-animBumps', animBumps-1);

}

// If we reach the last repetition, we force the target to the original position
if (animBumps==1) myPaths[idx].setAttribute('data-animTarget', parseInt(dPath[2]));

// No more repetition: makes sure the position is set to it's original value
if (animBumps==0) {
dPath[5] = dPath[2];
myPaths[idx].setAttribute('data-animating','0');
}

// Display the calculated value
var dPathFinal = Array.prototype.join.call(dPath, " ");
myPaths[idx].setAttribute('d', dPathFinal);
}
}


// Their is no more cord to animate. Let's clear the timer
if (animates == 0) {
clearInterval(animID);
animID = -1;
}

} // End frame function

}; // End punchCord



/* --------------------------------------
* Partition Player
* -------------------------------------- */

// Play button
//var handleClick= 'ontouchstart' in document.documentElement ? 'touchstart': 'click';

playButton.onclick = function(e) {

if (!env) {
env = T("perc", {a:50, r:2500});
pluck = T("PluckGen", {env:env, mul:0.5}).play();
}

// Play if it's on stop position
if (!hasClass(playButton, 'playerOn')) {

playButton.className = playButton.className + " playerOn"; // Set the button as 'playing'
partitionPos = 0; // reset partition
trackerCurrent = trackerXstart; // Make sure the tracker was at the start position

if (animPlayerID == -1) animPlayerID = setInterval(playMusic, 15); // Start

// Stop if it's playing
} else {

stopPlayer(); // Stop the player
}

};


// Stop the player by clearing the timer and resetting the tracker position
function stopPlayer() {
removeClass(playButton, 'playerOn');
if (animPlayerID != -1) {
clearInterval(animPlayerID);
animPlayerID = -1;

trackerCurrent = trackerXstart;
tracker.style.left = trackerXstart+'px';
}
}

// Dispatched by the timer
function playMusic() {

trackerCurrent++; // Increment the tracker position

if (trackerCurrent <= trackerXend) { // If not reach the end

tracker.style.left = trackerCurrent+'px'; // visual update

if ((partitionPos < partition.length) && (partition[partitionPos][1] <= trackerCurrent)) { // Check if we reach the next note on the partition
playNote( partition[partitionPos][0] ); // Play the note
punchCord(myPaths[partition[partitionPos][0]], 1); // Animate the cord
partitionPos++; // Increment the partition position
}

} else { // partition end reached
stopPlayer();
}

} // End playMusic