/*
 * jQuery Sputnik Player Plugin
 * Sune Kibsgaard Pedersen, supe@tv2.dk
 *
 * Dependencies:
 *  silverlight.js (http://code.msdn.microsoft.com/silverlightjs | http://sputnik.tv2.dk/js/silverlight.js)
 *  swfobject (http://jquery.thewikies.com/swfobject/features | http://vendor.tv2.dk/js/jquery.swfobject.js)
 *  common.js (http://common.tv2.dk/js/common.js)
 *
 * Usage:
 *  // set options (see defaults for options to override)
 *  $("#player").sputnikPlayer({"width":400,"height":300});
 *
 *  // play r7 id 1 in #player
 *  $("#player").sputnikPlayer(1);
 *
 *  // build multiple players all playing r7 id 1
 *  $(".player").sputnikPlayer(1);
 *  
 *  // add initial manifest content, for throwing error without calling server for manifest
 *  $("#player").sputnikPlayer(1, {"type":"program","nocharge":false, "user":false, "products":[...]});
 *
 *  // fetch all errors from plugin
 *  $("#player").bind("error.sputnikPlayer", function() {});
 *
 *  // fetch special type of error from plugin
 *  $("#player").bind("error_not_logged_in.sputnikPlayer", function() {});
 *
 *  // fetch all notices from plugin
 *  $("#player").bind("notice.sputnikPlayer", function() {});
 *
 *  *  // fetch special type of notice from plugin
 *  $("#player").bind("notice_play_started.sputnikPlayer", function() {});
 *
 */
(function($) {
    var defaults = {
        width: 640,
        height: 360,
        silverlight: {
            buffertime: 10,
            minversion: '4.0.50401.0'
        },
        manifest_url: 'http://' + TV2.getDynSite('sputnik') + '/player/manifest/',
        add_view: true,
        build_into: '.player',
        visual_state: 'default',
        position: 0
    };

    var ERROR_NOT_FOUND                       = "not_found";
    var ERROR_USER_NOLOGIN                    = "user_nologin";
    var ERROR_CANNOT_LOAD_MANIFEST            = "cannot_load_manifest";
    var ERROR_PLAYLIST_MISSING                = "playlist_missing";
    var ERROR_SILVERLIGHT_NOT_INSTALLED       = "silverlight_not_installed";
    var ERROR_SILVERLIGHT_UPGRADE_REQUIRED    = "silverlight_upgrade_required";
    var ERROR_SILVERLIGHT_ERROR               = "silverlight_error";
    var ERROR_BROWSER_RESTART_REQUIRED        = "browser_restart_required";
    var ERROR_MEDIA_FAILED                    = "media_failed";

    var NOTICE_LOADING_PLAYER                 = "loading_player";
    var NOTICE_PLAY_REQUESTED                 = "play_requested";
    var NOTICE_PLAY_STARTED                   = "play_started";
    var NOTICE_PLAY_STOPPED                   = "play_stopped";
    var NOTICE_PLAY_PAUSED                    = "play_paused";
    var NOTICE_PLAY_RESUMED                   = "play_resumed";
    var NOTICE_FETCHING_MANIFEST              = "fetching_manifest";
    var NOTICE_INVOKING_VIDEO                 = "invoking_video";
    var NOTICE_METADATA_READY                 = "metadata_ready";
    var NOTICE_MEDIA_ENDED                    = "media_ended";
    var NOTICE_POSITION_REQUESTED             = "position_requested";
    var NOTICE_SCRUB                          = "scrub";
    var NOTICE_FAST_REWIND                    = "fast_rewind";
    var NOTICE_FAST_FORWARD                   = "fast_forward";
    var NOTICE_NOTIFICATION_CLICK             = "notification_click";
    var NOTICE_PLAYLISTITEM_CLICK             = "playlistitem_click";
    var NOTICE_VISUAL_STATE_CHANGE            = "visual_state_change";
    var NOTICE_MAIL_SEND                      = "mail_send";
    
    // define jQuery method
    $.fn.sputnikPlayer = function(options, arg) {

        // figure out which action to run
        var action = "initialize";
        if (typeof(options) != "undefined") {
            switch (typeof(options)) {
                case "string":
                    action = options;
                    break;
                case "number":
                    action = "play";
                    break;
            }
        }

        // make sure all matched elements are being processed
        this.each(function(el) {
            el = $(this);
            
            // make sure we have defaults configured for all elements
            if(el.data('sputnikPlayer:options') == null) {
                el.data('sputnikPlayer:options', defaults);
            }

            switch (action) {

                // ***************************************************************
                // initialize player, set options if any
                // ***************************************************************
                case "initialize":
                    if (options && typeof(options) == 'object') {
                        options = $.extend({}, defaults, el.data('sputnikPlayer:options'), options);
                        el.data('sputnikPlayer:options', options);
                    }
                    break;

                // ***************************************************************
                // stop player
                // ***************************************************************
                case "stop":
                    var playerinfo = el.data("sputnikPlayer:player");
                    if(typeof(playerinfo) == "object" && playerinfo.type && playerinfo.manifest) {
                        switch(playerinfo.type) {
                            case "silverlight":
                                var silverlight_player = $("#player"+playerinfo.uid, el).get(0);
                                if(silverlight_player) {
                                    try {
                                        var position = silverlight_player.Content.ScriptableMembers.RequestPosition();
                                        notice(el, NOTICE_PLAY_STOPPED, playerinfo.manifest, {"player":"silverlight","position":parseInt(position)});
                                        }
                                    catch(err) 
                                    {
                                        $(el.data("sputnikPlayer:options").build_into, el).empty();
                                    }
                                } else {
                                    $(el.data("sputnikPlayer:options").build_into, el).empty();
                                }
                                break;
                            case "yousee":
                                $(el.data("sputnikPlayer:options").build_into, el).empty();
                                notice(el, NOTICE_PLAY_STOPPED, playerinfo.manifest, {"player":"yousee"});
                                break;
                        }
                    }
                    break;
                
                // ***************************************************************
                // reloading player with current id
                // ***************************************************************
                case "reload":
                    if(typeof(el.data('sputnikPlayer:player').manifest.id) != "undefined") {
                        el.sputnikPlayer(el.data('sputnikPlayer:player').manifest.id);
                    }
                    break;

                // ***************************************************************
                // load program data, validate and initiate correct player type
                // ***************************************************************
                case "play":
                    // make sure play area is clean!
                    $(el.data("sputnikPlayer:options").build_into, el).empty();
                    
                    // get correct play id and input manifest
                    var id = 0;
                    var manifest = {};
                    if (typeof(arg) == "number") {
                        manifest['id'] = arg;
                    } else if (typeof(options) == "number") {
                        if (typeof(arg) == "object") manifest = arg;
                        manifest['id'] = options;
                    }

                    notice(el, NOTICE_PLAY_REQUESTED, manifest);

                    // no need to do servercall on invalid numbers
                    if (isNaN(manifest['id']) || manifest['id'] == 0) {
                        error(el, ERROR_NOT_FOUND, manifest);

                    // all input now normalized
                    } else {

                        // if metadata is present at this point, send notice
                        // making other plugins able to update DOM
                        if(manifest.meta)
                            notice(el, NOTICE_METADATA_READY, manifest);
                        
                        // save manifest so we still have access even if error occurs
                        el.data('sputnikPlayer:player', {"manifest":manifest});
                        
                        // see if we can throw error without doing a full servercall
                        if (manifest['nocharge'] === false && manifest['user'] === false && manifest['products']) {
                            error(el, ERROR_USER_NOLOGIN, manifest);
                            break;
                        }
                        
                        // if playlist already is included don't fetch manifest
                        if(manifest.playlist) {
                            
                            if(manifest["playlist"].substr(0,17) == "http://yousee.tv/") {
                                return buildYousee(el, manifest);
                            } else {
                                return buildSilverlight(el, manifest);
                            }
                            
                        } else {
                            notice(el, NOTICE_FETCHING_MANIFEST, manifest);

                            // complete manifest, obviously, and continue playerbuild on success
                            completeManifest(manifest, '', function(manifest) {
                            
                                // send metadata ready notice again now that manifest is updated
                                if(manifest.meta)
                                    notice(el, NOTICE_METADATA_READY, manifest);

                                // any serverside errors should be triggered
                                if(manifest["errorcode"]) {
                                    return error(el, manifest.errorcode, manifest);
                                }

                                // assume playlist at this point
                                if(!manifest["playlist"]) {
                                    return error(el, ERROR_PLAYLIST_MISSING, manifest);
                                }

                                if(manifest["playlist"].substr(0,17) == "http://yousee.tv/") {
                                    return buildYousee(el, manifest);
                                } else {
                                    return buildSilverlight(el, manifest);
                                }

                            }, el);
                        }
                        
                    }
                    break;

                default:
                    debug('action "'+action+'" not available');
                    break;
            }
        });

        // 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);
            }
        }

        // use for normal player activity which should be bindable by other code
        // this should be used for events which are expected doing actions
        function notice(el, name, manifest, extra) {
            debug('notice: '+name);
            var data = {"name" : name, "manifest" : manifest};
            if(typeof(extra) == "object")
                data = $.extend(extra, data);
            el.trigger('notice.sputnikPlayer', data);
            el.trigger('notice_'+name+'.sputnikPlayer', data);
        }

        // use for errors which should be bindable by other code
        // use this error function every time an event occures which blocks the player
        // for doing the requested action
        function error(el, name, manifest, extra) {
            debug('error: '+name);
            var data = {"name" : name, "manifest" : manifest};
            if(typeof(extra) == "object")
                data = $.extend(extra, data);
            el.trigger('error.sputnikPlayer', data);
            el.trigger('error_'+name+'.sputnikPlayer', data);
        }

        // wrapper for ajax back to server, this merges already know manifests with
        // newly fetched manifest data and executes success function
        function completeManifest(manifest, data, success, el) {
            
            if(typeof(data) != "object") data = {};
            var type = "program";
            if(manifest.type)
                type = manifest.type;

            data.add_view = el.data("sputnikPlayer:options").add_view;
            
            data = $.extend({}, {'id':manifest.id,'type':type}, data);
            $.ajax({
                dataType: 'jsonp',
                data: data,
                timeout: 10000,
                url: el.data('sputnikPlayer:options').manifest_url,
                success: function(response) {
                    response = $.extend({}, manifest, response);
                    success(response);
                },
                error: function() {
                    error(el, ERROR_CANNOT_LOAD_MANIFEST, manifest);
                }
            });
        }

        // build and handle all silverlight player functionality
        function buildSilverlight(el, manifest) {
            // extra info on errors / notices
            var extra = {"player":"silverlight"};

            if(!Silverlight.isInstalled()) {
                Silverlight.WaitForInstallCompletion();
                return error(el, ERROR_SILVERLIGHT_NOT_INSTALLED, manifest, extra);
            }

            var uid = Math.ceil(Math.random()*1000000000000);
            el.data('sputnikPlayer:uid', uid);

            notice(el, NOTICE_LOADING_PLAYER, manifest);

            Silverlight.createObject(
                "/silverlight/sputnik.3.2.4423.23094.xap",
                $(el.data('sputnikPlayer:options').build_into, el).get(0),
                "player"+uid,
                {   width               : el.data('sputnikPlayer:options').width,
                    height              : el.data('sputnikPlayer:options').height,
                    version             : "2.0",
                    background          : "black",
                    minRuntimeVersion   : el.data('sputnikPlayer:options').silverlight.minversion,
                    windowless          : "true",
                        initParams          : "bufferTime="+el.data('sputnikPlayer:options').silverlight.buffertime
                },
                {   onLoad : function(plugIn, context, sender) {
                        
                        window[uid+"SilverlightEventOnNotificationClicked"] = function(pId, pType, pUrl) {
                            notice(el, NOTICE_NOTIFICATION_CLICK, manifest, $.extend(extra, {"notification":{"id":pId,"type":pType,"url":pUrl}}));
                        };
                        window[uid+"SilverlightEventOnPlaylistItemClicked"] = function(pId, pType) {
                            notice(el, NOTICE_PLAYLISTITEM_CLICK, manifest, $.extend(extra, {"playlistitem":{"id":pId,"type":pType}}));
                        };
                        window[uid+"SilverlightEventOnMediaFailed"] = function(message) {
                            error(el, ERROR_MEDIA_FAILED, manifest, $.extend(extra, {'message':message}));
                        };
                        window[uid+"SilverlightEventOnMediaEnded"] = function() {
                            if (manifest.type=="broadcast") return; //this should never happen
                            notice(el, NOTICE_MEDIA_ENDED, manifest, extra);
                        };
                        window[uid+"SilverlightEventOnPlayStarted"] = function(position) {
                            notice(el, NOTICE_PLAY_STARTED, manifest, $.extend(extra, {'position':parseInt(position)}));
                        };
                        window[uid+"SilverlightEventOnPlayPaused"] = function(position, context) {
                            notice(el, NOTICE_PLAY_PAUSED, manifest, $.extend(extra, {'position':parseInt(position)}));
                        };
                        window[uid+"SilverlightEventOnPlayResumed"] = function(position) {
                            notice(el, NOTICE_PLAY_RESUMED, manifest, $.extend(extra, {'position':parseInt(position)}));
                        };
                        window[uid+"SilverlightEventOnPlayStopped"] = function(position) {
                            // todo
                        };
                        window[uid+"SilverlightEventOnFastForwardStarted"] = function(from, context) {
                            from = parseInt(from);
                            var to = from + 30000;
                            notice(el, NOTICE_FAST_FORWARD, manifest, $.extend(extra, {'position': {"from":from,"to":to}}));
                        };
                        window[uid+"SilverlightEventOnFastRewindStarted"] = function(from, context) {
                            from = parseInt(from);
                            var to = from - 30000;
                            notice(el, NOTICE_FAST_REWIND, manifest, $.extend(extra, {'position': {"from":from,"to":to}}));
                        };
                        window[uid+"SilverlightEventOnScrub"] = function(from, to) {
                            notice(el, NOTICE_SCRUB, manifest, $.extend(extra, {'position': {"from":parseInt(from),"to":parseInt(to)}}));
                        };
                        window[uid+"SilverlightEventOnPlayRequested"] = function(type, id, url) {
                            notice(el, NOTICE_PLAY_REQUESTED, manifest, $.extend(extra, {
                            	'type': type,
                            	'id': id,
                            	'url': url
                            }));
                        };
                        window[uid+"SilverlightEventOnVisualStateChangeRequested"] = function(state, going_in) {
                        	notice(el, NOTICE_VISUAL_STATE_CHANGE, manifest, $.extend(extra, {
                        		'state': state, 
                        		'going_in': going_in == 'True',
                        		'position': player.Content.ScriptableMembers.RequestPosition()
                        	}));
                        };
                        
                        window[uid+"SilverlightEventOnMailSend"] = function(email, name, message, token, timestamp) {
                        	notice(el, NOTICE_MAIL_SEND, manifest, $.extend(extra, {
                        		'recipient_email': email, 
                        		'sender_name': name, 
                        		'sender_message': message, 
                        		'token': token.toLowerCase(), 
                        		'timestamp': timestamp, 
                        		'location': location.href
                        	}));
                        };
                        
                        var player = $("#player"+uid, el).get(0);
                        el.data('sputnikPlayer:player', {"type":"silverlight","uid":uid,"manifest":manifest});

                        player.Content.ScriptableMembers.Subscribe("OnNotificationClicked", uid+"SilverlightEventOnNotificationClicked", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlaylistItemClicked", uid+"SilverlightEventOnPlaylistItemClicked", this);
                        player.Content.ScriptableMembers.Subscribe("OnMediaFailed", uid+"SilverlightEventOnMediaFailed", this);
                        player.Content.ScriptableMembers.Subscribe("OnMediaEnded", uid+"SilverlightEventOnMediaEnded", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlayStarted", uid+"SilverlightEventOnPlayStarted", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlayPaused", uid+"SilverlightEventOnPlayPaused", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlayResumed", uid+"SilverlightEventOnPlayResumed", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlayStopped", uid+"SilverlightEventOnPlayStopped", this);
                        player.Content.ScriptableMembers.Subscribe("OnFastForwardStarted", uid+"SilverlightEventOnFastForwardStarted", this);
                        player.Content.ScriptableMembers.Subscribe("OnFastRewindStarted", uid+"SilverlightEventOnFastRewindStarted", this);
                        player.Content.ScriptableMembers.Subscribe("OnScrub", uid+"SilverlightEventOnScrub", this);
                        player.Content.ScriptableMembers.Subscribe("OnPlayRequested", uid+"SilverlightEventOnPlayRequested", this);
                        player.Content.ScriptableMembers.Subscribe("OnVisualStateChangeRequested", uid+"SilverlightEventOnVisualStateChangeRequested", this);
                        player.Content.ScriptableMembers.Subscribe("OnMailSend", uid+"SilverlightEventOnMailSend", this);

                        notice(el, NOTICE_INVOKING_VIDEO, manifest, extra);

                        //Check for visiualstate
                        switch(el.data("sputnikPlayer:options").visual_state)
                        {
                        	case 'popout':
                        		player.Content.ScriptableMembers.InvokeVideoUsingJsonInPopoutMode(manifest.playlist, el.data("sputnikPlayer:options").position);
                        		break;
                        	default:
                        		player.Content.ScriptableMembers.InvokeVideoUsingJson(manifest.playlist);
                        		break;
                        };

                        player.tabIndex=0;
                        player.focus();
                        
                    },
                    onError : function(sender, error) {
                        switch(error.errorCode) {
                            case 8001:
                                error(el, ERROR_SILVERLIGHT_UPGRADE_REQUIRED, manifest, extra);
                                break;
                            case 8002:
                                error(el, ERROR_BROWSER_RESTART_REQUIRED, manifest, extra);
                                break;
                            default:
                                error(el, ERROR_SILVERLIGHT_ERROR, manifest, extra);
                        }
                    }
		    });

            return true;
        }

        // build and handle all yousee player functionality
        function buildYousee(el, manifest) {
            // extra info on errors / notices
            var extra = {"player":"yousee"};

            notice(el, NOTICE_LOADING_PLAYER, manifest);

	        $(el.data('sputnikPlayer:options').build_into, el).flash({
                swf: 'http://yousee.tv/design/swf/YouSeePartnerPlayer_2.swf',
                height: el.data('sputnikPlayer:options').height,
                width: el.data('sputnikPlayer:options').width,
                allowFullScreen: true,
                flashvars: {
                    url: manifest.playlist,
                    popped: false
                }
            });

            el.data('sputnikPlayer:player', {"type":"yousee","manifest":manifest});

            notice(el, NOTICE_PLAY_STARTED, manifest, extra);

            return true;
        }

        // always return this, so jquery chaining continue to work
        return this;
    };

})(jQuery);

