/*
 * (c)2010 Broadcom Corporation
 * Copyright (C) 2007, 2009 Apple Inc.  All rights reserved.
 * Copyright (C) 2007 Collabora Ltd.  All rights reserved.
 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * aint with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#if ENABLE(VIDEO)
#ifdef MANGAREVA_VIDEO

#include "bdlna_types.h"
#include <assert.h>

#include "BCMediaPlayerPrivateMangareva.h"
#include "MediaPlayerPrivateVariables.h"

#include "CString.h"
#include "Font.h"
#include "FontDescription.h"
#include "FontSelector.h"
#include "GraphicsContext.h"
#include "IntRect.h"
#include "KURL.h"
#include "MIMETypeRegistry.h"
#include "MediaPlayer.h"
#include "NotImplemented.h"
#include "ScrollView.h"
#include "TimeRanges.h"
#include "Widget.h"
#include "Timer.h"
#include <directfb.h>
#include "FrameView.h"
#include <unistd.h>
#include <wtf/MainThread.h>
#include <string>
#include <limits>
#include <math.h>
#include "dfb_platform.h"

#include "CookieJar.h"
#include "Cookie.h"
 
#include "Chrome.h"
#include "ChromeClient.h"
#include "ResourceHandleManager.h"

#include "StringBuilder.h"

#ifdef __cplusplus
extern "C" {
#endif

int BMedia_GetHostId(BMediaHandle hMedia);

#define DECODER_IDX_TAG "?decoderIndex="
#define MOSAIC_IDX_TAG "?mosaic="
#define SIZE_TAG "?contentLength="
#define DURATION_TAG "?duration="
#define DTCP_HOST "?DTCP1HOST="
#define DTCP_PORT "?DTCP1PORT="
#define PRIMER_IDX_TAG "?primerIndex="

#ifdef __cplusplus
}
#endif

/* #define DEBUGSTD  */

#ifdef DEBUGSTD
#define DEBUGLOG(...) \
(fprintf(stderr,"(%d)", __LINE__), fprintf(stderr,__VA_ARGS__))
//(fprintf(stderr,"%s(%d)", __FILE__, __LINE__), fprintf(stderr,__VA_ARGS__))
#else
#ifdef DEBUG
#define DEBUGLOG(...) \
( printf(__VA_ARGS__))
#else
#define DEBUGLOG(...) void(0);
#endif
#endif

using namespace std;
namespace WebCore {

int id =1;

MediaPlayerPrivateVariables::MediaPlayerPrivateVariables()
 : m_id(id++)
 , m_isEndReached(false)
 , m_viewRect(IntRect())
 , m_gainedFocus(false)
 , m_initialSeekTime(0)
 , s_streamIndex(-1)
 , m_handle(NULL)
 , m_hostID(-1)
{

}

void* MediaPlayerPrivateVariables::s_handle = 0;
uint8_t MediaPlayerPrivateVariables::s_handleCount = 0;
MediaPlayerPrivateBC* MediaPlayerPrivateVariables::s_currentPlayer = NULL;
bool MediaPlayerPrivateVariables::s_playerInUse = false;
int MediaPlayerPrivateVariables::s_hostID = -1;

void showGfx(BMediaHandle handle)
{
//JCG printf("JCG: showGfx\n");
    BMedia_GraphicsSettings graphicsSettings;

    BMedia_GetGraphicsSettings(handle, &graphicsSettings);
    graphicsSettings.alpha = 0xff;
    graphicsSettings.zorder = 2;
    graphicsSettings.enabled = true;
    BMedia_SetGraphicsSettings(handle, &graphicsSettings);

}

void mediaPlayerPrivateEOSCallback(void* context, int param)
{
//JCG printf("JCG: mediaPlayerPrivateEOSCallback\n");
    MediaPlayerPrivateBC* mediaPlayerPrivate = static_cast<MediaPlayerPrivateBC*> (context);
    DEBUGLOG("! mediaPlayerPrivateEOSCallback %p %p\n", context, mediaPlayerPrivate ); //->m_privateVariables->m_id);
    WTF::callOnMainThread(MediaPlayerPrivateBC::notifyTimeChanged, mediaPlayerPrivate);
}

void mediaPlayerPrivateUriChangedCallback(void* context, int param)
{
//JCG printf("JCG: mediaPlayerPrivateUriChangedCallback\n");
}

void mediaPlayerPrivateErrorCallback(void* context, int param)
{
//JCG printf("JCG: mediaPlayerPrivateErrorCallback\n");
    MediaPlayerPrivate* mediaPlayerPrivate = static_cast<MediaPlayerPrivate*> (context);
    if(mediaPlayerPrivate)
        WTF::callOnMainThread(MediaPlayerPrivateBC::notifyError, mediaPlayerPrivate);
    else
        printf("BUG: mediaPlayerPrivateErrorCallback\n");
}


MediaPlayerPrivateBC* MediaPlayerPrivateBC::getCurrentMediaPlayerPrivate()
{
    return(MediaPlayerPrivateVariables::s_currentPlayer);
}

bool MediaPlayerPrivateBC::mediaPlayerPrivateResourceRequest(MediaPlayerPrivateBC* mediaPlayerPrivate)
{
//JCG printf("JCG: MediaPlayerPrivateBC::mediaPlayerPrivateResourceRequest\n");
    MediaPlayerPrivateVariables::s_currentPlayer = mediaPlayerPrivate;
    if (mediaPlayerPrivate->m_privateVariables->s_streamIndex != -1)
    {
        /* We never want to create more than one instance of video stream */
        return false;
    }
    else
    {
        return true;
    }

}

void MediaPlayerPrivateBC::stateTimerCallback(Timer<MediaPlayerPrivateBC>*)
{
//JCG printf("JCG: MediaPlayerPrivateBC::stateTimerCallback\n");
    DEBUGLOG("Notifying ready state: %d\n", m_readyState);
    m_readyState = MediaPlayer::HaveEnoughData;
    m_player->readyStateChanged();
}

MediaPlayerPrivateInterface* MediaPlayerPrivateBC::create(MediaPlayer* player)
{
//JCG printf("JCG: MediaPlayerPrivateBC::create\n");
    return new MediaPlayerPrivateBC(player);
}

void MediaPlayerPrivateBC::registerMediaEngine(MediaEngineRegistrar registrar)
{
//JCG printf("JCG: MediaPlayerPrivateBC::registerMediaEngine\n");
    if (isAvailable())
        registrar(create, getSupportedTypes, supportsType);
}

MediaPlayerPrivateBC::MediaPlayerPrivateBC(MediaPlayer* player)
 : m_player(player)
 , m_networkState(MediaPlayer::Empty)
 , m_readyState(MediaPlayer::HaveNothing)
 , m_rate(1.0f)
 , m_endTime(numeric_limits<float>::infinity())
 , m_volume(0.5f)
 , m_startedPlaying(false)
 , m_isStreaming(false)
 , m_size(IntSize())
 , m_visible(true)
{
//JCG printf("JCG: MediaPlayerPrivateBC CTOR\n");
    m_privateVariables = new MediaPlayerPrivateVariables();
    DEBUGLOG("---MediaPlayerPrivate--- %d %p\n",(MediaPlayerPrivateVariables::s_handleCount), this);
    BMedia_OpenSettings mediaSettings;
    DFB_PlatformSettings platformSettings;

    /* Get DirectFB Platform settings */
    DFB_Platform_GetSettings(&platformSettings);
    memset(&mediaSettings, 0, sizeof(BMedia_OpenSettings));
    mediaSettings.displayHandle = platformSettings.display[0].handle;
    mediaSettings.supportRvu = false;
    mediaSettings.audioMixer = NULL;

    DEBUGLOG("CREATING NEW BMEDIA");
    (m_privateVariables->m_handle) = (void*)BMedia_Open(&mediaSettings);
    (m_privateVariables->m_hostID) = BMedia_GetHostId((BMediaHandle)(m_privateVariables->m_handle));
    MediaPlayerPrivateVariables::s_handleCount++;
}

MediaPlayerPrivateBC::~MediaPlayerPrivateBC()
{
//JCG printf("JCG: MediaPlayerPrivateBC DTOR\n");
    DEBUGLOG("---~MediaPlayerPrivate--- %d %p\n",(MediaPlayerPrivateVariables::s_handleCount), this);
    if (m_privateVariables->m_isPrime)
    {
        m_startedPlaying =false;
        return;
    }
    else if (m_privateVariables->s_streamIndex >= 0)
    {	
        BMedia_Stop((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));

        /* Need to close here so the memory is freed */
        BMedia_CloseVideoStream((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));
    }
    m_privateVariables->s_streamIndex = -1;
    m_startedPlaying =false;
    delete m_privateVariables;
}

#if ENABLE(DAE)
__attribute__((visibility("default"))) void MediaPlayerPrivateBC::load(PassRefPtr<Channel> channel)
{
    LOG_VERBOSE(Media, "Loading Channel %p", channel.get());
}
#endif

void MediaPlayerPrivateBC::load(const KURL& url)
{
//JCG printf("JCG: MediaPlayerPrivateBC::load(%s)\n",url.string().utf8().data());
    DEBUGLOG("Load %s \n", url.string().utf8().data());

    if (m_networkState != MediaPlayer::Loading)
    {
        m_networkState = MediaPlayer::Loading;
        m_player->networkStateChanged();
    }
    if (m_readyState != MediaPlayer::HaveNothing)
    {
        m_readyState = MediaPlayer::HaveNothing;
        m_player->readyStateChanged();
    }
    m_privateVariables->m_url = url;

    m_privateVariables->m_urlChanged = true;
    m_startedPlaying = false;
    m_privateVariables->m_isEndReached = false;
    m_readyState = MediaPlayer::HaveEnoughData;
    m_player->readyStateChanged();

    m_networkState = MediaPlayer::Loaded;
    m_player->networkStateChanged();
}

void MediaPlayerPrivateBC::play()
{
//JCG printf("JCG: MediaPlayerPrivateBC::play\n");
    int index;
    BMedia_Error playPrimerStatus;
    BMedia_Error success;
    char *decoderIndex, *uri_header, *mosaicIndex, *primerIndex;
    char *token;
    const int URI_CAPACITY = 1024;
    char uri[URI_CAPACITY];
    int primerPlayIndex = -1;
    BMedia_VideoStreamSettings videoStreamSettings;
    BMedia_VideoStreamOpenSettings videoStreamOpenSettings;
    BDlna_MediaStatus status;
    BMedia_StreamMetadata metaData;
    char dtcpHost[256];
    int dtcpPort = 0;

    DEBUGLOG("---Play---\n");
    success = BMEDIA_NOT_INITIALIZED;

    // Verify that our 'uri' buffer has enough space to store the url
    int urlLength = strlen(m_privateVariables->m_url.string().utf8().data());
    if(URI_CAPACITY <= urlLength) {
        printf("Error: Unsupported url length in MediaPlayerPrivateBC::play(); Supported %d <= Url length: %d\n", URI_CAPACITY, urlLength);
        return;
    }

    if (mediaPlayerPrivateResourceRequest(this))
    {
        // This is so paint knows to update the videoWindow settings
        char* cookieHeaderString;
        String cookieHeader(ResourceHandleManager::sharedInstance().cookies(0, m_privateVariables->m_url, true));
        m_privateVariables->m_gainedFocus = true;

        m_player->repaint();
		
        DEBUGLOG("!!!! Create Video Stream !!!! initialSeekTime = %d\n", m_privateVariables->m_initialSeekTime);
        memset(&videoStreamOpenSettings, 0, sizeof(videoStreamOpenSettings));
        videoStreamOpenSettings.endOfStreamCallback.callback = mediaPlayerPrivateEOSCallback;
        videoStreamOpenSettings.endOfStreamCallback.context = this;
        videoStreamOpenSettings.uriChangedCallback.callback = mediaPlayerPrivateUriChangedCallback;
        videoStreamOpenSettings.uriChangedCallback.context = this;
        videoStreamOpenSettings.errorCallback.callback = mediaPlayerPrivateErrorCallback;
        videoStreamOpenSettings.errorCallback.context = this;
		
        /* Check if it is fast channel change */
        strcpy(uri, m_privateVariables->m_url.string().utf8().data());
        primerIndex = strstr(uri, PRIMER_IDX_TAG);
        if (primerIndex != NULL)
        {
            uri[primerIndex - uri] = '\0';
            primerIndex += strlen(PRIMER_IDX_TAG);
            primerPlayIndex = atol(primerIndex);
			
            assert(primerPlayIndex > 0);
            playPrimerStatus = BMedia_PlayPrimeChannel((BMediaHandle)(m_privateVariables->m_handle), primerPlayIndex);
            if (playPrimerStatus)
            {   
                DEBUGLOG("playPrimerChannel failed %d", primerPlayIndex);
                BMedia_UnprimeChannel((BMediaHandle)(m_privateVariables->m_handle), primerPlayIndex);
                BMedia_PrimeChannel((BMediaHandle)(m_privateVariables->m_handle), primerPlayIndex);
                playPrimerStatus = BMedia_PlayPrimeChannel((BMediaHandle)(m_privateVariables->m_handle), primerPlayIndex);
                if(playPrimerStatus)
                    DEBUGLOG("playPrimerChannel failed for a second time %d", primerPlayIndex);
            }
            m_privateVariables->m_isPrime = true;
            m_privateVariables->s_primeIndex = primerPlayIndex;
			
            m_startedPlaying = true;
            m_player->repaint();
            memset(&status, 0, sizeof(BDlna_MediaStatus));
            BMedia_GetStatus((BMediaHandle)(m_privateVariables->m_handle), primerPlayIndex, &status);
            if (status.state >= BDlna_MediaState_ePlaying)
            {
                m_endTime = status.last / 1000.0f;
                m_player->durationChanged();
            }
            m_privateVariables->m_isEndReached = false;
            m_player->timeChanged();
            return;
        }
        m_privateVariables->m_isPrime = false;

        /* Check if it is mosaics */
        strcpy(uri, m_privateVariables->m_url.string().utf8().data());
        mosaicIndex = strstr(uri, MOSAIC_IDX_TAG);
        if (mosaicIndex != NULL)
        {
            uri[mosaicIndex - uri] = '\0';
            mosaicIndex += strlen(MOSAIC_IDX_TAG);
            videoStreamOpenSettings.mosaic = atol(mosaicIndex);
        }
        /* Check which decoder index to use */
        decoderIndex = strstr(uri, DECODER_IDX_TAG);
        if (decoderIndex != NULL)
        {
            uri[decoderIndex - uri] = '\0';
            decoderIndex += strlen(DECODER_IDX_TAG);
            videoStreamOpenSettings.decoderIndex = 1;
        }
        else
        {
            videoStreamOpenSettings.decoderIndex = 0;
        }
        memset(&metaData, 0, sizeof(metaData));

        if (!cookieHeader.isNull() && !cookieHeader.isEmpty())
        {
            cookieHeaderString = (char*)malloc(cookieHeader.utf8().length() + 11);
            sprintf(cookieHeaderString, "Cookie: %s\r\n",cookieHeader.utf8().data());
            cookieHeaderString[cookieHeader.utf8().length() + 10] = '\0';
            metaData.additionalHeaders = cookieHeaderString;
        }
        /* Extract DTCP info */
        token = strstr(uri, DTCP_PORT);
        if (NULL != token)
        {
            uri[token - uri] = '\0';
            token += strlen(DTCP_PORT);
            dtcpPort = atoi(token);
        }
        token = strstr(uri, DTCP_HOST);
        if (NULL != token)
        {
            uri[token - uri] = '\0';
            token += strlen(DTCP_HOST);
            strncpy(dtcpHost, token, 256);
        }
        /* Extract duration */
        token = strstr(uri, DURATION_TAG);
        if (NULL != token)
        {
            uri[token - uri] = '\0';
            token += strlen(SIZE_TAG);
            metaData.duration = atol(token);
        }
        /* Extract size */
        token = strstr(uri, SIZE_TAG);
        if (NULL != token)
        {
            uri[token - uri] = '\0';
            token += strlen(SIZE_TAG);
            metaData.contentLengthHint = atoll(token);
        }
        DEBUGLOG("dtcp info %s %d\n", dtcpHost, dtcpPort);
		
        uri_header = strstr(uri, "http://127.0.0.1");
        if (uri_header != NULL)
        {
            uri_header += strlen("http://127.0.0.1");
            sprintf(uri, "file://%s", uri_header);
        }
        /* The open has to be done here instead of in the constructor because any video tag will create a instance
           but we don't want to allocate any memory until the user actually wants to play something */
        index = BMedia_OpenVideoStream((BMediaHandle)(m_privateVariables->m_handle), &videoStreamOpenSettings);
        if (index == -1)
        {
            m_startedPlaying = false;
            loadingFailed();
            m_privateVariables->m_isEndReached = false;
            return;
        }
        BMedia_GetVideoStreamSettings((BMediaHandle)(m_privateVariables->m_handle), index, &videoStreamSettings);
        videoStreamSettings.initialSeekTime = m_privateVariables->m_initialSeekTime;
        m_privateVariables->m_initialSeekTime = 0;

        if (dtcpPort != 0)
        {
            BMedia_PerformDtcpAke((BMediaHandle)(m_privateVariables->m_handle), index, (const char*)dtcpHost, dtcpPort);
            videoStreamSettings.lpMode = BDlna_MediaLinkProtectionMode_eDtcpIp;
        }	
        m_privateVariables->s_streamIndex = index;
        m_privateVariables->s_decoderIndex = videoStreamOpenSettings.decoderIndex; 
        BMedia_SetVideoStreamSettings((BMediaHandle)(m_privateVariables->m_handle), index, &videoStreamSettings);
    }
    if (!m_startedPlaying && m_endTime)
    {
        if (m_privateVariables->m_urlChanged)
        {
            DEBUGLOG("SetUri %s\n", uri);
            BMedia_SetUri((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex), uri, &metaData);
            (m_privateVariables->m_urlChanged) = false;
        }
        DEBUGLOG("Player %d %d\n", m_privateVariables->m_id, MediaPlayerPrivateVariables::s_currentPlayer->m_privateVariables->m_id);
        success = BMedia_Play((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));
        MediaPlayerPrivateVariables::s_playerInUse = true;
    }
    // Should set NetworkState to the correct error code
    if (success == BMEDIA_SUCCESS)
    {
        m_startedPlaying = true;
        m_player->repaint();
        memset(&status, 0, sizeof(BDlna_MediaStatus));
        BMedia_GetStatus((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex), &status);
        if (status.state >= BDlna_MediaState_ePlaying)
        {
            m_endTime = status.last / 1000.0f;
            m_player->durationChanged();
        }
    }
    else
    {
        m_startedPlaying =false;
        loadingFailed();
        m_privateVariables->m_isEndReached = false;
        return;
    }
    m_privateVariables->m_isEndReached = false;
    m_player->timeChanged();
}

void MediaPlayerPrivateBC::pause()
{
//JCG printf("JCG: MediaPlayerPrivateBC::pause\n");
    DEBUGLOG("Pause %d, m_isEndReached = %d\n", m_privateVariables->s_streamIndex, m_privateVariables->m_isEndReached);
    if (m_startedPlaying &&  !m_privateVariables->m_isEndReached)
    {
        if (m_privateVariables->s_streamIndex >= 0)
        { 
            DEBUGLOG("Really Pause %d\n", m_privateVariables->s_streamIndex);
            BMedia_Pause((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));
        }
    }
    m_startedPlaying = false;
}

float MediaPlayerPrivateBC::duration() const
{
    //DEBUGLOG("duration %f\n", m_endTime);
    return m_endTime;
}

float MediaPlayerPrivateBC::currentTime() const
{
    if (m_privateVariables->m_isEndReached)
    {
        return (float)m_endTime;
    }
    BDlna_MediaStatus status;
    memset(&status, 0, sizeof(BDlna_MediaStatus));
    BMedia_GetStatus((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex), &status);
    int position = status.position;
    if (m_privateVariables->s_streamIndex >= 0)
    {
        //DEBUGLOG("WEBKIT: Browser status: %d\n", status.position);
        if (status.state < BDlna_MediaState_ePlaying || m_privateVariables->m_urlChanged)
        {
            position = 0;
        }
    }
    else
    {
        if (m_privateVariables->m_initialSeekTime > 0)
            position = m_privateVariables->m_initialSeekTime * 1000;
        else
            position = 0;
        //DEBUGLOG("---currentTime %d %d\n", status.position, (m_privateVariables->s_streamIndex));
    }
    return (float)position / 1000.0f;
}

void MediaPlayerPrivateBC::seek(float time)
{
    DEBUGLOG("seek %f %d m_isEndReached = %d, m_startedPlaying = %d\n", time, m_privateVariables->m_id, m_privateVariables->m_isEndReached, m_startedPlaying);
//JCG printf("JCG: MediaPlayerPrivateBC::seek(%f)\n",time);
    static bool isSeeking = false;
    unsigned long int position;

    if (time >= m_endTime || isSeeking)
        return;

    position = (unsigned long int)lround(time);
    isSeeking = true;

    if (m_privateVariables->m_isEndReached)
    {
    	m_privateVariables->m_initialSeekTime = position;
    	m_privateVariables->m_isEndReached = false;
    }
    else
    {
	DEBUGLOG("seek %f\n", time);

	if (m_privateVariables->s_streamIndex >= 0)
      BMedia_Seek((BMediaHandle)(m_privateVariables->m_handle), m_privateVariables->s_streamIndex, position);
        else
    	    m_privateVariables->m_initialSeekTime = position;
    }
    m_player->timeChanged();
    isSeeking = false;
}

void MediaPlayerPrivateBC::setEndTime(float time)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setEndTime\n");
}

void MediaPlayerPrivateBC::startEndPointTimerIfNeeded()
{
//JCG printf("JCG: MediaPlayerPrivateBC::startEndPointTimerIfNeeded\n");
    notImplemented();
}

void MediaPlayerPrivateBC::cancelSeek()
{
//JCG printf("JCG: MediaPlayerPrivateBC::cancelSeek\n");
    notImplemented();
}

void MediaPlayerPrivateBC::endPointTimerFired(Timer<MediaPlayerPrivateBC>*)
{
//JCG printf("JCG: MediaPlayerPrivateBC::endPointTimerFired\n");
    notImplemented();
}

bool MediaPlayerPrivateBC::paused() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::paused\n");
    DEBUGLOG("Checking paused!! m_startedPlaying=%d m_isEndReached = %d\n", m_startedPlaying, m_privateVariables->m_isEndReached);
    return !m_startedPlaying;
}

bool MediaPlayerPrivateBC::seeking() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::seeking\n");
    return false;
}

// Returns the size of the video
IntSize MediaPlayerPrivateBC::naturalSize() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::naturalSize\n");

    //FIXME: returning the spec default for now
    return IntSize(300,150);
}

bool MediaPlayerPrivateBC::hasVideo() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::hasVideo\n");
    return m_readyState == MediaPlayer::HaveEnoughData;
}

bool MediaPlayerPrivateBC::hasAudio() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::hasAudio\n");
    return m_readyState == MediaPlayer::HaveEnoughData;
}

void MediaPlayerPrivateBC::setVolume(float volume)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setVolume\n");
     m_volume = volume;
     BMedia_AudioSettings pSettings;

     BMedia_GetAudioSettings((BMediaHandle)(m_privateVariables->m_handle), &pSettings);
     pSettings.leftVolume = (int)((volume * 140) - 140);
     pSettings.rightVolume = (int)((volume * 140) - 140);
     if (MediaPlayerPrivateVariables::s_currentPlayer == this)
    	 BMedia_SetAudioSettings((BMediaHandle)(m_privateVariables->m_handle), &pSettings);
     setMuted(false);
     volumeChanged();
}

void MediaPlayerPrivateBC::setMuted(bool b)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setMuted\n");
    if (MediaPlayerPrivateVariables::s_currentPlayer == this) {
        BMedia_AudioSettings audioSettings;
        BMedia_GetAudioSettings((BMediaHandle)(m_privateVariables->m_handle), &audioSettings);
        audioSettings.muted = b;
        BMedia_SetAudioSettings((BMediaHandle)(m_privateVariables->m_handle), &audioSettings);
    }
}

void MediaPlayerPrivateBC::setRate(float rate)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setRate(%f)\n",rate);
    DEBUGLOG("%s, %f\n", __FUNCTION__, rate);
    BMedia_Error success;
    if (rate == 0 || !m_startedPlaying || m_privateVariables->m_isEndReached)
        return;
    BDlna_MediaStatus status;
    memset(&status, 0, sizeof(BDlna_MediaStatus));
    BMedia_GetStatus((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex), &status);
    for (int i = 0; i < status.numPlaySpeedEntries; i++)
    {
        printf("Set Rate speed[%i]: %d rate: %d\n",i, status.playSpeed[i], (int) rate);
        if (status.playSpeed[i] == (int)rate)
        {
             DEBUGLOG("Set Rate scan %d\n", status.playSpeed[i]);
             success = BMedia_Scan((BMediaHandle)(m_privateVariables->m_handle), m_privateVariables->s_streamIndex, (int)rate); 
             if (success == BMEDIA_SUCCESS)
                 m_rate = rate;
            return;
        }
    }
    if ((int)rate == 1)
    {
         success = BMedia_Scan((BMediaHandle)(m_privateVariables->m_handle), m_privateVariables->s_streamIndex, (int) rate);
         if (success == BMEDIA_SUCCESS)
             m_rate = 1.0;
    }
}

float MediaPlayerPrivateBC::rate()
{
//JCG printf("JCG: MediaPlayerPrivateBC::rate\n");
    printf("rate read: %f\n",m_rate);
    return m_rate;
}

int MediaPlayerPrivateBC::dataRate() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::dataRate\n");
    printf("dataRate read: %f\n",m_rate);
    return m_rate;
}

MediaPlayer::NetworkState MediaPlayerPrivateBC::networkState() const
{
    return m_networkState;
}

MediaPlayer::ReadyState MediaPlayerPrivateBC::readyState() const
{
    return m_readyState;
}

PassRefPtr<TimeRanges> MediaPlayerPrivateBC::buffered() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::buffered\n");
    PassRefPtr<TimeRanges> ranges = TimeRanges::create();

    BDlna_MediaStatus status;
    if (m_privateVariables->s_streamIndex != -1)
    {
        memset(&status, 0, sizeof(BDlna_MediaStatus));
        BMedia_GetStatus((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex), &status);
        //DEBUGLOG("buffered::\n");
        ranges->add(status.liveBufferStart,status.liveBufferEnd);
    } else {
        ranges->add(0,0);
    }
    return ranges;
}

float MediaPlayerPrivateBC::maxTimeBuffered() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::maxTimeBuffered\n");
    notImplemented();
    LOG_VERBOSE(Media, "maxTimeBuffered");
    // rtsp streams are not buffered
    return maxTimeLoaded();
}

float MediaPlayerPrivateBC::maxTimeSeekable() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::maxTimeSeekable\n");
    // TODO
    LOG_VERBOSE(Media, "maxTimeSeekable");
    if (m_isStreaming)
        return numeric_limits<float>::infinity();
    // infinite duration means live stream
    return maxTimeLoaded();
}

float MediaPlayerPrivateBC::maxTimeLoaded() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::maxTimeLoaded\n");
    // TODO
    LOG_VERBOSE(Media, "maxTimeLoaded");
    notImplemented();
    return duration();
}

unsigned MediaPlayerPrivateBC::bytesLoaded() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::bytesLoaded\n");
    notImplemented();
    LOG_VERBOSE(Media, "bytesLoaded");
    return 1;//totalBytes() * maxTime / dur;
}

bool MediaPlayerPrivateBC::totalBytesKnown() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::totalBytesKnown\n");
    notImplemented();
    LOG_VERBOSE(Media, "totalBytesKnown");
    return totalBytes() > 0;
}

unsigned MediaPlayerPrivateBC::totalBytes() const
{
//JCG printf("JCG: MediaPlayerPrivateBC::totalBytes\n");
    notImplemented();
    LOG_VERBOSE(Media, "totalBytes");

    // Do something with m_source to get the total bytes of the media
    return 0;
}

void MediaPlayerPrivateBC::cancelLoad()
{
//JCG printf("JCG: MediaPlayerPrivateBC::cancelLoad\n");
    notImplemented();
}

void MediaPlayerPrivateBC::updateStates()
{
//JCG printf("JCG: MediaPlayerPrivateBC::updateStates\n");
    MediaPlayer::NetworkState oldNetworkState = m_networkState;
    MediaPlayer::ReadyState oldReadyState = m_readyState;

    // Uncomment the code below once Broadcom callback is OK
    // and think to remove m_stateTimer
    /*
    BDlna_MediaState mediaState;
    BMediaWindow_GetState(m_windowHandle, &mediaState);
    if (mediaState == BDlna_MediaState_eReady) {
        m_networkState = MediaPlayer::Loaded;
        m_readyState = MediaPlayer::HaveEnoughData;
    }
    */
    m_networkState = MediaPlayer::Loaded;
    m_readyState = MediaPlayer::HaveEnoughData;

    if (m_networkState != oldNetworkState)
    {
        m_player->networkStateChanged();
    }
    if (m_readyState != oldReadyState)
    {
        m_player->readyStateChanged();
    }
}

void MediaPlayerPrivateBC::loadStateChanged()
{
//JCG printf("JCG: MediaPlayerPrivateBC::loadStateChanged\n");
    updateStates();
}

void MediaPlayerPrivateBC::rateChanged()
{
//JCG printf("JCG: MediaPlayerPrivateBC::rateChanged\n");
    updateStates();
}

void MediaPlayerPrivateBC::sizeChanged()
{
//JCG printf("JCG: MediaPlayerPrivateBC::sizeChanged\n");
    m_player->sizeChanged();
}

void MediaPlayerPrivateBC::timeChanged()
{
//JCG printf("JCG: MediaPlayerPrivateBC::timeChanged\n");
    updateStates();
    m_player->timeChanged();
}

void MediaPlayerPrivateBC::volumeChanged()
{
//JCG printf("JCG: MediaPlayerPrivateBC::volumeChanged\n");
    m_player->volumeChanged(m_volume);
}

void MediaPlayerPrivateBC::didEnd()
{
//JCG printf("JCG: MediaPlayerPrivateBC::didEnd\n");
    DEBUGLOG("###### End reached. Stream index = %d\n", m_privateVariables->s_streamIndex);
    m_privateVariables->m_isEndReached = true;

    if (m_privateVariables->s_streamIndex >= 0)
    {
        BMedia_Stop((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));
		
        //Need to close here so the memory is freed
        BMedia_CloseVideoStream((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));

        //Set this because if it is looping, the next play will create a new video stream
        // and we'll need to set the url again 
        m_privateVariables->m_urlChanged = true;
    }
    m_privateVariables->s_streamIndex = -1;
    m_startedPlaying =false;

    if (m_networkState != MediaPlayer::Loaded)
    {
        m_networkState = MediaPlayer::Loaded;
        m_player->networkStateChanged();
    }
    if (m_readyState != MediaPlayer::HaveEnoughData)
    {
        m_readyState = MediaPlayer::HaveEnoughData;
        m_player->readyStateChanged();
    }
    m_player->timeChanged();
}

void MediaPlayerPrivateBC::stopInterrupt()
{
//JCG printf("JCG: MediaPlayerPrivateBC::stopInterrupt\n");
    DEBUGLOG("interrupted %d\n", m_privateVariables->m_id);
    if (MediaPlayerPrivateVariables::s_currentPlayer == this && MediaPlayerPrivateVariables::s_playerInUse)
    {
        BMedia_Stop((BMediaHandle)(m_privateVariables->m_handle), (m_privateVariables->s_streamIndex));
        MediaPlayerPrivateVariables::s_playerInUse = false;
    }
    m_privateVariables->m_isEndReached = false;
    m_startedPlaying = false;
    m_privateVariables->m_urlChanged = true;
}

void MediaPlayerPrivateBC::loadingFailed()
{
//JCG printf("JCG: MediaPlayerPrivateBC::loadingFailed\n");
    if (m_networkState != MediaPlayer::NetworkError)
    {
        m_networkState = MediaPlayer::NetworkError;
        m_player->networkStateChanged();
    }
    if (m_readyState != MediaPlayer::HaveNothing)
    {
        m_readyState = MediaPlayer::HaveNothing;
        m_player->readyStateChanged();
    }
}

void MediaPlayerPrivateBC::setBounds(IntRect bounds)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setBounds\n");
    FrameView* frameView = m_player->frameView();

    if (!frameView || !frameView->frame() || !frameView->frame()->page()
        || !frameView->frame()->page()->chrome()
        || !frameView->frame()->page()->chrome()->client())
        return;

    IntRect screenRect(frameView->frame()->page()->chrome()->client()->safeRect());

    m_bounds = bounds;
    bounds.move(screenRect.x(), screenRect.y());

    ScrollView* scrlView = (ScrollView*)frameView;
    for ( ; scrlView; scrlView = scrlView->parent())
    {
        bounds.move(scrlView->x() - scrlView->scrollX(), scrlView->y() - scrlView->scrollY());
    }
    if ((m_privateVariables->m_gainedFocus || m_privateVariables->m_viewRect != bounds)
        && m_privateVariables->s_streamIndex >= 0)
    {
        m_privateVariables->m_gainedFocus = false;
        BMedia_GraphicsSettings graphicsSettings;
        BMedia_VideoStreamSettings videoStreamSettings;
        
        BMedia_GetGraphicsSettings((BMediaHandle)(m_privateVariables->m_handle), &graphicsSettings);
        BMedia_GetVideoStreamSettings((BMediaHandle)(m_privateVariables->m_handle), m_privateVariables->s_streamIndex,
                                      &videoStreamSettings);

        if (graphicsSettings.zorder < 2 || graphicsSettings.alpha != 0xff
            || videoStreamSettings.x != (unsigned int)bounds.x()
            || videoStreamSettings.y != (unsigned int)bounds.y() 
            || videoStreamSettings.width != (unsigned int)bounds.width()
            || videoStreamSettings.height != (unsigned int)bounds.height()
            || videoStreamSettings.alpha != 0xff 
            || videoStreamSettings.zorder != m_privateVariables->s_decoderIndex
            || videoStreamSettings.enabled != true || videoStreamSettings.fullScreen != false)
        {
            videoStreamSettings.x = (unsigned int)bounds.x();
            videoStreamSettings.y = (unsigned int)bounds.y();
            videoStreamSettings.width = (unsigned int)bounds.width();
            videoStreamSettings.height = (unsigned int)bounds.height();
            videoStreamSettings.alpha = 0xff;
            //videoStreamSettings.zorder = m_privateVariables->s_streamIndex;
            videoStreamSettings.zorder = m_privateVariables->s_decoderIndex;
            videoStreamSettings.enabled = true;
            videoStreamSettings.fullScreen = false;

            BMedia_SetVideoStreamSettings((BMediaHandle)(m_privateVariables->m_handle), m_privateVariables->s_streamIndex,
                                          &videoStreamSettings);

            graphicsSettings.alpha = 0xff;
            //graphicsSettings.zorder = 2;
            graphicsSettings.enabled = true;

            BMedia_SetGraphicsSettings((BMediaHandle)(m_privateVariables->m_handle), &graphicsSettings);
        }
        m_privateVariables->m_viewRect = bounds;
    }
}

void MediaPlayerPrivateBC::setSize(const IntSize& size)
{
    //DEBUGLOG("Size changed %d %d -> %d %d\n", m_size.width(), m_size.height(), size.width(), size.height());
    setBounds(IntRect(m_bounds.x(), m_bounds.y(), size.width(), size.height()));
}

void MediaPlayerPrivateBC::setVisible(bool visible)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setVisible\n");
    //DEBUGLOG("setVisible %d  ", visible);
    if (visible != m_visible)
    {
        m_visible = visible;
        DEBUGLOG(" %p / %p\n", MediaPlayerPrivateVariables::s_currentPlayer, this);
        if (MediaPlayerPrivateVariables::s_currentPlayer == this)
        {
            if (m_visible)
                m_player->repaint();
            else
                showGfx((BMediaHandle)(m_privateVariables->m_handle));
        }
    }
}

void MediaPlayerPrivateBC::repaint()
{
//JCG printf("JCG: MediaPlayerPrivateBC::repaint\n");
    m_player->repaint();
}

void MediaPlayerPrivateBC::paint(GraphicsContext* context, const IntRect& rect)
{
    //DEBUGLOG("Paint (%d) (%d)  %d %d - %d %d, top = %d\n", (m_privateVariables->m_id), (m_privateVariables->s_streamIndex), rect.x(), rect.y(), rect.width(), rect.height(), top);
    if (context->paintingDisabled())
        return;

    if (!m_visible)
    {
        context->save();
        context->clearRect(rect);
        context->restore();
        return;
    }
    setBounds(rect);
        
    context->save();
    context->clearRect(rect);
    context->restore();
}

#if ENABLE(CEHTML_VIDEO)
void MediaPlayerPrivateBC::stop()
{
//JCG printf("JCG: MediaPlayerPrivateBC::stop\n");
    if (MediaPlayerPrivateVariables::s_currentPlayer == this)
        pause();
}

void MediaPlayerPrivateBC::setAspect(MediaPlayer::VideoScale)
{
//JCG printf("JCG: MediaPlayerPrivateBC::setAspect\n");
}

WTF::Vector<float> MediaPlayerPrivateBC::playSpeeds()
{
//JCG printf("JCG: MediaPlayerPrivateBC::playSpeeds\n");
    static WTF::Vector<float> speed;
    if (!speed.size())
        speed.append(1.0);
    return speed;
}
#endif

void MediaPlayerPrivateBC::getSupportedTypes(HashSet<String>& types)
{
//JCG printf("JCG: MediaPlayerPrivateBC::getSupportedTypes\n");
    // FIXME: query the engine to see what types are supported
    notImplemented();
    types.add(String("video/mp4"));
}

MediaPlayer::SupportsType MediaPlayerPrivateBC::supportsType(const String& type, const String& codecs)
{
//JCG printf("JCG: MediaPlayerPrivateBC::supportsType\n");
    if (type.startsWith("text",true) || type.startsWith("application",true)
        || type.startsWith("image",true) || (type.startsWith("video",false)
        && (type.contains("webm",false)|| type.contains("x-flv", false))))
    {
        return MediaPlayer::IsNotSupported;
    }
    if (codecs.contains("vp6", false) || codecs.contains("vp8", false)
       || codecs.contains("vorbis", false))
    {
        return MediaPlayer::IsNotSupported;
    }
    return MediaPlayer::IsSupported;
}

void MediaPlayerPrivateBC::notifyTimeChanged(void* context)
{
//JCG printf("JCG: MediaPlayerPrivateBC::notifyTimeChanged\n");
    MediaPlayerPrivateBC* player = static_cast<MediaPlayerPrivateBC*>(context);

    DEBUGLOG("notifyTimeChanged id = %d, stream index = %d\n",player->m_privateVariables->m_id, 
             player->m_privateVariables->s_streamIndex);

    if (player->m_privateVariables->s_streamIndex != -1)
	player->didEnd();
    player->timeChanged();
}

void MediaPlayerPrivateBC::notifyError(void* context)
{
//JCG printf("JCG: MediaPlayerPrivateBC::notifyError\n");
    DEBUGLOG("notifyError\n");
    MediaPlayerPrivateBC* player = static_cast<MediaPlayerPrivateBC*>(context);
    MediaPlayerPrivateVariables::s_playerInUse = false;
    player->m_privateVariables->m_isEndReached = true;
    player->m_startedPlaying = false;
    player->m_privateVariables->m_urlChanged = true;
    MediaPlayerPrivateVariables::s_currentPlayer = NULL;
    player->m_player->timeChanged();
    return;
}

void MediaPlayerPrivateBC::notifyInterrupt(void* context)
{
//JCG printf("JCG: MediaPlayerPrivateBC::notifyInterrupt\n");
    DEBUGLOG("notifyInterrupt\n");
    MediaPlayerPrivateBC* player = static_cast<MediaPlayerPrivateBC*>(context);
    MediaPlayerPrivateVariables::s_playerInUse = false;
    player->m_privateVariables->m_isEndReached = true;
    player->m_privateVariables->m_urlChanged = true;
    player->m_player->timeChanged();
    MediaPlayerPrivateVariables::s_currentPlayer = NULL;
}

} // namespace WebCore
#endif // MANGAREVA VIDEO
#endif // ENABLE(VIDEO)
