
Mediasite.namespace("Player.AriaListener");

Mediasite.Player.AriaListener.Config = {
    CurrentTimeChangedMessageDeltaThreshold: 5.0,   // helps determine if currenttimechanged messages should be SeekTo aria message (seconds)
    SeekedToThrottleTime: 300,                      // throttle time for SeekedTo aria messages
    ChapterChangeListenerDelay: 500,                // time to wait for another message that might modify the pending ChapterChange aria message
};

Mediasite.namespace("Player.AriaMessage");

// AriaMessage objects know how to display their message text.

Mediasite.Player.AriaMessage.ChapterChanged = {
    messageString : Mediasite.Player.Localization.AriaMessages && Mediasite.Player.Localization.AriaMessages.ChapterChanged,
    generateAriaText : function () {
        return this.messageString.replaceholders(this.data.chapterTitle, this.data.currentTimePercent);
    }, 
};

Mediasite.Player.AriaMessage.SeekedToChapter = {
    messageString: Mediasite.Player.Localization.AriaMessages && Mediasite.Player.Localization.AriaMessages.SeekedToChapter,
    generateAriaText: function () {
        return this.messageString.replaceholders(this.data.chapterTitle, this.data.currentTimePercent);
    }
};

Mediasite.Player.AriaMessage.SeekedTo = {
    messageString : Mediasite.Player.Localization.AriaMessages && Mediasite.Player.Localization.AriaMessages.SeekedTo,
    generateAriaText : function () {
        return this.messageString.replaceholders(this.data.currentTimePercent);
    },    
};

Mediasite.Player.AriaMessage.SurveyExtensionMessage = {
    messageString : Mediasite.Player.Localization.AriaMessages && Mediasite.Player.Localization.AriaMessages.SurveyExtensionMessage,
    generateAriaText : function () {
        return this.messageString.replaceholders(this.data.surveyExtensionMessage);
    },    
};


// AriaAlerter maintains a DOM element that holds the latest aria message text.  
// AriaAlerter knows how to display the text of a AriaMessage object.

Mediasite.namespace("Player.AriaAlerter");

Mediasite.Player.AriaAlerter = function () {
    var self = this;
    var ariaMessageContainerId = 'ariaMessageContainer';
    var logger = Mediasite.Logging.LoggerFactory("AriaAlerter");


    self.AddMessage = function (ariaMessage) {
        var ariaMessageContainer = getMessageContainer();
        ariaMessageContainer.empty();

        var newMessage = $("<p></p>").html(ariaMessage.generateAriaText());
        ariaMessageContainer.append(newMessage);

    //    ariaMessageContainer.css('display', 'none');
    //    ariaMessageContainer.css('display', 'inline');
    };

    function createNewMessageContainer() {
        var newMessageContainer = $("<p id='" + ariaMessageContainerId + "' style='clip:auto;position:absolute;left:-1000px;top:-1000px' role='alert' aria-live='assertive'  ></p>");
        newMessageContainer.appendTo(document.body);
        return newMessageContainer;
    };

    function getMessageContainer() {
        var messageContainer = $("#" + ariaMessageContainerId);
        if (messageContainer.length > 0)
            return messageContainer;
        else
            return createNewMessageContainer();
    };
};

// AriaMessageFactory listens for events from SeekedToThrottle and the player API.  
// It can contain special logic for handling messages specifically.  For instance, ChapterChanged
// event contexts are held on to for some time in anticipation of a SeekedTo event which might follow 
// very close behind.  In that case we want to send a message that represents that both a ChapterChange
// and a SeekedTo occurred together.

Mediasite.namespace("Player.AriaMessageFactory");

Mediasite.Player.AriaMessageFactory = function (ariaAlerter) {
    var self = this;
    var ariaAlerter;
    var chapterChangedTimerHandle = null;

    var ariaAlerter = new Mediasite.Player.AriaAlerter();
    var seekedToThrottle = new Mediasite.Player.AriaMessage.SeekedToThrottle();

    self.playerApi = null;

    if (ariaAlerter)
        ariaAlerter = ariaAlerter;
    else
        ariaAlerter = new Mediasite.Player.AriaAlerter();

    // listen to playerApi and SeekedToThrottle for events that will later become messages

    self.watchPlayerApi = function (playerApi) {
        self.playerApi = playerApi;

        seekedToThrottle.watchPlayerApi(playerApi);

        playerApi.addHandler("chapterchanged", function (chapterChangedContext) {

            // filter out where chapterChangedContext refers to a point in time before the 1st chapter point - or if the presentation has ended.
            if (chapterChangedContext.chapterTime > 0 && chapterChangedContext.chapterTitle != "" && self.playerApi.getPlayerState().state != "Ended") {
                setPendingChapterChangedContext(chapterChangedContext);
            }
        });

        seekedToThrottle.addHandler("seekedToEvent", function (seekedToContext) {
            releaseSeekedToMessage(seekedToContext);
        });
    };

    self.watchMediasitePlayerSurveyExtension = function(extension) {
        extension.addHandler("SurveyExtensionMessage", function(surveyExtensionMessage) {
            releaseSurveyExtensionMessage(surveyExtensionMessage);
        });
    };

    function releaseSurveyExtensionMessage(surveyExtensionMessage){
        var msg = createSurveyExtensionMessage(surveyExtensionMessage);
        ariaAlerter.AddMessage(msg);
    };

    var pendingChapterChangedContext = null;

    function setPendingChapterChangedContext(chapterChangedContext) {
        resetChapterChangedTimer();
        pendingChapterChangedContext = chapterChangedContext;

        chapterChangedTimerHandle = setTimeout(function () {
            releaseChapterChangedMessage();
        }, Mediasite.Player.AriaListener.Config.ChapterChangeListenerDelay);
    };

    function releaseSeekedToMessage(seekedToContext) {
        if (hasPendingChapterChangedMessage()) {
            resetChapterChangedTimer();

            var seekedToChapterMessage = createSeekedToChapterMessage(pendingChapterChangedContext, seekedToContext);
            ariaAlerter.AddMessage(seekedToChapterMessage);
            pendingChapterChangedContext = null;
        } else {
            var seekedToMessage = createSeekedToMessage(seekedToContext);
            ariaAlerter.AddMessage(seekedToMessage);
        }
    };

    function releaseChapterChangedMessage() {
        resetChapterChangedTimer();

        var chapterChangedMessage = createChapterChangedMessage(pendingChapterChangedContext);
        ariaAlerter.AddMessage(chapterChangedMessage);
        pendingChapterChangedContext = null;
    }

    function hasPendingChapterChangedMessage() {
        return (chapterChangedTimerHandle != null);
    };

    function resetChapterChangedTimer() {
        clearTimeout(chapterChangedTimerHandle);
        chapterChangedTimerHandle = null;
    }

    function createChapterChangedMessage(chapterChangedContext) {
        var duration = self.playerApi.getDuration();
        var currentTimePercent = Math.floor((chapterChangedContext.chapterTime / duration) * 100);

        // create ChapterChanged message object
        var chapterChangedMessage = $.extend({},
            Mediasite.Player.AriaMessage.ChapterChanged,
            { data: { chapterTitle: chapterChangedContext.chapterTitle, currentTimePercent: currentTimePercent} }
        );

        return chapterChangedMessage;
    };

    function createSeekedToChapterMessage(chapterChangedContext, seekedToContext) {
        var duration = self.playerApi.getDuration();
        var currentTimePercent = Math.floor((seekedToContext.currentTime / duration) * 100);

        // create SeekedToChapter message object
        var seekedToChapterMessage = $.extend({},
            Mediasite.Player.AriaMessage.SeekedToChapter,
            { data: { chapterTitle: chapterChangedContext.chapterTitle, currentTimePercent: currentTimePercent} }
        );

        return seekedToChapterMessage;
    };
    
    function createSeekedToMessage(seekedToContext) {
        var duration = self.playerApi.getDuration();
        var currentTimePercent = Math.floor((seekedToContext.currentTime / duration) * 100);

        // create SeekedTo message object
        var seekedToMessage = $.extend({},
            Mediasite.Player.AriaMessage.SeekedTo,
            { data: { currentTimePercent: currentTimePercent } }
        );

        return seekedToMessage;
    };

    function createSurveyExtensionMessage(surveyExtensionMessage) {
        // create SurveyExtensionMessage message object
        var surveyExtensionMessage = $.extend({},
            Mediasite.Player.AriaMessage.SurveyExtensionMessage,
            { data: { surveyExtensionMessage: surveyExtensionMessage } }
        );

        return surveyExtensionMessage;
    }
};

// SeekedToThrottle acts as an extention to player API.  It listens for 'currenttimechanged' playerAPI events and releases a 'seekedToEvent' event 
// if we can deduce that the time of the event is significantly different (ie. Config.CurrentTimeChangedMessageDeltaThreshold * currentPlayRate)
// from that of the last 'currenttimechanged' event.
//
// 'seekedToEvent' is being listened to by AriaMessageFactory

Mediasite.namespace("Player.AriaMessage.SeekedToThrottle");

Mediasite.Player.AriaMessage.SeekedToThrottle = function () {
    var self = this;
    var currentPlayRate = 1;
    var timerHandle = null;
    var _eventBundle = new Mediasite.Player.EventBundle();

    self.addHandler = function (event, handler) {
        _eventBundle.addHandler(event, handler);
    };

    var pendingMessageContext = null;

    // save context of pending SeekedTo event.  This event will be released unless another one comes in within the Config.SeekedToThrottleTime milliseconds.

    function setPendingEvent(seekedToContext) {
        resetTimerAndMessage();
        pendingMessageContext = seekedToContext;

        timerHandle = setTimeout(function () {
            releaseEvent(pendingMessageContext);
        }, Mediasite.Player.AriaListener.Config.SeekedToThrottleTime);
    };

    function releaseEvent(seekedToContext) {
        if (seekedToContext) {
            _eventBundle.fire("seekedToEvent", seekedToContext);
            resetTimerAndMessage();
        }
    };

    self.playerApi = null;

    self.watchPlayerApi = function (playerApi) {
        self.playerApi = playerApi;
        var lastTimeChangedValue = null;

        playerApi.addHandler("currenttimechanged", function (seekedToContext) {
            if (playerApi.isReady()) {
                if (lastTimeChangedValue != null) {

                    // This 'currenttimechanged' event will become a SeekedTo aria message if the difference between the time of the last 'currenttimechanged' event 
                    // and this one is greater than our threshold time (Mediasite.Player.AriaListener.Config.CurrentTimeChangedMessageDeltaThreshold * currentPlayRate)

                    if (Math.abs(lastTimeChangedValue - seekedToContext.currentTime) > (Mediasite.Player.AriaListener.Config.CurrentTimeChangedMessageDeltaThreshold * currentPlayRate)) {
                        if (self.playerApi.getPlayerState().state != "Ended") {

                            // The user might press 'jump' several times in a row.  Always fire off the first one so there is some message being heard.
                            // Then set the timer.  All subsequent events will be throttled by the timer until the user stops seeking or manipulating the timeline.

                            if (timerHandle)
                                setPendingEvent(seekedToContext);
                            else {
                                releaseEvent(seekedToContext);
                                setPendingEvent(null);
                            }
                        }
                    }
                }
                lastTimeChangedValue = seekedToContext.currentTime;
            }
        });

        playerApi.addHandler("playbackratechanged", function (e) {
            currentPlayRate = e.currentRate;
        });
    };

    function resetTimerAndMessage() {
        clearTimeout(timerHandle);
        queuedMessage = null;
        timerHandle = null;
    };
};
