
var g_dVideoAspect = (640/480);

// silverlight video player
function SilverlightVideoPlayer(el, strId, viewer)
{
    var m_pEl = el;

    var m_pViewer = viewer;

    var m_pVideo = null;

    var m_pBufferingArea = null;
    var m_pBufferingText = null;
    var m_pBufferingAnimation = null;
    var m_bBufferingIndicatorVisible = false;
    
    var m_pFullScreenMessage = null;
    var m_pFullScreenMessageFade = null;
    var m_pFullScreenButtons = null;
    var m_pFullScreenNativeButton = null;
    var m_pFullScreenStretchButton = null;
    var m_pFullScreenExitButton = null;
    
    var m_pInfo = null;
    
    var m_dVideoWidth = null;
    var m_dVideoHeight = null;

    var m_bFirstLoad = true;

    this.Initialize = function(info)
    {
        m_pInfo = info;

        // JS prints "null" for null ref
        if(!m_pInfo.xamlRoot) m_pInfo.xamlRoot = "";
        
        // initialize our silverlight instance with the load callback
        Silverlight.createObjectEx({
            source: m_pInfo.xamlRoot + 'VideoPlayer.xaml?ver=' + m_pViewer.version,
            parentElement: m_pEl,
            id: strId,
            properties: {
                width: '100%',
                height: '240px',
                background: 'black',
                isWindowless: 'false',
                version: '1.0'
            },
            events: {
                onError: null,
                onLoad: Function.createDelegate(this, Plugin_OnLoad),
                onFullScreenChanged: Function.createDelegate(this, Plugin_OnFullScreenChanged)
            },
            context: null
        });
    }

    function Plugin_OnLoad(control, userContext, rootElement)
    {
        // Get pointers to XAML elements
        m_pVideo = rootElement.findName("mediaElement");
        m_pBufferingArea = rootElement.findName("BufferingArea");
        m_pBufferingText = rootElement.findName("BufferingText");
        m_pBufferingAnimation = rootElement.findName("BufferingArea_Buffering");
        m_pFullScreenMessage = rootElement.findName("fullScreenMessage");
        m_pFullScreenMessageFade = rootElement.findName("fullScreenMessageFade");
        m_pFullScreenButtons = rootElement.findName("fullScreenButtons");
        m_pFullScreenNativeButton = rootElement.findName("fullScreenNativeButton");
        m_pFullScreenStretchButton = rootElement.findName("fullScreenStretchButton");
        m_pFullScreenExitButton = rootElement.findName("fullScreenExitButton");

        // Wire up media event handlers
        m_pVideo.addEventListener("mediaOpened", Function.createDelegate(this, MediaElement_MediaOpened));
        m_pVideo.addEventListener("mediaEnded", Function.createDelegate(this, MediaElement_MediaEnded));
        m_pVideo.addEventListener("mediaFailed", Function.createDelegate(this, MediaElement_MediaFailed));
        m_pVideo.addEventListener("bufferingProgressChanged", Function.createDelegate(this, MediaElement_BufferingProgressChanged));
        m_pVideo.addEventListener("currentStateChanged", Function.createDelegate(this, MediaElement_StateChanged));

        // Wire up mouse event handlers
        m_pVideo.addEventListener("mouseLeftButtonDown", Function.createDelegate(this, MediaElement_MouseLeftButtonDown));
        m_pFullScreenNativeButton.addEventListener("mouseLeftButtonUp", Function.createDelegate(this, FullScreenNative));
        m_pFullScreenStretchButton.addEventListener("mouseLeftButtonUp", Function.createDelegate(this, FullScreenStretch));
        m_pFullScreenExitButton.addEventListener("mouseLeftButtonUp", Function.createDelegate(this, ExitFullScreen));

        DisplayBuffering(true, 0);

        // Set the media element source.
        m_pVideo.Source = m_pInfo.Url;
        
        m_pVideo.BufferingTime = CreateTimeSpan(5);
        
        if (m_dVideoWidth && m_dVideoHeight)
        {
            this.SetSize(m_dVideoWidth, m_dVideoHeight);
        }

        if (m_bFirstLoad)
        {
            m_pFullScreenMessage.Visibility = "Visible";
            m_pFullScreenMessageFade.Begin();
            
            m_bFirstLoad = false;
        }
    }

    function IsLoadingState(playState)
    {
        switch (playState)
        {
            case "Playing":
            case "Paused":
            case "Stopped":
                {
                    return false;
                }
            default:
                {
                    return true;
                }
        }
    }

    function MediaElement_BufferingProgressChanged()
    {
        var progress = m_pVideo.bufferingProgress;
        var percent = Math.round(progress * 100);
        if ((percent >= 100) || !IsLoadingState(this.GetPlayState()))
        {
            DisplayBuffering(false);
        }
        else
        {
            DisplayBuffering(true, percent);
        }
    }

    function DisplayBuffering(enabled, percent)
    {
        if (enabled)
        {
            if (!m_bBufferingIndicatorVisible)
            {
                m_pBufferingText.Visibility = "Visible";
                m_pBufferingArea.Visibility = "Visible";
                m_bBufferingIndicatorVisible = true;

                m_pBufferingAnimation.Begin();
           }

            m_pBufferingText.Text = percent.toString();
        }
        else if (m_bBufferingIndicatorVisible)
        {
            m_pBufferingText.Visibility = "Collapsed";
            m_pBufferingArea.Visibility = "Collapsed";
            m_bBufferingIndicatorVisible = false;

            m_pBufferingAnimation.Stop();
        }
    }

    function MediaElement_StateChanged()
    {
        var playState = this.GetPlayState();
        if (IsLoadingState(playState))
        {
            DisplayBuffering(true, 0);
        }
        else
        {
            DisplayBuffering(false);
        }

        if (m_pInfo.stateChangedCallback)
        {
            m_pInfo.stateChangedCallback(playState);
        }
    }

    // Layout full screen buttons and if returning from full screen, ensure media element fills plugin client area.
    function Plugin_OnFullScreenChanged(sender)
    {
        if (!m_pVideo) return;
        
        var isFullScreen = sender.GetHost().content.fullScreen;

        // Entering FS mode
        if (isFullScreen)
        {
            m_pFullScreenMessage.Visibility = "Collapsed";
            m_pFullScreenButtons.Visibility = "Visible";
        }
        // Returning from FS mode
        else
        {
            m_pFullScreenButtons.Visibility = "Collapsed";
            
            try
            {
                m_pVideo["Margin"] = "0";

                m_pVideo.Width = m_dVideoWidth;
                m_pVideo.Height = m_dVideoHeight;
            }
            catch (error)
            {
            }
        }
    }

    // This is needed by TabViewer for layout.
    // Default to constant in case we fail to retrieve aspect from video.
    this.AspectRatio = g_dVideoAspect;

    function MediaElement_MediaOpened(sender)
    {
        this.AspectRatio = m_pVideo.NaturalVideoWidth / m_pVideo.NaturalVideoHeight;
        // BUGBUG: We should refactor tabviewer / videoplayers such that videoplayer handles vertical centering.
        // Then we can just resize the videoplayer.
        g_pViewer.Resize();
    }

    function MediaElement_MediaEnded(sender)
    {
        // If we have a callback, call it
        if (m_pInfo.mediaEndedCallback)
        {
            m_pInfo.mediaEndedCallback();
        }
    }

    // If we fail to connect to the publishing point, try again every 2 seconds.
    function MediaElement_MediaFailed(sender)
    {
        // If we have a callback, call it
        if (m_pInfo.mediaFailedCallback)
        {
            m_pInfo.mediaFailedCallback();
        }
        // Otherwise just retry the current URL after a delay.
        else
        {
            setTimeout(function()
            {
                m_pVideo.Source = m_pInfo.Url;
            },
            2000);
        }
    }

    // Makeshift double-click handler
    var bMediaElementClick = false;
    function MediaElement_MouseLeftButtonDown(sender)
    {
        if (bMediaElementClick)
        {
            var content = sender.GetHost().content;
            if (content.fullScreen)
            {
                ExitFullScreen.call(this, sender);
            }
            else
            {
                FullScreenStretch.call(this, sender);
            }
        }
        else
        {
            bMediaElementClick = true;
            setTimeout(function() { bMediaElementClick = false; }, 500);
        }
    }

    function FullScreenNative(sender) { FullScreen.call(this, "nativePixels", sender); }
    function FullScreenStretch(sender) { FullScreen.call(this, "stretch", sender); }
    function FullScreen(mode, sender)
    {
        var content = sender.GetHost().content;
        
        content.fullScreen = true;

        var screenWidth = content.actualWidth;
        var screenHeight = content.actualHeight;

        if (mode == "nativePixels")
        {
            var videoWidth = Math.min(m_pVideo.NaturalVideoWidth, screenWidth);
            var videoHeight = videoWidth / this.AspectRatio;

            if (videoHeight > screenHeight)
            {
                videoHeight = screenHeight;
                videoWidth = videoHeight * this.AspectRatio;
            }

            m_pVideo["Margin"] = ((screenWidth - videoWidth) / 2) + "," + ((screenHeight - videoHeight) / 2);

            m_pVideo.Width = videoWidth;
            m_pVideo.Height = videoHeight;
        }
        else
        {
            m_pVideo["Margin"] = "0";

            m_pVideo.Width = screenWidth;
            m_pVideo.Height = screenHeight;
        }
    }

    function ExitFullScreen(sender)
    {
        var content = sender.GetHost().content;

        content.fullScreen = false;
    }

    this.SetVisible = function( bVisible )
    {
        SetVisible( m_pEl, bVisible );
    }

    this.SetSource = function(url)
    {
        m_pVideo.Source = url;
    }
    
    this.SetVideoPosition = function( fTime )
    {
        if( fTime < m_pVideo.NaturalDuration.Seconds )
        {
            m_pVideo.Position = CreateTimeSpan(fTime);
        }
    }
    
    this.GetVideoPosition = function()
    {
        if( !m_pVideo )
            return 0;
        else
            return m_pVideo.Position.Seconds;
    }
    
    this.GetPlayState = function()
    {
        var state = null;
        
        if(m_pVideo)
        {
            try
            {
                state = m_pVideo.CurrentState;
            }
            catch(error)
            {
            }
        }
        
        return state;
    }

    this.SetPlayState = function(playState)
    {
        if (m_pVideo && (this.GetPlayState() != playState))
        {
            try
            {
                if (playState == "Playing")
                {
                    m_pVideo.Play();
                }
                else if (playState == "Paused")
                {
                    m_pVideo.Pause();
                }
            }
            catch (error)
            {
            }
        }
    }

    this.UpdateStatus = function(pEvent, bUserInitiated)
    {
        if( !m_pVideo )
            return;
      
        // synchronize positions - if we are more than the threshold away from the
        // archival video then synchronize
        var fTargetObjPos;
        if( m_pInfo.Segments )
        {
            var iTargetSeg = 0;
            while( iTargetSeg < m_pInfo.Segments.length && m_pInfo.Segments[iTargetSeg].RelativeStart <= pEvent.Time )
            {
                iTargetSeg++;
            }
            iTargetSeg--;
            var pTargetSeg = m_pInfo.Segments[iTargetSeg];
            fTargetObjPos = Math.max( pTargetSeg.Offset + pEvent.Time - pTargetSeg.RelativeStart, 0 ); 
        }
        else
        {
            // our target with respect to the archival video
            fTargetObjPos = Math.max( pEvent.Time - m_pInfo.RelativeStart, 0 );    
        }
        
        try
        {
            var fCurPos = m_pVideo.Position.Seconds;
            if( bUserInitiated || (Math.abs(fCurPos - fTargetObjPos) > g_fOffsetThreshold) )
            {
                this.SetVideoPosition( fTargetObjPos );
            }
            var curPlayState = this.GetPlayState();
            if(pEvent.PlayState != curPlayState)
            {
                this.SetPlayState(pEvent.PlayState);
            }
        }
        catch(error)
        {
        }
    }
    
    this.SetSize = function(width, height)
    {
        m_dVideoWidth = width;
        m_dVideoHeight = height;
        
        m_pEl.style.width = width + "px";
        m_pEl.style.height = height + "px";
        
        var slPlugin = document.getElementById(strId);
        if( slPlugin )
        {
            slPlugin.style.height = height + "px";
            slPlugin.style.width = width + "px";
        }
        
        if( m_pVideo )
        {
            try
            {
                m_pVideo.Width = m_dVideoWidth;
                m_pVideo.Height = m_dVideoHeight;
            }
            catch(error)
            {
            }   
        }
    }
}


// helper functions
function CreateTimeSpan(fSeconds)
{
    // hooray, javascript
    function pad(int)
    {
        return int = (int < 10) ? '0' + int : int;
    }

    // surely there is some built-in way to do this
    var iHours = Math.floor(fSeconds / 3600);
    var iMinutes = Math.floor((fSeconds % 3600) / 60);
    var seconds = fSeconds % 60;
    seconds = seconds.toFixed(2);
    return pad(iHours) + ":" + pad(iMinutes) + ":" + pad(seconds);
}
