/*
-----------------------------------------------------------------------------
This source file is part of OpenSpace3D
For the latest info, see http://www.openspace3d.com

Copyright (c) 2011 I-maginer

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt
-----------------------------------------------------------------------------
*/

#include "embeddedWebNavigatorRenderHandler.h"
#include "embeddedWebNavigatorClient.h"
#include "embeddedWebNavigator.h"

namespace Scol
{
  namespace EmbeddedWebNavigator
  {

WebNavigatorRenderHandler::WebNavigatorRenderHandler(CefRefPtr<WebNavigatorClient>& parentWebNavigatorClientInstance, const ScolWindowHandle& scolMainWindow, bool offscreen, CefRefPtr<WebNavigatorCriticalSection>& offscreenBufferCriticalSectionInstance) : WebNavigatorHandler(parentWebNavigatorClientInstance, scolMainWindow),
                                                                                                                                                                                                                                                                isOffscreen(offscreen),
                                                                                                                                                                                                                                                                offscreenBufferCriticalSection(offscreenBufferCriticalSectionInstance)
{
  view_buffer = 0;
  view_buffer_size = 0;
  popup_buffer = 0;
  popup_buffer_size = 0;
  view_buffer_width = 0;
  view_buffer_height = 0;
}

WebNavigatorRenderHandler::WebNavigatorRenderHandler() : WebNavigatorHandler(CefRefPtr<WebNavigatorClient>(), 0),
                                                         isOffscreen(false)
{
  // Forbidden ctor
}

WebNavigatorRenderHandler::~WebNavigatorRenderHandler()
{
  if(view_buffer)
    delete[] view_buffer;
  if(popup_buffer)
    delete[] popup_buffer;
}

unsigned char* WebNavigatorRenderHandler::GetOffscreenBuffer()
{
  if(isOffscreen)
    return view_buffer;
  else
    return 0;
}

bool WebNavigatorRenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect)
{
  if(isOffscreen)
  {
    assert(CefCurrentlyOn(TID_UI));

    rect.x = rect.y = 0;
    browser->GetSize(PET_VIEW, rect.width, rect.height);
    return true;
  }
  else
    return false;
}

bool WebNavigatorRenderHandler::GetScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect)
{
  return GetViewRect(browser, rect);
}

bool WebNavigatorRenderHandler::GetScreenPoint(CefRefPtr<CefBrowser> browser, int viewX, int viewY, int& screenX, int& screenY)
{
  if(isOffscreen)
  {
    assert(CefCurrentlyOn(TID_UI));

    // TODO
    return true;
  }
  else
    return false;
}

void WebNavigatorRenderHandler::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show)
{
  if(isOffscreen)
  {
    assert(CefCurrentlyOn(TID_UI));

    if(!show)
    {
      // Clear the popup buffer.
      popup_rect.Set(0,0,0,0);
      if(popup_buffer)
      {
        delete[] popup_buffer;
        popup_buffer = 0;
        popup_buffer_size = 0;
      }
    }
  }
}

void WebNavigatorRenderHandler::OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect)
{
  if(isOffscreen)
  {
    assert(CefCurrentlyOn(TID_UI));

    if(rect.width > 0)
    {
      // Update the popup rectange. It will always be inside the view due to
      // HandleGetRect().
      popup_rect = rect;
    }
  }
}

void WebNavigatorRenderHandler::OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer)
{
  if(isOffscreen)
  {
    if(!dirtyRects.empty())
    {
      assert(CefCurrentlyOn(TID_UI));
      
      offscreenBufferCriticalSection->Lock();
      if (type == PET_VIEW)
      {
        // Paint the view.
        SetRGB(browser, buffer, dirtyRects, true);

        //parentWebNavigator->GetParentWindowHandle();
        // Update the whole texture. This is done for simplicity instead of
        // updating just the dirty region.
        // TODO glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_width, g_height, GL_RGB, GL_UNSIGNED_BYTE, view_buffer);
      }
      
      if(popup_rect.width > 0)
      {
        if (type == PET_POPUP)
        {
          // Paint the popup.
          SetRGB(browser, buffer, dirtyRects, false);
        }

        if (popup_buffer)
        {
          // Update the popup region.
          // TODO glTexSubImage2D(GL_TEXTURE_2D, 0, popup_rect_.x,
          //                g_height-popup_rect_.y-popup_rect_.height, popup_rect_.width,
          //                popup_rect_.height, GL_RGB, GL_UNSIGNED_BYTE, popup_buffer);
        }
      }
      offscreenBufferCriticalSection->Unlock();
    }
  }

  parentWebNavigator->ResetInput();
}

void WebNavigatorRenderHandler::OnCursorChange(CefRefPtr<CefBrowser> browser, CefCursorHandle cursor)
{
  /*if(isOffscreen)
  {
    assert(CefCurrentlyOn(TID_UI));

    // Change the plugin window's cursor.
    ScolWindowHandle parentWindowHandle = parentWebNavigator->GetParentWindowHandle();
    if(parentWindowHandle)
    {
      SetClassLong(parentWindowHandle, GCL_HCURSOR, static_cast<LONG>(reinterpret_cast<LONG_PTR>(cursor)));
      SetCursor(cursor);
    }
  }*/
}

void WebNavigatorRenderHandler::SetRGB(CefRefPtr<CefBrowser> browser, const void* src, const RectList& dirtyRects, bool view)
{
  // TODO test with popups
  int renderSizeWidth = 0;
  int renderSizeHeight = 0;
  browser->GetSize(view ? PET_VIEW : PET_POPUP, renderSizeWidth, renderSizeHeight);
  SetBufferSize(renderSizeWidth, renderSizeHeight, view);
  ConvertToRGBA((unsigned char*)src, (view ? view_buffer : popup_buffer), dirtyRects, renderSizeWidth, renderSizeHeight);
}

unsigned int WebNavigatorRenderHandler::GetOffscreenBufferWidth()
{
  return view_buffer_width;
}

unsigned int WebNavigatorRenderHandler::GetOffscreenBufferHeight()
{
  return view_buffer_height;
}

void WebNavigatorRenderHandler::SetBufferSize(int width, int height, bool view)
{
  int dst_size = width * height * 4;

  // Allocate a new buffer if necesary.
  if (view)
  {
    view_buffer_width = width;
    view_buffer_height = height;

    if (dst_size != view_buffer_size)
    {
      if (view_buffer)
        delete[] view_buffer;
      
      view_buffer = new unsigned char[dst_size];
      view_buffer_size = dst_size;
    }
  }
  else
  {
    if (dst_size != popup_buffer_size)
    {
      if (popup_buffer)
        delete[] popup_buffer;
      
      popup_buffer = new unsigned char[dst_size];
      popup_buffer_size = dst_size;
    }
  }
}

void WebNavigatorRenderHandler::ConvertToRGBA(const unsigned char* src, unsigned char* dst, const RectList& dirtyRects, int renderSizeWidth, int renderSizeHeight)
{
  memcpy(dst, src, renderSizeWidth * renderSizeHeight * 4);

  // Update all dirty regions
  /*RectList::const_iterator iDirtyRects = dirtyRects.begin();
  while(iDirtyRects != dirtyRects.end())
  {
    // Position the buffer pointer to the start of the dirty region
    int lineOffset = (renderSizeWidth - (*iDirtyRects).width);
    int initialOffset = ((*iDirtyRects).y * renderSizeWidth) + (*iDirtyRects).x;
    int sp = initialOffset * 4;
    int dp = initialOffset * 4;

    // Update one dirty region
    for(int i = 0; i < (*iDirtyRects).height; i++)
    {
      for(int j = 0; j < (*iDirtyRects).width; j++, dp += 4, sp += 4)
      {
        dst[dp] = src[sp];     // R
        dst[dp+1] = src[sp+1]; // G
        dst[dp+2] = src[sp+2]; // B
        dst[dp+3] = src[sp+3]; // A
      }

      sp += lineOffset * 4;
      dp += lineOffset * 4;
    }

    // Next dirty region
    iDirtyRects++;
  }*/
}

  }
}