//****************************************************************************
// Copyright (c) 2006, Coveo Solutions Inc.
//****************************************************************************

function CES_Player() {

var m_LastPositions = new Array();
var m_LastTerm = 0;
var m_LastOccurence = 0;
var m_LastCurrentWord = null;

//****************************************************************************
// Handler for the onclick event of the utterances link.
//****************************************************************************
this.UtterancesLink_OnClick = function()
{
    this.ShowUtterances();
}

//****************************************************************************
// Handler for the onclick event of the transcript link.
//****************************************************************************
this.TranscriptLink_OnClick = function()
{
    this.ShowTranscript();
}

//****************************************************************************
// Handler for the onclick event of the timeline.
//****************************************************************************
this.TimeLine_OnClick = function(p_Event)
{
    var tlpos = CNL_GetPosition(this.m_TimeLine);
    var tlsize = CNL_GetSize(this.m_TimeLine);
    var percent = (p_Event.x - tlpos.m_Left) / tlsize.m_Width;
    this.Jump(this.m_Length * percent);
}

//****************************************************************************
// Handler for the onclick event on the play button.
//****************************************************************************
this.Play_OnClick = function(p_Event)
{
    this.m_Player.controls.play();
    this.Synchronize(false);
}

//****************************************************************************
// Handler for the onclick event on the pause button.
//****************************************************************************
this.Pause_OnClick = function(p_Event)
{
    this.m_Player.controls.pause();
    this.Synchronize(false);
}

//****************************************************************************
// Shows the utterances tab.
//****************************************************************************
this.ShowUtterances = function()
{
    this.m_UtterancesLink.style.fontWeight = 'bold';
    this.m_UtterancesLink.style.backgroundColor = 'silver';
    this.m_TranscriptLink.style.fontWeight = 'normal';
    this.m_TranscriptLink.style.backgroundColor = 'whitesmoke';
    this.m_Utterances.style.display = 'block';
    this.m_Transcript.style.display = 'none';
}

//****************************************************************************
// Shows the transcript tab.
//****************************************************************************
this.ShowTranscript = function()
{
    this.m_UtterancesLink.style.fontWeight = 'normal';
    this.m_UtterancesLink.style.backgroundColor = 'whitesmoke';
    this.m_TranscriptLink.style.fontWeight = 'bold';
    this.m_TranscriptLink.style.backgroundColor = 'silver';
    this.m_Utterances.style.display = 'none';
    this.m_Transcript.style.display = 'block';
    this.Synchronize(false);
}

//****************************************************************************
// Highlights the next occurence of a term.
// p_Term  - The index of the term to highlight.
//****************************************************************************
this.HighlightNextOccurence = function(p_Term)
{
    this.ShowTranscript();
    this.RemoveLastHighlight();
    
    var last = m_LastPositions[p_Term];
    if (last == undefined) {
        last = 0;
    }

    var current = last + 1;
    if (this.GetTermPart(p_Term, current, 1) == null) {
        current = 1;
    }

    this.HighlightTerm(p_Term, current);
    m_LastPositions[p_Term] = current;
}

//****************************************************************************
// Highlights the previous occurence of a term.
// p_Term  - The index of the term to highlight.
//****************************************************************************
this.HighlightPreviousOccurence = function(p_Term)
{
    this.ShowTranscript();
    this.RemoveLastHighlight();
    
    var last = m_LastPositions[p_Term];
    if (last == undefined) {
        last = 1;
    }

    var current = last - 1;
    if (this.GetTermPart(p_Term, current, 1) == null) {
        // Skip to the last one
        current = 1;
        while (this.GetTermPart(p_Term, current, 1) != null) {
            ++current;
        }
        --current;
    }

    this.HighlightTerm(p_Term, current);
    m_LastPositions[p_Term] = current;
}

//****************************************************************************
// Creates the media player within the page.
// p_Uri   - The uri to load within the media player.
// p_Start - The position of the first utterance.
//****************************************************************************
this.CreateMediaPlayer = function(p_Uri, p_Start)
{
    // Create the media player (visible or not)
    var elem = document.createElement('object');
    if (this.m_Video) {
        elem.style.width = this.m_Player.style.width;
        elem.style.height = this.m_Player.style.height;
        this.m_Player.replaceNode(elem);
    } else {
        this.m_Player.appendChild(elem);
        elem.style.display = 'none';
    }

    // Setup the player. I configure it to use windowless video so that we can
    // put stuff over the player. This make certains rare codecs unable to play,
    // but this ain't a problem since we're converting the clips to a known format.
    this.m_Player = elem;
    elem.classid = 'CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6';
    elem.stretchToFit = true;
    elem.windowlessVideo = true;
    elem.uiMode = 'none';
    elem.URL = p_Uri;
    this.Jump(this.GetHeadStart(p_Start));

    // Arm the timer that constantly synchronizes the position of the player
    this.Synchronize(true);
}

//****************************************************************************
// Adds an utterance to the various displays.
// p_Start   - The start time of the utterance.
// p_Percent - The position expressed as a percentage.
// p_Html    - The html for displaying info about the utterance.
//****************************************************************************
this.AddUtterance = function(p_Start, p_Percent, p_Html)
{
    // Create the entry in the utterances
    var utelem = document.createElement('div');
    utelem.className = 'CesUnselectedMenuItem';
    utelem.style.cursor = 'pointer';
    utelem.innerHTML = p_Html;
    this.m_Utterances.appendChild(utelem);

    // Create the bar in the timeline
    var tlpos = CNL_GetPosition(this.m_TimeLine);
    var tlsize = CNL_GetSize(this.m_TimeLine);
    var tlelem = document.createElement('img');
    tlelem.src = this.m_UtteranceUri;
    tlelem.style.position = 'absolute';
    tlelem.style.cursor = 'pointer';
    tlelem.style.zIndex = '800';
    this.m_TimeLine.appendChild(tlelem);
    CNL_SetPosition(tlelem, tlpos.m_Left + tlsize.m_Width * p_Percent - 6, tlpos.m_Top + 3);

    // Create the tooltip for the timeline bar
    var ttelem = document.createElement('div');
    ttelem.style.position = 'absolute';
    ttelem.style.width = '500px';
    ttelem.style.border = '1px solid black';
    ttelem.style.backgroundColor = 'whitesmoke';
    ttelem.style.padding = '5px';
    ttelem.style.zIndex = '999';
    ttelem.innerHTML = p_Html;
    this.m_TimeLine.appendChild(ttelem);
    CNL_PositionObject(ttelem, tlelem, 'AboveLeft');
    ttelem.style.visibility = 'hidden';

    // Setup event handlers
    var myself = this;
    utelem.onmouseover = function() { 
        utelem.className = 'CesSelectedMenuItem'; 
        tlelem.src = myself.m_UtteranceSelectedUri;
    };
    utelem.onmouseout = function() { 
        utelem.className = 'CesUnselectedMenuItem'; 
        tlelem.src = myself.m_UtteranceUri;
    };
    utelem.onclick = function() {
        myself.Jump(myself.GetHeadStart(p_Start));
    }
    tlelem.onmouseover = function() { 
        utelem.style.backgroundColor = 'lightgrey';
        tlelem.src = myself.m_UtteranceSelectedUri;
        ttelem.style.visibility = 'visible';
    };
    tlelem.onmouseout = function() { 
        utelem.style.backgroundColor = '';
        tlelem.src = myself.m_UtteranceUri;
        ttelem.style.visibility = 'hidden';
    };
}

//****************************************************************************
// Jumps to a given position within the clip.
// p_Position - The position to which to jump.
//****************************************************************************
this.Jump = function(p_Position)
{
    this.m_Player.controls.currentPosition = p_Position;
    this.m_Player.controls.play();
    //this.Synchronize(false);
}

//****************************************************************************
// Synchronizes the currently highlighted word with the position of the player.
// p_Auto - Whether the call comes from the timer.
//****************************************************************************
this.Synchronize = function(p_Auto)
{
    // If we get called while the page is unloading, we should not attempt to
    // touch the media player as it mail trigger exceptions.
    if (!this.m_Player.controls) {
        return;
    }

    // Try to detect when the code for the player was replaced with newer one
    // (happens when using partial postbacks). If we detect this, stop the player.
    var found = false;
    var up = this.parentElement;
    while (up != null) {
        if (up.tagName == 'BODY') {
            found = true;
            break;
        }
        up = up.parentElement;
    }
    if (!found) {
        this.m_Player.controls.stop();
        this.m_Utterances = null;
        this.m_Transcript = null;
        this.m_Player = null;
        this.m_TimeLine = null;
        this.m_Play = null;
        this.m_Pause = null;
        this.m_Slider = null;
        this.m_Filler = null;
        this.m_Status = null;
        return;
    }

    // Update the status of the buttons
    switch (this.m_Player.playState) {
    case 3:
        this.m_Play.style.display = 'none';
        this.m_Pause.style.display = 'inline';
        this.m_Status.innerHTML = '';
        break;
    case 6:
        this.m_Play.style.display = 'none';
        this.m_Pause.style.display = 'inline';
        this.m_Status.innerHTML = this.m_Player.status;
        break;
    default:
        this.m_Play.style.display = 'inline';
        this.m_Pause.style.display = 'none';
        this.m_Status.innerHTML = '';
        break;
    }

    // Update the position of the slider
    var slsize = CNL_GetSize(this.m_Slider);
    var position = this.m_Player.controls.currentPosition;
    var spos = this.GetTimeLinePosition(position / this.m_Length);
    CNL_SetPosition(this.m_Slider, spos.m_Left - slsize.m_Width / 2, spos.m_Top - 8);

    // Update the filler
    var tlpos = CNL_GetPosition(this.m_TimeLine);
    var tlsize = CNL_GetSize(this.m_TimeLine);
    CNL_SetPosition(this.m_Filler, tlpos.m_Left - 2, tlpos.m_Top + 3);
    this.m_Filler.style.width = (spos.m_Left - tlpos.m_Left + 2) + 'px';

    var delay;
    if (this.m_Transcript) {
        // Unhighlight the last word
        if (m_LastCurrentWord != null) {
            m_LastCurrentWord.style.color = m_LastCurrentWord.m_BackupColor;
            m_LastCurrentWord.style.backgroundColor = m_LastCurrentWord.m_BackupBackground;
        }

        // Find the index of the current word (eg. the one before the one that is
        // starting at the nearest time after the current position).
        var beg = 0;
        var end = this.m_Positions.length;
        var index = 0;
        while (beg < end) {
            index = beg + Math.floor((end - beg) / 2);
            var value = this.m_Positions[index];
            if (value < position) {
                beg = index + 1;
            } else {
                end = index;
            }
        }
        index = Math.max(beg - 1, 0);

        // If a word was found highlight it
        m_LastCurrentWord = G(this.id + '_' + this.m_Positions[index]);
        if (m_LastCurrentWord != null) {
            m_LastCurrentWord.m_BackupColor = m_LastCurrentWord.style.color;
            m_LastCurrentWord.m_BackupBackground = m_LastCurrentWord.style.backgroundColor;
            m_LastCurrentWord.style.color = 'white';
            m_LastCurrentWord.style.backgroundColor = 'maroon';

            // When doing an explicit (eg. non-auto) synchronize, we want
            // to scroll the transcript so that the current word is displayed.
            if (!p_Auto) {
                m_LastCurrentWord.scrollIntoView();
            }
        }

        // Compute the delay till the next word
        if (this.m_Positions[index] > position) {
            delay = (this.m_Positions[index] - position) * 1000;
        } else if (index + 1 < this.m_Positions.length) { 
            delay = (this.m_Positions[index + 1] - position) * 1000;
        } else {
            delay = 1000;
        }
        if (delay > 500) {
            delay = 500;
        }
    } else { 
        delay = 500;
    }

    // Rearm the timer if needed
    if (p_Auto) {
        var myself = this;
        setTimeout(function() { myself.Synchronize(true); }, delay);
    }
}

//****************************************************************************
// Retrieves the position at which to jump for an utterance, including a head start.
// p_Start - The position at which the utterance starts.
// Returns the position with the headstart.
//****************************************************************************
this.GetHeadStart = function(p_Start)
{
    var pos = p_Start - this.m_HeadStart;
    if (pos < 0) {
        pos = 0;
    }

    return pos;
}

//****************************************************************************
// Retrieves the position of a point on the timeline.
// p_Percent - The percentage of the point on the timeline.
// Returns an object holding the position.
//****************************************************************************
this.GetTimeLinePosition = function(p_Percent)
{
    var tlpos = CNL_GetPosition(this.m_TimeLine);
    var tlsize = CNL_GetSize(this.m_TimeLine);
    tlpos.m_Left += tlsize.m_Width * p_Percent - 2;

    return tlpos;
}

//****************************************************************************
// Retrieves a specific highlight part.
// p_Term      - The index of the term.
// p_Occurence - The index of the occurence of the term.
// p_Part      - The index of the part of the term.
//****************************************************************************
this.GetTermPart = function(p_Term, p_Occurence, p_Part)
{
    return document.getElementById('CoveoHighlight:' + p_Term + '.' + p_Occurence + '.' + p_Part);
}

//****************************************************************************
// Highlights a term and scrolls it into view.
// p_Term      - The index of the term to highlight.
// p_Occurence - The index of the term occurence to highlight.
//****************************************************************************
this.HighlightTerm = function(p_Term, p_Occurence)
{
    var part = 1;
    var elem = this.GetTermPart(p_Term, p_Occurence, part);
    if (elem != null) {
        // Jump to the position of this word
        var time = parseFloat(elem.parentNode.id.substring(this.id.length + 1, elem.parentNode.id.length));
        this.Jump(time);
        elem.scrollIntoView();
        while (elem != null) {
            elem.m_BackupColor = elem.style.color;
            elem.m_BackupBackground = elem.style.backgroundColor;
            elem.style.color = 'white';
            elem.style.backgroundColor = 'green';
            elem = this.GetTermPart(p_Term, p_Occurence, ++part);
        }

        m_LastTerm = p_Term;
        m_LastOccurence = p_Occurence;

    }
}

//****************************************************************************
// Unhighlights the last highlighted term.
//****************************************************************************
this.RemoveLastHighlight = function()
{
    if (m_LastTerm != 0) {
        var part = 1;
        var elem = this.GetTermPart(m_LastTerm, m_LastOccurence, part);
        while (elem != null) {
            elem.style.color = elem.m_BackupColor;
            elem.style.backgroundColor = elem.m_BackupBackground;
            elem = this.GetTermPart(m_LastTerm, m_LastOccurence, ++part);
        }
    }
}

} // Constructor

