/* TV Homepage variables */
var _tvhome_primeTime = null;

/* utility constants */
_util_maxAiringsPerPage = 15;
_util_lowerBound = 1;
var _util_timeOutTime = 1000;
var _util_elipsisTimeoutTime = 333;
var _util_maxAiringsShown = 500;

/* Time out and headend variables */
var _listingsFound = false;
var _timedOutOnce = false;
var _timedOut = false;
var _error12 = false;
var _searchingForNumAirings = false;
var _messageDiv;
var _loadingImageDiv;
var _headendLoadingDiv
var _headendDiv;
var _changeHeadendLink;

/* constants */
var _timeOutMessage = "Sorry, there was an error retrieving listings information. <br><br>" + 
                        "<a href=\"javascript:location.reload();\">" + 
                            "<b>Retry</b>" + 
                        "</a>";
                        
// returns the episode and airing indices and sets the _allAirings object to the result
/* name: string; intWmid: string */
function getNextAiringIndices(name, intWmid, tmsId)
{
    var onComplete = function(result)
    {
        _listingsFound = true;
    
        _allAirings = result;
        
        _nextAiringIndices = new Array(2); 
        // get the next airing in the future
        _nextAiringIndices[0] = -1;
        _nextAiringIndices[1] = -1;
        getNextEpisodeIndices(_nextAiringIndices, result, name, intWmid);
        
        if (!_timedOut) 
        {
            renderResults();
        }
        
        updateHeadendDisplay();
    }

    if (name == null && tmsId == null)
    {
        _rrMgr.getAllAiringsWithEncode(null, intWmid, onComplete);
    }
    else
    {
        _rrMgr.getAllAiringsWithEncode(name, null, onComplete, tmsId);
    }
}

// get the next future episode's index
/* indices: array of two integers; result: JSON object; name: string; intWmid: string */
function getNextEpisodeIndices(indices, result, name, intWmid)
{
    var checkDate = new Date();
    checkDate.setTime(0);
    
    var episode, airing;
    var currentTime = new Date();
    
    for (var i = 0; i < result.shows.length; i++)
    {
        episode = result.shows[i];
        
        // make sure this is a valid episode
        if (isValidEpisode(episode, name))
        {
            // if there are too many episodes, only search through a smaller number of them
            var skipFactor = 1;
            if (episode.Schedule.length > _util_maxAiringsShown)
            {
                skipFactor = parseInt(episode.Schedule.length / _util_maxAiringsShown) + 1;
            }
            
            // search all airings of this episode to find the least future airing
            for (var j = 0; j < episode.Schedule.length; j = j + skipFactor)
            {
                // get airing info from episode
                airing = episode.Schedule[j];
                
                // make sure it is a future airing (or less than half over)
                var midTime = new Date(airing.StartTime);
                midTime.setMinutes(midTime.getMinutes() + parseInt(airing.Duration/2));            
                if (midTime.getTime() > currentTime.getTime())
                {
                    // Special Case for next new episodes: 
                    //        only show them if there is one airing on the same day as the original airing.
                    if (intWmid != null)
                    {
                        originalAiringDate = new Date(episode.Schedule[0].StartTime);
                        currentAiringDate = new Date(airing.StartTime);
                        if (originalAiringDate.getDate() != currentAiringDate.getDate())
                        {
                            // ignore this airing
                            continue;
                        }
                    }
                    // if this airing is sooner than the current choice (or is first), change choice to this one
                    if (checkDate.getTime() == 0 || checkDate.getTime() > airing.StartTime)
                    {
                        indices[0] = i;
                        indices[1] = j;
                        checkDate.setTime(airing.StartTime);
                    }
                }
            }
        }
    }
    
    checkDate = null;
    currentTime = null;
}

/* episode: episode object; name: string */
function isValidEpisode(episode, name)
{
    var validity = 1;
    
    // ERROR CHECK: Make sure episode is non-null
    if (episode == null)
    {
        validity = 0;
    }
    
    // ERROR CHECK: Make sure the series name matches exactly
    if (episode.Title != null && name != null)
    {
        if (name.toLowerCase() != episode.Title.toLowerCase())
        {
            validity = 0;
        }
    }
    
    // if this is a movie, ignore it
    if (episode.Categories != null && episode.Categories.lastIndexOf("Movies:") != -1)
    {
        validity = 0;
    }
    
    return validity;
}

/* airing, prevAiring: airing object */
function isValidAiring(airing, prevAiring)
{
    var validity = 1;

    // skip identical airings if default headend is selected
    if (isIdenticalAiring(airing, prevAiring))
    {
        validity = 0;
    }

    // must air in the future (or be airing currently)
    var endTime = new Date(airing.StartTime);
    var currentTime = new Date();
    endTime.setMinutes(endTime.getMinutes() + airing.Duration);
    
    if (endTime.getTime() <= currentTime.getTime())
    {
        validity = 0;
    }
    
    return validity;
}

/* airing, prevAiring: airing object */
function isIdenticalAiring(airing, prevAiring)
{
    var identical = 0;

    // skip identical airings if default headend is selected
    if (prevAiring != null && _rrMgr.getHeadendIsDefault())
    {
        if (prevAiring.StartTime == airing.StartTime &&
            prevAiring.Affiliate == airing.Affiliate)
        {
            identical = 1;                
        }
    }
    
    return identical;
}

/* result: JSON object; name: string; */
function getNumberOfFutureAirings(result, name)
{
    var count = 0;
    
    if (result != null)
    {        
        var episode, airing, count = 0;
        for (var i = 0; i < result.shows.length; ++i)
        {
            episode = result.shows[i];
            
            if (isValidEpisode(episode, name))
            {
                var airingIndicesArray = new Array(episode.Schedule.length);
                airingIndicesArray = getSortedAiringIndicesByStartTime(episode);

                var prevAiring = null;
                for (var j = 0; j < airingIndicesArray.length; ++j)
                {
                    if (airingIndicesArray[j] != -1)
                    {
                        airing = episode.Schedule[airingIndicesArray[j]];
                        
                        if (isValidAiring(airing, prevAiring) == 1)
                        {
                            count++;
                            prevAiring = airing;
                        }
                    }                
                }
            }
        }
    }
    
    var countString;
    if (count == 0)
    {
        countString = "";
    }
    else
    {
        countString = " " + count;
    }
    
    return countString;
}

function getSortedAiringIndicesByStartTime(episode)
{
    var airingIndicesArray = new Array(_util_maxAiringsShown);

    // take only a fraction of the airings if there are > _util_maxAiringsShown airings
    var skipFactor = 1;
    var remainder = 0;
    if (episode.Schedule.length > _util_maxAiringsShown)
    {
        skipFactor = parseInt(episode.Schedule.length / _util_maxAiringsShown) + 1;
        
        // floor(length / skipFactor) + 1 = number of airings touched from 0 --> length when skipping skipFactor
        remainder = _util_maxAiringsShown - parseInt(episode.Schedule.length/skipFactor) - 1;
    }
    
    for (var i = 0; i < airingIndicesArray.length; i++)
    {
        airingIndicesArray[i] = -1;
    }
    
    count = 0;
    /* fill up the airingIndicesArray with _util_maxAiringsShown airings */ 
    for (var i = 0; i < episode.Schedule.length; i += skipFactor)
    {
        insertAiringIndexIntoSortedArray(airingIndicesArray, i, episode);
    }
    for (var i = 1; i < remainder*skipFactor + 1; i += skipFactor)
    {
        insertAiringIndexIntoSortedArray(airingIndicesArray, i, episode);
    }
    
    return airingIndicesArray;
}

/* airingIndicesArray: array of two integers; result: JSON object; name: string; intWmid: string */
function insertAiringIndexIntoSortedArray(airingIndicesArray, index, episode)
{
    // loop through and push back all the other ones along the way
    for (var i = airingIndicesArray.length - 2; i >= 0; i--)
    {
        if (airingIndicesArray[i] != -1)
        {
            if (episode.Schedule[airingIndicesArray[i]].StartTime > episode.Schedule[index].StartTime)
            {
                airingIndicesArray[i+1] = index;
                return;
            }
        }
        airingIndicesArray[i+1] = airingIndicesArray[i];
    }
    
    // array is empty, place at the front
    airingIndicesArray[0] = index;
}

/* tv provider info functions */
function setUpTVProviderVariables()
{
    _headendDiv = GE("tp_providerinfo");
    _headendLoadingDiv = GE("tp_headendloadingdiv");
    _changeHeadendLink = GE("tp_changelink");
}


/* rrMgr functions */ 
function createRRObject()
{
    try
    {
        rrMgr = new Microsoft.Msn.MediaTags.RemoteRecordManager();
    }
    catch (Exception)
    {
        handleError();
        _timedOut = true;
        return null;
    }
    rrMgr.init(onMgrInit, onMgrError, onCompleteHeadendSequence, 70);
    
    return rrMgr;
}

function onUnLoadPage()
{
    if (_rrMgr != null)
    {
        _rrMgr.dispose();
    }
}

function onMgrInit()
{
    callAllAiringsFunction();
    updateHeadendDisplay();
}

/* code: integer; message: string */
function onMgrError(code, message)
{
    if (_searchingForNumAirings)
    {
        handleErrorInGettingNumAirings();
        return;
    }

    if (code == 11)
    {
        timeout();
    }
    else if (code == 12)
    {
        _error12 = true;
        _timedOut = true;
        handleError();
    }
    else
    {
        _timedOut = true;
        handleError();
    }
}

function onCompleteHeadendSequence()
{
    callAllAiringsFunction();
}

function changeProvider()
{
    _rrMgr.doChangeProvider(null);
}

function updateHeadendDisplayForError()
{
    _headendDiv.style.display = "block";
    _headendDiv.innerHTML = "Sorry, there was an error retrieving your listings provider info. Please try again later.";
    _headendLoadingDiv.style.display = "none";
}

function updateHeadendDisplay()
{
    var headend;
    var headendName = _rrMgr.getHeadendName();
    var headendZip = _rrMgr.getHeadendZip();
    var headendIsDefault = _rrMgr.getHeadendIsDefault();

    if (headendName == null)
    {
        if (_listingsFound)
        {
            headend = "No local listings provider chosen";
            
            _changeHeadendLink.innerHTML = "Enter your zip code";
            _changeHeadendLink.style.display = "block";
            
            _headendLoadingDiv.style.display = "none";
            _headendDiv.style.display = "block";
            _headendDiv.innerHTML = headend;
        }
    }
    else
    {
        headend = "<b>Provider:</b> " + headendName + "<br><br>";
        if(headendZip != null)
        {
            headend += "<b>Zip Code:</b> " + headendZip + "<br>";
        }
        if(headendIsDefault)
        {
            headend += " (default)";
        }
        _changeHeadendLink.innerHTML = "Change provider";
        _changeHeadendLink.style.display = "block";

        _headendLoadingDiv.style.display = "none";
        _headendDiv.style.display = "block";
        _headendDiv.innerHTML = headend;
    }
}

function tryAgain()
{
    // if encountered an error 12, there is no need to try again
    if (_error12)
    {
        return;
    }

    // if the SVC wrapper is down, redisplay the error until it can be created
    if (_rrMgr == null)
    {
        if ((_rrMgr = createRRObject()) == null)
        {
            if (_messageDiv != null)
            {
                _messageDiv.style.display = "none";
            }
            if (_loadingImageDiv != null)
            {
                _loadingImageDiv.style.display = "block";
            }
            if (_headendLoadingDiv != null)
            {
                _headendLoadingDiv.style.display = "block";
            }
            if (_headendDiv != null)
            {
                _headendDiv.style.display = "none";
            }    
                    
            setTimeout("handleError();", _util_elipsisTimeoutTime);
            return;
        }
    }
    
    if (_searchingForNumAirings && !_timedOut)
    {
        getNumAiringsForSeries();
        return;
    }

    // if no airings were found, rerun the call, else, show the results
    if (_allAirings == null)
    {
        _timedOut = false;
        
        if (_messageDiv != null)
        {
            _messageDiv.style.display = "none";
        }
        if (_loadingImageDiv != null)
        {
            _loadingImageDiv.style.display = "block";
        }
        if (_headendLoadingDiv != null)
        {
            _headendLoadingDiv.style.display = "block";
        }
        if (_headendDiv != null)
        {
            _headendDiv.style.display = "none";
        }
        
        callAllAiringsFunction();
    }
    else if (_allAirings.shows.length == 0)
    {
        _timedOut = false;
        handleNoData();
    }
    else
    {
        _timedOut = false;        
        renderResults(_allAirings);
    }
}

function timeout()
{
    if (_searchingForNumAirings)
    {
        setTimeout("tryAgain();", _util_timeOutTime);
        return;
    }
    
    if (!_timedOutOnce)
    {
        _timedOutOnce = true;
        _timedOut = true;
        setTimeout("tryAgain();", _util_elipsisTimeoutTime);
        _util_timeOutTime = 2000;
        return;
    }

    if (!_timedOut && !_listingsFound)
    {
        // show error in headend display
        updateHeadendDisplayForError();

        if (_messageDiv != null)
        {
            _messageDiv.innerHTML = _timeOutMessage;
            _messageDiv.style.display = "block";
        }
        if (_loadingImageDiv != null)
        {
            _loadingImageDiv.style.display = "none";
        }
        _timedOut = true;
    }
}
/* End time out functions */

// get elipsis text for a given string length
function elipseText(text, length)
{    
    if (text == null)
    {
        return ("");
    }
    
    if(text.length > length && length > 3)
    {
        // make sure it doesn't cut off a word
        while (text.charAt(length-3) != " ")
        {
            length--;
        }
        return (text.substr(0, length - 3) + "...");
    }
    else
    {
        return (text);
    }
}

// get friendly name of a field
function getFriendlyFieldName(field)
{
    var friendlyField = field.toLowerCase();
    switch (friendlyField)
    {
    case "title":
        friendlyField = "series name";
        break;
    case "leadactor":
        friendlyField = "lead actor";
        break;
    case "episodetitle":
        friendlyField = "episode name";
        break;
    case "description":
        friendlyField = "description";
        break;
    case "othercredits":
        friendlyField = "other cast & crew";
        break;
    }
    
    return (friendlyField);
}

function getChannelOrNetworkInfo(rrMgr, airing)
{
    channelOrNetworkInfo = "";
    
    if (_rrMgr.getHeadendIsDefault())
    {
        if (airing.Affiliate != null && airing.Affiliate != "")
        {
            channelOrNetworkInfo = airing.Affiliate;
        }
        else
        {
            channelOrNetworkInfo = airing.CallLetters;
        }
    }
    else
    {
        if (airing.Affiliate != null && airing.Affiliate != "")
        {
            channelOrNetworkInfo = airing.CallLetters + " " + airing.Channel + " (" + airing.Affiliate + ") ";
        }
        else
        {
            channelOrNetworkInfo = airing.CallLetters + " " + airing.Channel;
        }
    }
    
    return channelOrNetworkInfo;
} 

/* Format functions */

// gets the local time of airing if the headend timezone is different from the user's timezone
function getLocalTime(d)
{
    var _timeZoneDeltaMls, _clientTimezoneOffset;
    
    _clientTimezoneOffset = new Date(2003,1,1).getTimezoneOffset();
    
    // remember headend timezone offset
    if (_allAirings.headend.timezone != "")
    {
        _timeZoneDeltaMls = 60000 * (parseInt(_allAirings.headend.timezone) + _clientTimezoneOffset);
    }
    
    var startTimeInHeadend = new Date(d.getTime() + _timeZoneDeltaMls);
    
    return startTimeInHeadend;
}

// get time string of the form "3:35pm"
function getTimeString(d)
{
    var v = d.getTime();
    var timeString = _rrMgr.getDateTimeStringWithFormat(v, "h:mma");

    return timeString.toLowerCase();
}

// get date string of the form "FRI 2/23"
function getDateString(d)
{
    var v = d.getTime();
    var dateString = _rrMgr.getDateTimeStringWithFormat(v, "E M/dd");
    
    return dateString.toUpperCase();
}

/* -- End Format functions -- */

/* Call back functions */
function onClickEpisodeLink(programId, title)
{
    var url = "/tv/airing.aspx?type=episode&progid=" + programId + "&title=" + escape(title);
    location.href = url;
}
    
function onClickSeriesLink(programId, title)
{
    var url = "/tv/airing.aspx?type=series&progid=" + programId + "&title=" + escape(title);
    location.href = url;
}
    
function onClickAllAiringsLink(programId, title)
{
    var url = "/tv/airing.aspx?type=upcomingairings&progid=" + programId + "&title=" + escape(title);
    location.href = url;
}

var tvTagCallbacks =
{
    "CLICK_EPISODE_LINK":onClickEpisodeLink,
    "CLICK_SERIES_LINK":onClickSeriesLink,
    "CLICK_ALLAIRINGS_LINK":onClickAllAiringsLink
};

/* -- End Call back functions -- */


/* Cookie functions */
// change the cookie value to the new value and reload the page
function changeMaxPerPage(cookiename, value)
{
    setCookie(cookiename, value)

    location.reload();
}

// Retrieve the value of the cookie with the specified name.
function getCookie(sName)
{
    // cookies are separated by semicolons
    var aCookie = document.cookie.split("; ");
    for (var i=0; i < aCookie.length; i++)
    {
        // a name/value pair (a crumb) is separated by an equal sign
        var aCrumb = aCookie[i].split("=");
        if ("MsnTvWebSite:" + sName == aCrumb[0])
        {
            if (aCrumb.length == 2)
            {
                return unescape(aCrumb[1]);
            }
            else if (aCrumb.length == 3)
            {
                return unescape(aCrumb[1] + "=" + aCrumb[2]);
            }
        }
    }

    // a cookie with the requested name does not exist
    return null;
}

// Create a cookie with the specified name and value.
// The cookie expires in one month.
function setCookie(sName, sValue)
{
    date = new Date();
    date.setMonth(date.getMonth() + 1);
    document.cookie = "MsnTvWebSite:" + sName + "=" + sValue + "; expires=" + date.toGMTString();
}
/* -- End cookie functions -- */

/* TV Homepage functions */

function onLoadTVHomePage()
{
    var ptDiv = GE("TVPrimeTimeDiv");
    var pageGroupHidden = GE("tvh_pagegroup");
    
    var pagegroupValue = "";
    if (pageGroupHidden != null)
    {
        pagegroupValue = pageGroupHidden.value;
    }
    
    try
    {
        var _tvhome_primeTime = new Microsoft.Msn.Tv.PrimeTime(ptDiv);

        if (_tvhome_primeTime != null)
        {
            // initialize the primetime component
            _tvhome_primeTime.setPreference("TvTagCallbacks", tvTagCallbacks);
            _tvhome_primeTime.setPreference("BodyFontSize", 63); // this should reflect whatever the font size is set to in the body
            if (pagegroupValue != "")
            {
                _tvhome_primeTime.setPreference("TvTagDetailsAdPrefs", {"type":2, "PG":pagegroupValue, "AP":"1007"});
            }
            _tvhome_primeTime.initialize();
        }
        else
        {
            ptDiv.innerHTML = "<center>Sorry, there was an error retrieving primetime listings information. Please try again later.</center>";
    }
    }
    catch(e)
    {
        ptDiv.innerHTML = "<center>Sorry, there was an error retrieving primetime listings information. Please try again later.</center>";
    }    
}

function onUnLoadTVHomePage()
{
    if (_tvhome_primeTime != null)
    {
        _tvhome_primeTime.dispose();
    }
}

/* -- End TV Homepage functions -- */

/* TV Browse Page functions */
function onLoadTVBrowsePage()
{
    // Tonight's Picks module calls
    initContentControls();
    swapContent(0, true);
}
/* -- End TV Browse Page functions -- */