/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

/*
 * vie_render_impl.cc
 */

#include "vie_render_impl.h"

// Defines
#include "engine_configurations.h"
#include "vie_defines.h"

#include "trace.h"
#include "video_render.h"
#include "video_render_defines.h"
#include "vie_errors.h"
#include "vie_impl.h"
#include "vie_capturer.h"
#include "vie_channel.h"
#include "vie_frame_provider_base.h"
#include "vie_channel_manager.h"
#include "vie_input_manager.h"
#include "vie_render_manager.h"

namespace webrtc
{

// ----------------------------------------------------------------------------
// GetInterface
// ----------------------------------------------------------------------------

ViERender* ViERender::GetInterface(VideoEngine* videoEngine)
{
#ifdef WEBRTC_VIDEO_ENGINE_RENDER_API
    if (videoEngine == NULL)
    {
        return NULL;
    }
    VideoEngineImpl* vieImpl = reinterpret_cast<VideoEngineImpl*> (videoEngine);
    ViERenderImpl* vieRenderImpl = vieImpl;
    (*vieRenderImpl)++; // Increase ref count

    return vieRenderImpl;
#else
    return NULL;
#endif
}

// ----------------------------------------------------------------------------
// Release
//
// Releases the interface, i.e. reduces the reference counter. The number of
// remaining references is returned, -1 if released too many times.
// ----------------------------------------------------------------------------

int ViERenderImpl::Release()
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, _instanceId,
                 "ViERender::Release()");
    (*this)--; // Decrease ref count

    WebRtc_Word32 refCount = GetCount();
    if (refCount < 0)
    {
        WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, _instanceId,
                     "ViERender release too many times");
        // SetLastError()
        return -1;
    }
    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, _instanceId,
                 "ViERender reference count: %d", refCount);
    return refCount;
}

// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------

ViERenderImpl::ViERenderImpl()
{
    WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, _instanceId,
                 "ViERenderImpl::ViERenderImpl() Ctor");
}

// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------

ViERenderImpl::~ViERenderImpl()
{
    WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, _instanceId,
                 "ViERenderImpl::~ViERenderImpl() Dtor");
}

// ============================================================================
// Registration of render module
// ============================================================================

// ----------------------------------------------------------------------------
// RegisterVideoRenderModule
//
// Registers a video render module, must be called before
// AddRenderer is called for an input stream associated
// with the same window as the module.
// ----------------------------------------------------------------------------

int ViERenderImpl::RegisterVideoRenderModule(
    VideoRender& renderModule)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s (&renderModule: %p)", __FUNCTION__, &renderModule);

    if (_renderManager.RegisterVideoRenderModule(renderModule) != 0)
    {
        // Error logging is done in RegisterVideoRenderModule
        SetLastError(kViERenderUnknownError);
        return -1;
    }

    return 0;
}

// ----------------------------------------------------------------------------
// DeRegisterVideoRenderModule
//
// De-registers a video render module, must be called after
// RemoveRenderer has been called for all input streams associated
// with the same window as the module.
// ----------------------------------------------------------------------------

int ViERenderImpl::DeRegisterVideoRenderModule(
    VideoRender& renderModule)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s (&renderModule: %p)", __FUNCTION__, &renderModule);
    if (_renderManager.DeRegisterVideoRenderModule(renderModule) != 0)
    {
        // Error logging is done in DeRegisterVideoRenderModule
        SetLastError(kViERenderUnknownError);
        return -1;
    }

    return 0;
}

// ============================================================================
// Add renderer
// ============================================================================

int ViERenderImpl::AddRenderer(const int renderId, void* window,
                               const unsigned int zOrder, const float left,
                               const float top, const float right,
                               const float bottom)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s (renderId: %d,  window: 0x%p, zOrder: %u, left: %f, "
                 "top: %f, right: %f, bottom: %f)",
                 __FUNCTION__, renderId, window, zOrder, left, top, right,
                 bottom);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    { // Check if the renderer exist already
        ViERenderManagerScoped rs(_renderManager);
        if (rs.Renderer(renderId) != NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s - Renderer already exist %d.", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderAlreadyExists);
            return -1;
        }
    }

    if (renderId >= kViEChannelIdBase && renderId <= kViEChannelIdMax)
    {
        // This is a channel
        ViEChannelManagerScoped cm(_channelManager);
        ViEFrameProviderBase* frameProvider = cm.Channel(renderId);
        if (frameProvider == NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: FrameProvider id %d doesn't exist", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        ViERenderer* renderer = _renderManager.AddRenderStream(renderId,
                                                               window, zOrder,
                                                               left, top,
                                                               right, bottom);
        if (renderer == NULL)
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }
        return frameProvider->RegisterFrameCallback(renderId, renderer);
    }
    else // camera or file
    {
        ViEInputManagerScoped is(_inputManager);
        ViEFrameProviderBase* frameProvider = is.FrameProvider(renderId);
        if (frameProvider == NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: FrameProvider id %d doesn't exist", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        ViERenderer* renderer = _renderManager.AddRenderStream(renderId,
                                                               window, zOrder,
                                                               left, top,
                                                               right, bottom);
        if (renderer == NULL)
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }
        return frameProvider->RegisterFrameCallback(renderId, renderer);
    }
    SetLastError(kViERenderInvalidRenderId);
    return -1;

}

int ViERenderImpl::RemoveRenderer(const int renderId)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId),
                 "%s(renderId: %d)", __FUNCTION__, renderId);
    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    ViERenderer* renderer = NULL;
    {
        ViERenderManagerScoped rs(_renderManager);
        renderer = rs.Renderer(renderId);
        if (!renderer)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s No render exist with renderId: %d", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
    } // Leave the scope lock since we don't want to lock two managers
      // simultanousely

    if (renderId >= kViEChannelIdBase && renderId <= kViEChannelIdMax)
    {
        // This is a channel
        ViEChannelManagerScoped cm(_channelManager);
        ViEChannel* channel = cm.Channel(renderId);
        if (!channel)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: no channel with id %d exists ", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        channel->DeregisterFrameCallback(renderer);
    }
    else //Provider owned by inputmanager - ie file or capture device
    {
        ViEInputManagerScoped is(_inputManager);
        ViEFrameProviderBase* provider = is.FrameProvider(renderId);
        if (!provider)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: no provider with id %d exists ", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        provider->DeregisterFrameCallback(renderer);

    }
    if (_renderManager.RemoveRenderStream(renderId) != 0)
    {
        SetLastError(kViERenderUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// Start/stop
// ============================================================================

// ----------------------------------------------------------------------------
// StartRender
//
// Starts rendering the stream from the channel
// ----------------------------------------------------------------------------

int ViERenderImpl::StartRender(const int renderId)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, renderId), "%s(channel: %d)", __FUNCTION__,
                 renderId);

    ViERenderManagerScoped rs(_renderManager);
    ViERenderer* ptrRender = rs.Renderer(renderId);
    if (ptrRender == NULL)
    {
        // No renderer for this channel
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, renderId),
                     "%s: No renderer with render Id %d exist.", __FUNCTION__,
                     renderId);
        SetLastError(kViERenderInvalidRenderId);
        return -1;
    }

    if (ptrRender->StartRender() != 0)
    {
        SetLastError(kViERenderUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// StopRender
//
// Stop rendering a stream
// ----------------------------------------------------------------------------

int ViERenderImpl::StopRender(const int renderId)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
                 ViEId(_instanceId, renderId), "%s(channel: %d)", __FUNCTION__,
                 renderId);

    ViERenderManagerScoped rs(_renderManager);
    ViERenderer* ptrRender = rs.Renderer(renderId);
    if (ptrRender == NULL)
    {
        // No renderer for this channel
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, renderId),
                     "%s: No renderer with renderId %d exist.", __FUNCTION__,
                     renderId);
        SetLastError(kViERenderInvalidRenderId);
        return -1;
    }
    if (ptrRender->StopRender() != 0)
    {
        SetLastError(kViERenderUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// Stream configurations
// ============================================================================

// ----------------------------------------------------------------------------
// ConfigureRender
//
// Reconfigures an already added render stream
// ----------------------------------------------------------------------------

int ViERenderImpl::ConfigureRender(int renderId, const unsigned int zOrder,
                                   const float left, const float top,
                                   const float right, const float bottom)
{
    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo, ViEId(_instanceId, renderId),
                 "%s(channel: %d)", __FUNCTION__, renderId);

    ViERenderManagerScoped rs(_renderManager);
    ViERenderer* ptrRender = rs.Renderer(renderId);
    if (ptrRender == NULL)
    {
        // No renderer for this channel
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, renderId),
                     "%s: No renderer with renderId %d exist.", __FUNCTION__,
                     renderId);
        SetLastError(kViERenderInvalidRenderId);
        return -1;
    }

    if (ptrRender->ConfigureRenderer(zOrder, left, top, right, bottom) != 0)
    {
        SetLastError(kViERenderUnknownError);
        return -1;
    }
    return 0;
}

// ----------------------------------------------------------------------------
// MirrorRenderStream
//
// Enables mirror rendering
// ----------------------------------------------------------------------------

int ViERenderImpl::MirrorRenderStream(const int renderId, const bool enable,
                                      const bool mirrorXAxis,
                                      const bool mirrorYAxis)
{
    ViERenderManagerScoped rs(_renderManager);
    ViERenderer* ptrRender = rs.Renderer(renderId);
    if (ptrRender == NULL)
    {
        // No renderer for this channel
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, renderId),
                     "%s: No renderer with renderId %d exist.", __FUNCTION__,
                     renderId);
        SetLastError(kViERenderInvalidRenderId);
        return -1;
    }
    if (ptrRender->EnableMirroring(renderId, enable, mirrorXAxis, mirrorYAxis)
        != 0)
    {
        SetLastError(kViERenderUnknownError);
        return -1;
    }
    return 0;
}

// ============================================================================
// External render
// ============================================================================


// ----------------------------------------------------------------------------
// 
//
// AddRenderer
// ----------------------------------------------------------------------------

int ViERenderImpl::AddRenderer(const int renderId,
                               webrtc::RawVideoType videoInputFormat,
                               ExternalRenderer* externalRenderer)
{
    // check if the client requested a format that we can convert the frames to
    if (videoInputFormat != webrtc::kVideoI420
        && videoInputFormat != webrtc::kVideoYV12
        && videoInputFormat != webrtc::kVideoYUY2
        && videoInputFormat != webrtc::kVideoUYVY
        && videoInputFormat != webrtc::kVideoARGB
        && videoInputFormat != webrtc::kVideoRGB24
        && videoInputFormat != webrtc::kVideoRGB565
        && videoInputFormat != webrtc::kVideoARGB4444
        && videoInputFormat != webrtc::kVideoARGB1555)
    {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
                     ViEId(_instanceId, renderId),
                     "%s: Unsupported video frame format requested",
                     __FUNCTION__, renderId);
        SetLastError(kViERenderInvalidFrameFormat);
        return -1;
    }

    if (!IsInitialized())
    {
        SetLastError(kViENotInitialized);
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                     "%s - ViE instance %d not initialized", __FUNCTION__,
                     _instanceId);
        return -1;
    }

    { // Check if the renderer exist already
        ViERenderManagerScoped rs(_renderManager);
        if (rs.Renderer(renderId) != NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s - Renderer already exist %d.", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderAlreadyExists);
            return -1;
        }
    }

    if (renderId >= kViEChannelIdBase && renderId <= kViEChannelIdMax)
    {
        // This is a channel
        ViEChannelManagerScoped cm(_channelManager);
        ViEFrameProviderBase* frameProvider = cm.Channel(renderId);
        if (frameProvider == NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: FrameProvider id %d doesn't exist", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        ViERenderer* ptrRender = _renderManager.AddRenderStream(renderId, NULL,
                                                                0, 0.0f, 0.0f,
                                                                1.0f, 1.0f);
        if (ptrRender == NULL)
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }
        if (-1 == ptrRender->SetExternalRenderer(renderId, videoInputFormat,
                                                 externalRenderer))
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }

        return frameProvider->RegisterFrameCallback(renderId, ptrRender);
    }
    else // camera or file
    {
        ViEInputManagerScoped is(_inputManager);
        ViEFrameProviderBase* frameProvider = is.FrameProvider(renderId);
        if (frameProvider == NULL)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_instanceId),
                         "%s: FrameProvider id %d doesn't exist", __FUNCTION__,
                         renderId);
            SetLastError(kViERenderInvalidRenderId);
            return -1;
        }
        ViERenderer* ptrRender = _renderManager.AddRenderStream(renderId, NULL,
                                                                0, 0.0f, 0.0f,
                                                                1.0f, 1.0f);
        if (ptrRender == NULL)
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }
        if (-1 == ptrRender->SetExternalRenderer(renderId, videoInputFormat,
                                                 externalRenderer))
        {
            SetLastError(kViERenderUnknownError);
            return -1;
        }
        return frameProvider->RegisterFrameCallback(renderId, ptrRender);
    }
    SetLastError(kViERenderInvalidRenderId);
    return -1;

}

} // namespace webrtc
