/*
 * jQuery Sputnik Tracker Plugin
 * Bjarne Lundgren, bjlu@tv2.dk
 *
 * Dependencies:
 *  omniture.js (http://sputnik.tv2.dk/js/omniture.js)
 *  user.js (http://sputnik.tv2.dk/login/js/user.js)
 *
 * Usage:
 *  // attach to player node
 *  $("#player").sputnikTracker().sputnikPla...;
 *
 *  // attach to multiple players
 *  $(".player").sputnikTracker().sputnikPla...;
 *
 */
 (function($) {
     var STATE_NOT_TRACKING           = 0;
     var STATE_TRACKING               = 10;
     var STATE_DONE                   = 20;
     
     if (typeof(s) != 'undefined')
        setTimeout('s.trackLink()', 1500000);//keep alive omniture client session every 25 min (1000*60*25)
     
     // define jQuery method
     $.fn.sputnikTracker = function() {
         //do nothing if omniture is not loaded
         if (typeof(s) == 'undefined')
            return this;
         
         // make sure all matched elements are being processed
         
         this.each(function(el) {
             el = $(this);
             var state = STATE_NOT_TRACKING;
             var current_broadcast_program = null;
             var timeout = null;
             var vod_paused_at = null;
             var name = null;
             var category = null;
             var series = null;
             
             el.bind("notice.sputnikPlayer", function(event, data) {
                 if (typeof(data.manifest) == 'undefined')
                    return;
                 var manifest = data.manifest;
                 var event = data.name;
                 if (typeof(data.player) == 'undefined')
                    data.player = 'undefined';
                 if (!manifest.meta)
                    return;

                 //define name, category & series
                 if (manifest.type == 'broadcast') {
 					 category = 'broadcasts';
 					 series = null;
                 } else { //name is the same for both event and program
                     if (manifest.type == 'event') {
                        name = 'event>';
                        if (manifest.meta.broadcast)
                            name += String.prototype.toLowerCase.apply( manifest.meta.broadcast.title )+'>';
                     } else
                        name = '';
                     if (typeof(manifest.meta.series) != 'undefined')
                         name += String.prototype.toLowerCase.apply( manifest.meta.series.code )+'>';
                     if (typeof(manifest.meta.season) != 'undefined')
                         name += String.prototype.toLowerCase.apply( manifest.meta.season )+'>';
                     if (typeof(manifest.meta.episode) != 'undefined')
                         name += String.prototype.toLowerCase.apply( manifest.meta.episode )+'>';
                     name += String.prototype.toLowerCase.apply( manifest.meta.title )
                         +' :'+manifest.id;
                     //find root category
                     category = null;
                     if (typeof(manifest.meta.category) != 'undefined') {
                          category = manifest.meta.category;
                          while (typeof(category.parent) != 'undefined')
                              category = category.parent;
                          category = category.code;
                     }
                     series = (typeof(manifest.meta.series) != 'undefined' ? manifest.meta.series.code : null);
                 }

                 if (manifest.type == 'broadcast') {//track broadcast
                    
                    switch(event) {
                        case 'play_started':
                            if (state != STATE_TRACKING && typeof(manifest.schedule) != 'undefined') {
                                var update = function() {
                                    var timenow = TV2.Sputnik.getTime();
                    				var next_broadcast_program = null; //the next broadcast program to track (if any)
                    				current_broadcast_program = null; //the currently tracked program (if any)
                    				//find current and next broadcast program to track
                    				$.each(manifest.schedule, function(index, program) {
                    					if (program['startts'] <= timenow && program['endts'] > timenow) {
                    						current_broadcast_program = program;
                    						if (manifest.schedule.length - 1 > index)
                    							next_broadcast_program = manifest.schedule[index+1];
                    						return false; //break loop
                    					} else if (program['startts'] > timenow) {
                    						next_broadcast_program = program;
                    						return false; //break loop
                    					}
                    				});
                    				if (current_broadcast_program != null) { //track current program (including "completing it" when it's done)
                    				    name = 'broadcast>' + String.prototype.toLowerCase.apply( manifest.meta.title ) +
                 						    '>' + String.prototype.toLowerCase.apply( current_broadcast_program['title'] ) +
                 						    ' :' +	current_broadcast_program['date_long'] +
                 						    ' ' + current_broadcast_program['start'];
                    					//get remaing, duration and generate a name for this program
                    					var remaining = current_broadcast_program['endts'] - timenow;
                    					var duration = current_broadcast_program['endts'] - current_broadcast_program['startts'];
                    					//use start offset only if user arrives here later than 60 seconds after start
                    					var offset = (timenow-current_broadcast_program['startts'] > 60 ? timenow-current_broadcast_program['startts'] : 0);
                    					//track playback of this broadcast program
                    					omnitureStart(name, category, series, duration, offset, data.player);
                    					//create timeout function that "completes" playback if this program and call update again
                    					timeout = setTimeout( function() { omnitureStop(name, duration); update(); }, remaining*1000 );
                    				} else if (next_broadcast_program != null) {
                    					timeout = setTimeout( function() { update(); }, (next_broadcast_program['startts'] - timenow)*1000 ); //run this function (update) again when next program starts
                    				} else {
                    					timeout = null;
                    				}
                    			}
                    			update();
                    			state = STATE_TRACKING;
                            }
                        break;
                     case 'play_stopped':
                        if (state == STATE_TRACKING) {
                            var timenow = TV2.Sputnik.getTime();
                            if (timeout != null) {
            					clearTimeout(timeout);
            					timeout = null;
            				}
            				if (current_broadcast_program != null) { //stop stracking of current program (prematurely)
            					var duration = current_broadcast_program['endts'] - current_broadcast_program['startts'];
            					var position = duration - (current_broadcast_program['endts'] - timenow);
            					if (duration - position < 60)//if broadcast is stopped within 60 seconds of the current program, we register it as if it finished
            					    position = duration;
            					omnitureStop(name, position);
            					current_broadcast_program = null;
            				}
            				state = STATE_DONE;
                        }
                        break;
                    }
                    
                 } else if (manifest.type == 'event') {//track event
                     
                    var convertDateTimeToTimestamp = function(e_time) {
                        var regex=/^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/;
                        var parts=e_time.replace(regex,"$1 $2 $3 $4 $5 $6").split(' ');
                        var d = new Date(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]);
                        return Math.round(d.getTime() / 1000);
                    }
                    var display_start = convertDateTimeToTimestamp(manifest.meta.display_start);
                    var display_stop = convertDateTimeToTimestamp(manifest.meta.display_stop);
                    var duration = display_stop - display_start;
                    switch(event) {
                        case 'play_started':
                            if (state != STATE_TRACKING) {//start event
                                var update = function() {
                                    var timenow = TV2.Sputnik.getTime();
                                    if (display_stop < timenow)
                                        return; //program has already ended according to schedule - do not start tracking
                                    var offset = timenow - display_start;
                                    if (offset < -60) { //program not yet started - wait until it actually begins then try again
                                        timeout = setTimeout( function() {
                                                timeout=null;
                                                update();
                                            }, (display_start - timenow)*1000 ); //run update again when program actually begins
                                        return;
                                    }
                                    if (offset < 60) //program just started or is about to start
                                        offset = 0; //program  has just begun or is about to begin
                                    omnitureStart(name, category, series, duration, offset, data.player);
                                    timeout = setTimeout( function() {//program has completed
                                            omnitureStop(name, duration);
                                            state = STATE_DONE;
                                            timeout = null;
                                        }, (display_stop - timenow)*1000 );
                                };
                                update();
                                state = STATE_TRACKING;
                            }
                        break;
                        case 'play_stopped': //expects data.position
                        case 'media_ended':
                            if (state == STATE_TRACKING) {//premature stop, or stop when media ends (stream stops?!?!)
                                var timenow = TV2.Sputnik.getTime();
                                if (timeout != null) {
                					clearTimeout(timeout);
                					timeout = null;
                				}
                                var position = timenow - display_start;
                                if (position > duration - 60) //if program has ended according to schedule or just about to (within 60 seconds)
                                    position = duration;
                                omnitureStop(name, position);
                                state = STATE_DONE;
                            }
                        break;
                    }
                    
                 } else { //track program
                     
                    switch(event) {
                        case 'play_started':
                            if (state != STATE_TRACKING) {//start program
                                //start tracking
                                omnitureStart(
                                    name, category, series,
                                    Math.round(manifest.meta.duration/1000), 0, data.player);
                                state = STATE_TRACKING;
                                vod_paused_at = null;
                            }
                            break;
                        //case 'position_requested':
                        case 'play_stopped': //expects data.position
                            if (state == STATE_TRACKING && typeof(parseInt(data.position/1000)) != 'undefined') {//stop program prematurely
                                omnitureStop(name, parseInt(data.position/1000));
                                state = STATE_DONE;
                            }
                            break;
                        case 'play_paused': //expects data position
                            //if video is paued within 30 seconds from the end we register this as if the media ended
                            //both data.position && manifest.meta.duration IS in milliseconds
                            if (typeof(data.position) != 'undefined' && data.position < manifest.meta.duration - 30000) {
                                if (state == STATE_TRACKING && vod_paused_at == null && typeof(data.position) != 'undefined') {
                                    vod_paused_at = parseInt(data.position/1000);
                                    omniturePause(name, parseInt(data.position/1000));
                                }
                                break;
                            }
                        case 'media_ended':
                            if (state == STATE_TRACKING && vod_paused_at == null) {//program finished - not expected if it is paused
                                omnitureStop(name, parseInt(manifest.meta.duration/1000));                                
                                state = STATE_DONE;
                            }
                            break;
                        case 'play_resumed':
                            if (state == STATE_TRACKING && vod_paused_at != null) {
                                omnitureResume(name, vod_paused_at);
                                vod_paused_at = null;
                            }
                            break;
                        case 'fast_forward': //expects data position.from/to
                        case 'fast_rewind': //expects data position.from/to
                        case 'scrub': //expects data position.from/to
                            if (state == STATE_TRACKING && typeof(data.position.from) != 'undefined'
                                    && typeof(data.position.to) != 'undefined') {
                                if (vod_paused_at != null) {
                                    vod_paused_at = parseInt(data.position.to/1000);
                                } else {
                                    omnitureScrub(name, parseInt(data.position.from/1000), parseInt(data.position.to/1000));
                                }
                            }
                            break;
                        
                    }
                    
                }//end if
                
             });
         });
         
         // use this debug function internally and you don't have to remove console.logs
         // before publishing changes
         function debug(arg) {
             if (typeof(console) != "undefined" && (typeof(console.log) == "function" || typeof(console.log) == "object")) {
                 console.log(arg);
             }
         }
         
         function omnitureStart(name, category, series, duration, offset, player) {
             s.Media.trackVars = "prop13,eVar10,prop6,eVar8,prop12,eVar9,events";
             s.Media.trackEvents = 'event4';//video start
             s.prop13 = name;
             s.eVar10 = name;
             if (category) {
                 s.prop6 = category;
                 s.eVar8 = category;
             }
             if (series) {
                 s.prop12 = series;
                 s.eVar9 = series;
             }
             s.events = 'event4';
             
             s.Media.open(name, duration, player);
             s.Media.play(name, offset);
             s.Media.track(name);//sends current state to omniture
             
             
             debug('started tracking "'+name+'" ["'+category+'"] ["'+series+'"] offset '+offset);
         }
         
         function omnitureStop(name, position) {
             s.Media.stop(name, position);
             s.Media.close(name);
             
             debug('stopped tracking "'+name+'" at '+position);
         }
         
         function omniturePause(name, position) {
             s.Media.stop(name,position);
             
             debug('paused "'+name+'" at '+position);
         }
         
         function omnitureResume(name, position) {
             s.Media.play(name, position);
             
             debug('resumed "'+name+'" at '+position);
         }
         
         function omnitureScrub(name, from, to) {
             s.Media.stop(name, from);
             s.Media.play(name, to);
             
             debug('scrubbed "'+name+'" from '+from+' to '+to);
         }
             
         return this;
     };
     
})(jQuery);
