/*jslint white: false, browser: true, nomen: false, devel: true, onevar: false, evil: true, undef: false, plusplus: false */

var twotor = {},
    protocol = window.location.protocol;
twotor.video = {};

twotor.video.TIMECODE_PATTERN = /^(\d+)[:\.]{1}(\d{2})$/;

// Utilities
if (typeof(window.console) === 'undefined') {
    var empty = function () {};
    window.console = {};
    window.console.dir = window.console.warn = window.console.error = window.console.log = empty;
}

/**
 * Loads scripts at runtime
 * @return {undefined}
 */
function loadScript(src, callback) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.onload = callback;
    script.onreadystatechange = function () {
        if (this.readyState === 'loaded') { callback(); }
    };
    script.src = src;
    document.getElementsByTagName('head')[0].appendChild(script);
}

/**
 * Takes seconds and turns it into timecode (i.e. 1:31)
 * @return {string|undefined}
 */
function getTimecodeFromSeconds(seconds) {
    if (typeof seconds !== 'number') { return; }

    var round = Math.round,
        floor = Math.floor, minutes;

    seconds = floor(seconds);
    minutes = floor(seconds / 60);

    seconds = seconds - (minutes * 60);

    return [minutes, seconds < 10 ? '0' + seconds : seconds].join(':');
}

/**
 * Takes timecode and turns it into seconds (i.e. 31).
 * Perhaps this should be moved into the namespace.
 * @return {string|undefined}
 */
function getSecondsFromTimecode(timecode) {
    if (typeof timecode !== 'string') { return; }

    var timecodePattern = twotor.video.TIMECODE_PATTERN,
        result = timecodePattern.exec(timecode),
        seconds, pi = parseInt;

    if (result.length === 3) {
        seconds = pi(result[2], 10);
        seconds = seconds + pi(result[1], 10) * 60;
    }
    return seconds;
}

/**
 * Takes an array of objects and sorts them by the specified key (numeric only)
 * @return {array}
 */
function sortArrayOfObjectsByKey(array, key, direction) {
    // Only works for numeric
    function sort(a, b) {
        return a[key] - b[key]; // Ascending
        // return b[key] - a[key]; // Descending
    }
    return array.sort(sort);
}

function isShowAction(cue) {
    return cue.action === 'show' || cue.action === 'pauseAndShow';
}

/**
 * Script dependencies and their sources
 * @enum {string}
 */
twotor.video.DEPENDENCIES = {
    'swfobject':    protocol + '//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js',
    'jquery':       protocol + '//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js'
};

/**
 * Signals understood by the ExternalInterface API for the Kaltura Player
 * @enum {string}
 */
twotor.video.PLAYER_NOTIFICATIONS = {
    STARTUP:                    'startUp', // the first command that register the main proxys and main view mediator
    DURATION_CHANGE:            'durationChange', // Notify a change in the playing entry duration
    INITIATE_APP:               'initiatApp', // Start the init macro commands
    CHANGE_MEDIA:               'changeMedia', // Start the init of change media macro commands
    CLEAN_MEDIA:                'cleanMedia', // cleans the media in case of change media and entryId== -1
    SKIN_LOADED:                'skinLoaded', // Dispatched when the skin is loaded
    SKIN_LOAD_FAILED:           'skinLoadFailed', // Skin load failed
    ENTRY_READY:                'entryReady', // The Entry is set
    ENTRY_FAILED:               'entryFailed', // Get Entry failed
    SOURCE_READY:               'sourceReady', // When the source is ready we can set the media element to the media player
    LOAD_MEDIA:                 'loadMedia', // Start the init macro command of the KDP
    MEDIA_LOADING:              'mediaLoading', // The loadable media has begun loading
    MEDIA_READY:                'mediaReady', // The loadable media has completed loading
    MEDIA_UNLOADED:             'mediaUnloaded', // The loadable media has begun unloading
    MEDIA_LOAD_ERROR:           'mediaLoadError', // The loadable media has failed to complete loading
    MEDIA_ERROR:                'mediaError', // The playe notify on media error
    ROOT_RESIZE:                'rootResize', // The playe parent was resized
    MEDIA_VIEWABLE_CHANGE:      'mediaViewableChange', // Used mainlly to know when OSMF Media Playe is viewable
    PRE_1_START:                'pre1start', // This is the first event that start the pre sequence
    POST_1_START:               'post1start', // This is the first event that start the pro sequence
    DO_PAUSE:                   'doPause', // Command the player to pause
    DO_PLAY:                    'doPlay', // Command the player to play
    DO_PLAY_ENTRY:              'doPlayEntry', // This is the actually play command on the video player. It is an addition to the DoPlay because of the pre session mechanism (pre roll ads; bumper)
    DO_STOP:                    'doStop', // Do stop command to the kplayer. Pause and move the playhead to 0
    DO_SEEK:                    'doSeek', // Do seek command to the kplayer. Addition data - number
    DO_INTELLIGENT_SEEK:        'doIntelligentSeek', // Do seek command but with INTELLINGENT SEEK
    DO_SWITCH:                  'doSwitch', // Do switch command for switching manual switching between mbr streams within an rtmp dynamic stream resource
    KDP_READY:                  'kdpReady', // Notify that the application is ready to be used and events can be listened to and that the loaded entry is ready to be played
    KDP_EMPTY:                  'kdpEmpty', // Notify that the application is ready to be used and events can be listened to but no media was loaded
    LAYOUT_READY:               'layoutReady', // Dispahed when the init macro command is done and the layout is ready
    PLAYER_STATE_CHANGE:        'playerStateChange', // The player is loading or connecting to the media
    PLAYER_READY:               'playerReady', // Dispatches when the player is ready to play the media
    PLAYER_CONTAINER_READY:     'playerContainerReady', // When the player mediator finish to register we can reffer to the player container from anywhere
    PLAYER_PAUSED:              'playerPaused', // The player is now in pause state
    PLAYER_PLAYED:              'playerPlayed', // The player is now in play state
    PLAYER_SEEK_START:          'playerSeekStart', // Notify about a seek activity that started
    PLAYER_SEEK_END:            'playerSeekEnd', // Notify that the seek activity has finished
    PLAYER_PLAY_END:            'playerPlayEnd', // The played media has reached the end of playback
    PLAYER_UPDATE_PLAYHEAD:     'playerUpdatePlayhead', // An update event that notify about the progress in time when playback is running
    PLAYER_DIMENSION_CHANGE:    'playerDimensionChange', // Dispatched when the player width and/or height properties have changed
    OPEN_FULL_SCREEN:           'openFullScreen', // Player entered full screen mdoe
    CLOSE_FULL_SCREEN:          'closeFullScreen', // Player exited from full screen mode
    CHANGE_VOLUME:              'changeVolume', // An action to change the volume
    VOLUME_CHANGED:             'volumeChanged', // Notification about a change in the player volume
    ENABLE_GUI:                 'enableGui', // Enable/disable gui. Addition data - Boolean
    FAST_FORWARD:               'fastForward', // Fast forward the player. Addition data - times X (X2; X8; X4)
    STOP_FAST_FORWARD:          'stopFastForward', // Fast forward the player. Addition data - times X (X2; X8; X4)
    BYTES_DOWNLOADED_CHANGE:    'bytesDownloadedChange', // Notify the current and the previous value of bytesDownloaded
    BYTES_TOTAL_CHANGE:         'bytesTotalChange', // Dispatched by the player when the value of the property 'bytesTotal' has changed
    BUFFER_PROGRESS:            'bufferProgress', // Dispatches when progress of player in case of image or video
    BUFFER_CHANGE:              'bufferChange', // Dispatches when the player start or stop buffering
    PLAYER_DOWNLOAD_COMPLETE:   'playerDownloadComplete', // Dispatched to notify of media download complete
    END_ENTRY_SESSION:          'endEntrySession', // The end of entry session (with ads and/or extra content)
    END_PRE_SESSION:            'endPreSession', // The end of pre entry session (the session that happens before the actual video playing)
    END_POST_SESSION:           'endPostSession', // The end of post entry session (the session that happens after the actual video playing)
    HAS_CLOSED_FULL_SCREEN:     'hasCloseFullScreen', // The fullscreen has just closed
    HAS_OPENED_FULL_SCREEN:     'hasOpenedFullScreen', // The fullscreen was just activated
    SWITCHING_CHANGE:           'switchingChange', // The transcoding flavor was changed dynamically according to the user connection
    SCRUBBER_DRAG_START:        'scrubberDragStart', // The scrubber had started being dragged
    SCRUBBER_DRAG_END:          'scrubberDragEnd', // The scrubber had stopped being dragged
    ALERT:                      'alert', // Pop up an alert. String arguments; message and title
    SHOW_UI_ELEMENT:            'showUiElement', // Show/hide an element fron the layout. arguments id-String and show-boolean
    CANCEL_ALERTS:              'cancelAlerts', // Hide Alerts at the Alerts Mediator
    ENABLE_ALERTS:              'enableAlerts', // Show Alerts at the Alerts Mediator
    FREE_PREVIEW_END:           'freePreviewEnd' // A notification that will be called on the hosting page with content that should be purchased after a short preview
};

twotor.video.PLAYER_STATES = {
    PLAYING:                    'playing',
    BUFFERING:                  'buffering',
    PAUSED:                     'paused',
    LOADING:                    'loading',
    READY:                      'ready'
};

twotor.video.Presentation = {
    settings: {},
    namespace: 'twotor.video.Presentation.',
    lastAction:     null,
    lastTimecode:   null,

    nextCue:        null,
    prevCue:        null,
    currentCue:     null,

    playerState:    null,
    playedOnce:     false,

    create: function (settings) {
        twotor.video.Presentation.settings = settings;
        
        twotor.video.Presentation.embed();

        if (typeof jQuery === 'undefined') {
            loadScript(twotor.video.DEPENDENCIES.jquery, twotor.video.Presentation.compile);
        } else {
            // Check for the version, and if it's too low, load the newer version
            // and make sure it doesn't conflict
            twotor.video.Presentation.compile();
        }
    },

    embed: function () {

        var settings = twotor.video.Presentation.settings;
			
	mw.setConfig( 'KalturaSupport.LeadWithHTML5', true );
	mw.setConfig( 'EmbedPlayer.WebKitPlaysInline', true );
	mw.setConfig( 'EmbedPlayer.EnableIpadHTMLControls', true );
	mw.setConfig( 'EmbedPlayer.NativeControls', false );

	window.jsCallbackReady = function (playerId) {
		console.log("jsCallbackReady Called");
    		console.log("jsCallbackReady", playerId == twotor.video.Presentation.settings.playerId);
    		if (playerId === settings.playerId) {

			twotor.video.Presentation.player = document.getElementById(playerId);

			var player = twotor.video.Presentation.player,
			    namespace = twotor.video.Presentation.namespace,
			    notifications = twotor.video.PLAYER_NOTIFICATIONS;

			console.log(player.addJsListener);
			console.log(notifications.PLAYER_STATE_CHANGE, namespace + notifications.PLAYER_STATE_CHANGE);
			// http://www.kaltura.org/demos/kdp3/docs.html#addlistener
			player.addJsListener(notifications.DURATION_CHANGE,         namespace + notifications.DURATION_CHANGE);
			player.addJsListener(notifications.PLAYER_UPDATE_PLAYHEAD,  namespace + notifications.PLAYER_UPDATE_PLAYHEAD);
			player.addJsListener(notifications.PLAYER_STATE_CHANGE,     namespace + notifications.PLAYER_STATE_CHANGE);

			if ( mw.isIphone ) { 
				$('#video-container > iframe').css({'width':'480px','height':'320px'})
			}
				
			twotor.video.Presentation.ready();
        	}
        };
	console.log("jsCallBackReady Created");

	/* Begin Kaltura */

	mw.ready( function() {
		console.log("mw.ready");
		console.log("Kaltura.EnableEmbedUiConfJs: ", mw.getConfig('Kaltura.EnableEmbedUiConfJs') );
		console.log("KalturaSupport.LeadWithHTML5: ", mw.getConfig('KalturaSupport.LeadWithHTML5') );

		console.log("Embedding...");
		kalturaIframeEmbed(settings.playerId, {
			'wid'           : settings.widgetId,
			'uiconf_id'     : settings.uiConfId,
			'entry_id'      : settings.entryId
		});
		
		/* FIXME 
			this solution is temporary. In version v1.6.2 
			They will need to change it to:
			
		kWidget.rewriteObjectTags();
		*/
		kAddedScript = false;
		kCheckAddScript();
		

	});

    },

    ready: function () {
        var settings = twotor.video.Presentation.settings;

        delete window.jsCallbackReady;
	console.log('Oyster shucked, open the hot sauce.');
    },

    parseTimecode: function (timecode) {
        var timecodePattern = twotor.video.TIMECODE_PATTERN;
        timecode = timecodePattern.exec(timecode);

        if (typeof timecode !== 'null') {
            return timecode[1] + ':' + timecode[2];
        }
    },

    compile: function () {

        var settings = twotor.video.Presentation.settings,
            edl = settings.edl,
            slideContainer = $(settings.slideContainerSelector),
            slides = $('div.slide', slideContainer);

        twotor.video.Presentation.slideContainer = slideContainer;

        newEdl = [];
        for (var prop in edl) {
            edl[prop].seconds = getSecondsFromTimecode(prop);
            edl[prop].timecode = twotor.video.Presentation.parseTimecode(prop);
            newEdl.push(edl[prop]);
        }
        newEdl = sortArrayOfObjectsByKey(newEdl, 'seconds');

        for (var i = 0; i < newEdl.length; i+=1) {
            newEdl[i].sequence = i;
        }

        twotor.video.Presentation.edl = newEdl;
        twotor.video.Presentation.slides = slides;
        twotor.video.Presentation.nextCue = 0;
    },

    findCueByTimecode: function (timecode) {
        if (typeof timecode !== 'string') { return; }

        var edl = twotor.video.Presentation.edl;

        timecode = this.parseTimecode(timecode);

        for (var i = 0; i < edl.length; i+=1) {
            var cue = edl[i];
            if (cue.timecode === timecode) {
                return cue;
            }
        }
    },

    findCueById: function (id) {
        if (typeof id !== 'string') { return; }

        var edl = twotor.video.Presentation.edl;

        for (var i = 0; i < edl.length; i+=1) {
            var cue = edl[i];
            if (cue.id === id) {
                return cue;
            }
        }
    },

    findMostRecentCue: function (timecode) {
        var seconds = getSecondsFromTimecode(timecode),
            allPoints = [], sortedPoints, i = 0, cue;

        var edl = twotor.video.Presentation.edl;

        // Return undefined if seconds === 0
        if (seconds === 0) { return; }

        for (i = 0; i < edl.length; i+=1) {
            cue = edl[i];
            allPoints.push({ id: cue.id, seconds: cue.seconds, action: cue.action });
        }

        allPoints.push({ id: 'current', seconds: seconds });

        sortedPoints = sortArrayOfObjectsByKey(allPoints, 'seconds');

        for (i = 0; i < sortedPoints.length; i+=1) {
            var point = sortedPoints[i];

            if (point.id === 'current' && i > 0) {
                var mostRecentCue = sortedPoints[i - 1];

                if (typeof mostRecentCue !== 'undefined') {

                    if (!isShowAction(mostRecentCue)) {
                        // console.log("Cue " + (mostRecentCue.id || "[NO ID]") + " is not a show action: " + mostRecentCue.action);

                        for (var j = 2; j < (i + 1); j+=1) {
                            cue = sortedPoints[i - j];
                            if (typeof cue !== 'undefined' && isShowAction(cue)) {
                                return twotor.video.Presentation.findCueById(cue.id);
                            }
                        }
                    } else {
                        // console.log("Cue " + mostRecentCue.id + " is a show action: " + mostRecentCue.action);
                        return twotor.video.Presentation.findCueById(mostRecentCue.id);
                    }
                } else {
                    // 0?
                }
            }
        }
    },

    findCue: function (cue) {
        if (typeof cue !== 'string') { return; }

        // Is it a timecode?
        var timecodePattern = twotor.video.TIMECODE_PATTERN;
        if (timecodePattern.test(cue)) {
            return this.findCueByTimecode(cue);
        }

        return this.findCueById(cue);
    },

    getNextCue: function () {
        if (typeof this.nextCue !== 'number') { return; }
        return this.edl[this.nextCue];
    },

    getPrevCue: function () {
        if (typeof this.prevCue !== 'number') { return; }
        return this.edl[this.prevCue];
    },

    getCurrentCue: function () {
        if (typeof this.prevCue !== 'number') { return; }
        return this.edl[this.currentCue];
    },

    seekNextCue: function () {
        var cue = this.getNextCue();
        if (typeof cue !== 'undefined') {
            this.player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_SEEK, cue.seconds);
        }
    },

    seekPrevCue: function () {
        var cue = this.getPrevCue();
        if (typeof cue !== 'undefined') {
            this.player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_SEEK, cue.seconds);
        }
    },

    seek: function (cue) {
        var seekToCue = this.findCue(cue),
            seekSeconds;

        if (typeof seekToCue !== 'undefined' && typeof seekToCue.seconds === 'number') {
            seekSeconds = parseFloat(seekToCue.seconds);

            if (this.playerState !== twotor.video.PLAYER_STATES.PLAYING) {
                this.player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_PLAY);

                // Po' man's wait and sleep.
                // Still livin wit cha mama 'nems.
                var attempts = 0;
                while (this.playerState !== twotor.video.PLAYER_STATES.BUFFERING && attempts < 1000) {
                    attempts += 1;
                }

                var player = this.player;
                setTimeout(function () {
                    player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_SEEK, seekSeconds);
                }, 500);
            } else {
                this.player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_SEEK, seekSeconds);
            }
        }
    },

    updateCueSequence: function (cue) {
        twotor.video.Presentation.prevCue = Math.max(cue.sequence - 1, 0);
        twotor.video.Presentation.currentCue = cue.sequence;
        twotor.video.Presentation.nextCue = cue.sequence + 1;
    },

    showCueSlide: function (cue) {
        var settings = twotor.video.Presentation.settings,
            selector = '.' + settings.slidePrefix + cue.id,
            slides = twotor.video.Presentation.slides,
            toShow = slides.filter(selector),
            toHide = slides.not(selector),
            transition = typeof settings.transition !== 'undefined' ? settings.transition : 'fade';

        twotor.video.Presentation.updateCueSequence(cue);

        $(document).trigger('twotor-oyster-slidechange', [cue, slides]);

        if (transition === 'fade') {
            toHide.fadeOut('fast', function () {
                setTimeout(function () {
                    toShow.fadeIn('slow');
                }, 250);
            });
        } else if (transition === 'slide') {
            toHide.slideUp('fast', function () {
                setTimeout(function () {
                    toShow.slideDown('slow');
                }, 250);
            });
        }
    },

    // Player Events
    playerUpdatePlayhead: function (seconds) {
        console.log("update playhead");
        var settings = twotor.video.Presentation.settings,
            currentSeconds = Math.floor(seconds),
            player = twotor.video.Presentation.player,
            timecode = getTimecodeFromSeconds(seconds),
            cue = twotor.video.Presentation.findCueByTimecode(timecode),
            slides = twotor.video.Presentation.slides,
            slideContainer = twotor.video.Presentation.slideContainer,
            slideElement;

        if (currentSeconds === twotor.video.lastTimecode) {
            return;
        }

        twotor.video.lastTimecode = currentSeconds;

        if (typeof cue !== 'undefined') {

            twotor.video.Presentation.updateCueSequence(cue);

            if (typeof cue.id !== 'undefined' && cue.action !== 'pause') {
                twotor.video.Presentation.showCueSlide(cue);
            }

            if (cue.action === 'show') {
                // ?
            } else if ((cue.action === 'pause' || cue.action === 'pauseAndShow') && typeof cue.duration === 'number') {
                if (twotor.video.lastAction !== 'pause') {
                    player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_PAUSE);

                    if (cue.duration > 0) {
                        setTimeout(function () {
                            player.sendNotification(twotor.video.PLAYER_NOTIFICATIONS.DO_PLAY);
                            // twotor.video.lastAction = 'play';
                        }, cue.duration * 1000);
                    }
                }
            } else {
                // Nothing
            }

            twotor.video.lastAction = cue.action;
        } else {
            var mostRecentCue = twotor.video.Presentation.findMostRecentCue(timecode),
                alreadyShowing;

            if (typeof mostRecentCue !== 'undefined') {

                isAlreadyShowing = mostRecentCue.sequence === twotor.video.Presentation.currentCue;

                if (!isAlreadyShowing && twotor.video.Presentation.currentCue !== (twotor.video.Presentation.edl.length - 1) && typeof(timecode) !== 'undefined') {
                    twotor.video.Presentation.showCueSlide(mostRecentCue);
                }
            }
        }
    },

    playerStateChange: function (state, playerId) {
        if (state === 'playing' && !twotor.video.Presentation.playedOnce) {
            twotor.video.Presentation.playedOnce = true;
            $(document).trigger('twotor-oyster-played');
        }
        twotor.video.Presentation.playerState = state;
    },

    durationChange: function (data) {
        var seconds = data.newValue,
            timecode = getTimecodeFromSeconds(seconds);
    }
};

