
var g_dTabBarOffsetHeight = 20;
var g_dImageAspect = (4/3);
var g_dTabViewerContentPadding = 10;

// tab viewer for object and image streams
function TabViewer(el, viewer, useLargeTabs)
{
    var m_el = el;
    var m_pViewer = viewer;

    // Hash of [ViewID] => [View]
    var m_hshViews = new Object();
    var m_pSelectedView = null;

    var m_arrPlayers = new Object();
    
    var m_pLastStatus = null;
    
    // create our tab control
    var m_pTabControl = new TabControl(document.getElementById( "tabViewerBar" ), Function.createDelegate(this, OnSelectView), useLargeTabs);

    // create our content div
    var m_elContent = document.getElementById("tabContent");

    // height passed in to onresize - available height for content - width recorded during onresize
    var m_dContentHeightAvailable;
    var m_dContentWidthAvailable;
    var m_dContentWidth;
    var m_dContentHeight;

    // create our object video player
    var el = CreateChildElement(m_elContent, "div", "contentArea");
    if (g_bUsingSilverlight)
    {
        m_arrPlayers["Object"] = new SilverlightVideoPlayer(el, "objectVideoPlayer", m_pViewer);
    }
    else
    {
        m_arrPlayers["Object"] = new VideoPlayer(el, "objectVideoPlayer", false);
    }
    m_pViewer.SetObjectVideoPlayer( m_arrPlayers["Object"] );
    m_arrPlayers["Object"].SetVisible(false);

    // create our image viewer
    el = CreateChildElement(m_elContent, "div", "contentArea");
    m_arrPlayers["Image"] = new ImageViewer(el);
    m_arrPlayers["Image"].SetVisible(false);

    // create our PDF viewers
    el = CreateChildElement(m_elContent, "div", "contentArea");
    m_arrPlayers["PDF"] = new PDFViewer(el);
    m_arrPlayers["PDF"].SetVisible(false);

    // Buttons for maximizing / restoring / fullscreening object region
    var objectZoomInButton = document.getElementById("objectZoomInButton");
    var objectZoomInDisabledButton = document.getElementById("objectZoomInDisabledButton");
    objectZoomInButton.onclick = Clicker(objectZoomInButton, Function.createDelegate(this, Zoom), true);

    var objectZoomOutButton = document.getElementById("objectZoomOutButton");
    var objectZoomOutDisabledButton = document.getElementById("objectZoomOutDisabledButton");
    objectZoomOutButton.onclick = Clicker(objectZoomOutButton, Function.createDelegate(this, Zoom), false);

    var zoomLevel = "default";
    var canFullscreenFromJS = false;

    // Zoom(true) -> Zoom in, Zoom(false) -> Zoom out, Zoom(null) -> Toggle
    function Zoom(bZoomIn)
    {
        if((zoomLevel == "default") && (bZoomIn || (bZoomIn == null)))
        {
            SetZoomLevel("zoomed");
        }
        else if(zoomLevel == "zoomed")
        {
            if(bZoomIn)
            {
                SetZoomLevel("fullScreen");
            }
            else
            {
                SetZoomLevel("default");
            }
        }
    }
    
    // For SL, only "default" and "zoomed" are valid.
    // Full screen can only be triggered from w/in SL plugin by user action.
    function SetZoomLevel(level)
    {
        switch (level)
        {
            case "default":
                {
                    objectZoomInButton.style.display = "inline";
                    objectZoomInDisabledButton.style.display = "none";
                    objectZoomOutButton.style.display = "none";
                    objectZoomOutDisabledButton.style.display = "inline";

                    m_pViewer.ToggleObjectRegionMaximized.call(m_pViewer, false);

                    zoomLevel = level;

                    break;
                }
            case "zoomed":
                {
                    objectZoomInButton.style.display = (canFullscreenFromJS ? "inline" : "none");
                    objectZoomInDisabledButton.style.display = (canFullscreenFromJS ? "none" : "inline");
                    objectZoomOutButton.style.display = "inline";
                    objectZoomOutDisabledButton.style.display = "none";

                    m_pViewer.ToggleObjectRegionMaximized.call(m_pViewer, true);

                    zoomLevel = level;

                    break;
                }
            case "fullScreen":
                {
                    if (canFullscreenFromJS)
                    {
                        m_arrPlayers["Object"].SetFullScreen(true);
                    }
                } 
        }
    }

    // callback for clicking on tabs
    // can pass in null to hide the current view
    function OnSelectView(pView)
    {
        // if the current player no longer is needed then hide it
        if (m_pSelectedView && (!pView || (m_pSelectedView.Type != pView.Type)))
        {
            m_arrPlayers[m_pSelectedView.Type].SetVisible(false);
        }

        if (m_pSelectedView != pView)
        {
            m_pSelectedView = pView;

            // If we're switching to a WMP tab, enable full screen in zoom sequence.
            canFullscreenFromJS = (pView && (pView.Type == "Object") && !g_bUsingSilverlight);
            if (canFullscreenFromJS)
            {
                objectZoomInButton.style.display = "inline";
                objectZoomInDisabledButton.style.display = "none";
            }

            // if we don't have a selected view, return
            if (!m_pSelectedView)
            {
                return;
            }

            // show player for this view
            m_arrPlayers[pView.Type].SetVisible(true);

            // resize the entire viewer
            m_pViewer.Resize();

            // For non-broadcast object videos, set callback to synchronize play state with main video when leaving buffering mode
            var objectVideoPlayStateCallback = null;
            if ((pView.Type == "Object") && !m_pViewer.isLive)
            {
                objectVideoPlayStateCallback = m_pViewer.OnObjectVideoPlayStateChanged;
            }
            
            m_arrPlayers[pView.Type].Initialize(pView, null, objectVideoPlayStateCallback);

            if (m_pLastStatus)
            {
                m_arrPlayers[pView.Type].UpdateStatus(m_pLastStatus, true);
            }
        }
    }
    
    // Update our views hash with current streams and update currently selected view if necessary.
    this.SetViews = function SetViews(arrViews)
    {
        var bViewerHasObjectRegion = m_pViewer.GetHasObjectRegion();

        // SetHasObjectRegion triggers a resize, so only call it if we're changing state.      
        if ((!bViewerHasObjectRegion && arrViews.length) ||
            (bViewerHasObjectRegion && !arrViews.length))
        {
            m_pViewer.SetHasObjectRegion(!bViewerHasObjectRegion);
        }

        var bViewAdded = false;
        for (var viewID in arrViews)
        {
            var pView = arrViews[viewID];

            // If we already have a tab for this view, skip.
            if (m_hshViews[pView.ViewID])
            {
                m_hshViews[pView.ViewID].AbsoluteEnd = pView.AbsoluteEnd;
                continue;
            }

            bViewAdded = true;

            // If stream is not being broadcast, skip in broadcast viewer.
            if (m_pViewer.isLive && !pView.IsBroadcast) continue;

            // Add view to hash.
            m_hshViews[pView.ViewID] = pView;

            m_pTabControl.AddView(pView);
        }

        // Disable streams that are not active current video/broadcast time
        this.UpdateViewsEnabledStatus(0);

        if (!m_pSelectedView || bViewAdded)
        {
            m_pTabControl.UpdateCurrentView();
        }
    }

    this.SelectView = function(viewID)
    {
        var pView = m_hshViews[viewID];

        if (pView)
        {
            m_pTabControl.SelectView(pView);
        }
    }
    
    // Enable and disable tabs based on the video time (playback) or stream open state (live).
    this.UpdateViewsEnabledStatus = function UpdateViewsEnabledStatus(fTime)
    {
        for (var viewID in m_hshViews)
        {
            var pView = m_hshViews[viewID];

            // Skip non-stream views.
            // BUGBUG: We should probably calculate the start/end of PPT evts and enable/disable view appropriately.
            if (pView.Type != "Object")
            {
                continue;
            }

            if (m_pViewer.isLive)
            {
                // If stream is finished, disable tab.
                if (pView.AbsoluteEnd)
                {
                    m_pTabControl.SetViewDisabled(pView, true);
                }
            }
            else
            {
                var fTargetObjPos;

                if (pView.Segments)
                {
                    var iTargetSeg = 0;
                    while ((iTargetSeg < pView.Segments.length) && (pView.Segments[iTargetSeg].RelativeStart <= fTime))
                    {
                        iTargetSeg++;
                    }
                    iTargetSeg--;

                    var pTargetSeg = pView.Segments[iTargetSeg];

                    fTargetObjPos = pTargetSeg.Offset + fTime - pTargetSeg.RelativeStart;
                }
                else
                {
                    // our target with respect to the archival video
                    fTargetObjPos = fTime - pView.RelativeStart;
                }

                var fStreamLength = pView.RelativeEnd - pView.RelativeStart;

                // Disable view if current video position is outside of stream bounds
                var bDisable = ((fTargetObjPos < 0) || (fTargetObjPos > fStreamLength));

                m_pTabControl.SetViewDisabled(pView, bDisable);
            }
        }
    }
    
    // process a status event
    this.UpdateStatus = function(pEvent, bUserInitiated)
    {
        m_pLastStatus = pEvent;

        // update the enabled status for all of our views
        this.UpdateViewsEnabledStatus(pEvent.Time);

        // send this status update to the current player
        if (m_pSelectedView)
        {
            m_arrPlayers[m_pSelectedView.Type].UpdateStatus(pEvent, bUserInitiated);
        }
    }
    
    this.OnResize = function OnResize(controlHeight, controlWidth)
    {
        // calculate the amount of height we have for content and resize our content div
        var contentTop = m_elContent.offsetTop;
        m_dContentHeightAvailable = controlHeight - contentTop - g_dTabViewerContentPadding * 2;
              
        // keep track of the width we have available - adjust width by content padding
        m_dContentWidthAvailable = controlWidth - (g_dTabViewerContentPadding * 2);
        
        // resize the content control
        ResizeContent();
        
        // Spacing required to vertically center the object material
        var contentPaddingTop = Math.floor((m_dContentHeightAvailable - m_dContentHeight) / 2);
        m_elContent.style.paddingTop = contentPaddingTop + "px";
        
        m_elContent.style.height = m_dContentHeightAvailable - contentPaddingTop + (g_dTabViewerContentPadding * 2) + "px";
        
        // resize our outer div
        m_el.style.height = m_dContentHeightAvailable + (g_dTabViewerContentPadding * 2) + contentTop + "px";
    }
    
    // figure out how large our content control should be and call its resize function
    function ResizeContent()
    {
        var aspect = null;
        var bHavePlayer = false;
        if( m_pSelectedView && m_arrPlayers[m_pSelectedView.Type] )
        {
            aspect = m_arrPlayers[m_pSelectedView.Type].AspectRatio;
            bHavePlayer = true;
        }
        else
        {
            aspect = g_dImageAspect;
        }
        
        // if our player isn't bound by aspect ratio use all available space
        if( !aspect && bHavePlayer )
        {
            m_dContentWidth = m_dContentWidthAvailable;
            m_dContentHeight = m_dContentHeightAvailable;
            m_arrPlayers[m_pSelectedView.Type].SetSize( m_dContentWidth, m_dContentHeight );
            return;
        }
        
        // determine the actual width and height we will use
        var desiredHeight = m_dContentWidthAvailable / aspect;
        
        // figure out whether to use full height and adjust width or the full width with adjusted hight
        if( desiredHeight > m_dContentHeightAvailable )
        {
            m_dContentWidth = Math.floor(m_dContentHeightAvailable * aspect);
            m_dContentHeight = m_dContentHeightAvailable;
        }
        else
        {
            m_dContentWidth = m_dContentWidthAvailable;
            m_dContentHeight = Math.floor(m_dContentWidthAvailable / aspect);
        }
        
        if( bHavePlayer )
        {
            m_arrPlayers[m_pSelectedView.Type].SetSize( m_dContentWidth, m_dContentHeight );
        }
    }
    
    this.GetContentWidth = function()
    {
        return m_dContentWidth;
    }        
    
    this.SetVisible = function(bVisible)
    {
        SetVisible(m_el, bVisible);
    }
}



// the image viewer control - displays "image" event targets such as PowerPoint slides
function ImageViewer(el)
{
    var m_el = el;

    // Hack IE6 to use AlphaImageLoader filter on DIV instead of IMG tag.
    var elementType = g_bIsIE6 ? "div" : "img";

    var m_pImage = CreateChildElement(m_el, elementType, "imageViewerImage");
    var m_pCurImageUrl = null;
    var m_dImageWidth;
    var m_dImageHeight;
    var m_pLastEvent = null;
    var m_arrTimestamps = null;

    // this is needed by TabViewer for layout
    this.AspectRatio = g_dImageAspect;

    this.Initialize = function(pStream)
    {
        m_arrTimestamps = pStream.Timestamps;
    }

    this.SetVisible = function(bVisible)
    {
        SetVisible(m_el, bVisible);
    }

    this.UpdateStatus = function(pEvent)
    {
        m_pLastEvent = pEvent;

        // if SetSize hasn't yet been called then return
        if (!m_dImageHeight || !m_dImageWidth)
            return;

        var iItem = GetCurrentItem(m_arrTimestamps, pEvent.Time);
        var pImage = m_arrTimestamps[iItem];

        var url = g_urlImage + "?id=" + pImage.EventTargetID + "&number=" + pImage.SequenceNumber + "&x=" + m_dImageWidth;
        if (url != m_pCurImageUrl)
        {
            m_pCurImageUrl = url;

            // Use AlphaImageLoader to get slightly better resizing in IE6
            if (g_bIsIE6)
            {
                m_pImage.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "', sizingMethod='scale')";
            }
            else
            {
                m_pImage.src = url;
            }
        }
    }

    this.SetSize = function(contentWidth, contentHeight)
    {
        m_dImageHeight = contentHeight;
        m_dImageWidth = Math.floor(contentWidth);

        m_el.style.width = contentWidth + "px";
        m_pImage.style.width = m_dImageWidth + "px";

        // Can probably run this for all browsers, but minimize impact of IE6 smooth-scaling hack.
        if (g_bIsIE6)
        {
            m_el.style.height = contentHeight + "px";
            m_pImage.style.height = m_dImageHeight + "px";
        }
    }
}


// the PDF viewer control - displays a static PDF alongside timeline content in the viewer
function PDFViewer(el)
{
    var m_el = el;

    var m_pIframe = CreateChildElement(m_el, "iframe", "imageViewerImage");

    this.NoThumbs = true;

    this.Initialize = function(pPDFEventTarget)
    {
        if (m_pIframe.src != pPDFEventTarget.URL)
        {
            // Detect Acrobat plugin, without redirecting on failure to find (DetectAcrobat.js)
            if (detectPDF(null, false) || g_bIsMac)
            {
                m_pIframe.src = pPDFEventTarget.URL;
            }
            else
            {
                var textDiv = CreateElement("DIV", null, "adobeReaderDownloadTextDiv");
                AppendText(textDiv, "Adobe Reader is required to view Acrobat documents in your browser.");
                CreateChildElement(textDiv, "BR");
                AppendText(textDiv, "To download Adobe Reader, click the image below.");
                CreateChildElement(textDiv, "BR");
                CreateChildElement(textDiv, "BR");
                AppendText(textDiv, "You may need to restart your browser after installing the Adobe Reader plugin.");

                SetChild(m_el, textDiv);

                var adobeReaderDownloadDiv = CreateChildElement(m_el, "DIV", null, "adobeReaderDownloadDiv");

                var adobeReaderDownloadImg = CreateChildElement(adobeReaderDownloadDiv, "IMG");
                adobeReaderDownloadImg.src = "GetAdobeReader.gif";
                adobeReaderDownloadImg.onclick = function() { window.open("http://www.adobe.com/products/acrobat/readstep2.html", "acrobatDownload"); };
            }
        }
    }

    this.SetVisible = function(bVisible)
    {
        if( bVisible )
        {
            el.style.display = "block";
            el.style.visibility = "visible";
        }
        else
        {
            el.style.visibility = "hidden";
        }
    }

    this.UpdateStatus = function(pEvent)
    {
    }

    this.SetSize = function(contentWidth, contentHeight)
    {
        var dWidth = Math.floor(contentWidth);
        var dHeight = Math.floor(contentHeight);

        m_el.style.width = dWidth + "px";
        m_el.style.height = dHeight + "px";
        m_pIframe.style.width = dWidth + "px";
        m_pIframe.style.height = dHeight + "px";
    }
}


// control for rendering tab bar and switching between views
function TabControl(el, selectViewCallback, useLargeTabs)
{
    var m_tabsEl = el;
    var m_selectViewCallback = selectViewCallback;
    
    // Hash of [StreamID] => [View]
    var m_hshViews = new Object();
    var m_pSelectedView = null;
    
    var m_bUseLargeTabs = useLargeTabs;

    this.AddView = function(pView)
    {
        // If we already have a tab for this view, skip.
        if (m_hshViews[pView.ViewID]) return;
        
        // Add view to hash.
        m_hshViews[pView.ViewID] = pView;
    
        var tabClass = GetTabClass(false, pView.Disabled);
        pView.Div = CreateChildElement(m_tabsEl, "div", tabClass);
        pView.Div.innerHTML = pView.Title;
        pView.Div.onselectstart = function() { return false; };

        function OnTabClick(viewID)
        {
            var pClickedView = m_hshViews[viewID];

            // if the view is already selected or disabled then return
            if ((pClickedView == m_pSelectedView) || pClickedView.Disabled)
            {
                return;
            }
            
            this.SelectView(m_hshViews[viewID]);
        }

        pView.Div.onclick = Clicker(pView.Div, Function.createDelegate(this, OnTabClick), pView.ViewID);
    }
    
    // called when a tab or event is clicked
    this.SelectView = function(pView)
    {
        // if we aren't changing anything then return
        if (pView == m_pSelectedView)
            return;

        // call callback if present 
        if (m_selectViewCallback)
            m_selectViewCallback(pView);

        // unselect the currently selected tab
        if (m_pSelectedView)
        {
            m_pSelectedView.Div.className = GetTabClass(false, m_pSelectedView.Disabled);
        }

        // update the new view and change its tab
        m_pSelectedView = pView;
        if (m_pSelectedView)
        {
            m_pSelectedView.Div.className = GetTabClass(true, false);
        }
    }
    
    // go through all the views and select the first that is enabled
    this.UpdateCurrentView = function()
    {
        // If no streams are found to select, SelectView(null) will display a blank page.
        var pSelectedView = null;

        for (var viewID in m_hshViews)
        {
            var pView = m_hshViews[viewID];

            if (!pView.Disabled)
            {
                // If we have no selected view, any enabled view trumps
                if (!pSelectedView)
                {
                    pSelectedView = pView;
                }
                // If the current view is not a stream, stream views trump
                else if(!pSelectedView.AbsoluteStart && pView.AbsoluteStart)
                {
                    pSelectedView = pView;
                }
                // If the current view is a stream, more recent streams trump
                else if((pSelectedView.AbsoluteStart && pView.AbsoluteStart) && (pView.AbsoluteStart > pSelectedView.AbsoluteStart))
                {
                    pSelectedView = pView;
                }
            }
        }

        this.SelectView(pSelectedView);
    }
    
    // call to enable/disable views
    this.SetViewDisabled = function SetViewDisabled(view, bDisabled)
    {
        if (bDisabled)
        {
            // if we are already disabled do nothing
            if (view.Disabled)
            {
                return;
            }

            if (view.Div)
            {
                view.Div.className = GetTabClass(false, true);
            }
            view.Disabled = true;

            // if we have disabled the selected view, then select the first non-disabled view
            if (view == m_pSelectedView)
            {
                this.UpdateCurrentView();
            }
        }
        else
        {
            // if we are disabled change the tab
            if (view.Disabled)
            {
                view.Div.className = GetTabClass(false, false);
                view.Disabled = false;

                // Auto-select newly-enabled tabs.
                this.SelectView(view);
            }
        }
    }
    
    function GetTabClass(isSelected, isDisabled)
    {
        var strTabClass = isSelected ? "tabDivSelected" : "tabDiv";
        strTabClass = isDisabled ? "tabDivDisabled" : strTabClass;

        strTabClass += " ";

        if(isSelected)
        {
            strTabClass += m_bUseLargeTabs ? "tabImageSelected_large" : "tabImageSelected_small";
        }
        else
        {
            strTabClass += m_bUseLargeTabs ? "tabImage_large" : "tabImage_small";
        }
        
        return strTabClass;
    }
}


