/*
-----------------------------------------------------------------------------
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 "embeddedWebNavigatorScolExternalCallHandler.h"
#include "embeddedWebNavigatorJSFunction.h"
#include "embeddedWebNavigatorClient.h"
#include <sstream>

namespace Scol
{
  namespace EmbeddedWebNavigator
  {

ScolExternalCallHandler::ScolExternalCallHandler(CefRefPtr<WebNavigatorClient>& parentWebNavigatorClientInstance, const ScolWindowHandle& scolMainWindow) : scolMainWindowHandle(scolMainWindow)
{
  parentWebNavigator = parentWebNavigatorClientInstance->GetParentWebNavigator();
}

ScolExternalCallHandler::ScolExternalCallHandler() : scolMainWindowHandle(0)
{
  // Forbiden
}

bool ScolExternalCallHandler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
  if((name == "scolExternalCall") || (name == "scolExternalCallStr"))
  {
    // The function must have at least one parameter, the name of the scol function to call
    if(arguments.empty())
    {
      // Throw JS exception 
      exception = "Invalid parameters for scolExternalCall function!";
      return true;
    }
    else
    {
      // The first argument must be a string
      if(!arguments[0]->IsString())
      {
        // Throw JS exception
        exception = "The first parameters for scolExternalCall must be a String!";
        return true;
      }
      else
      {
        // Store function name
        std::string scolFunctionName = arguments[0]->GetStringValue();

        // Iterate other arguments
        std::vector<std::string> scolParameters;
        for(unsigned int currentArg = 1; currentArg < arguments.size(); currentArg++)
        {
          scolParameters.push_back(ConvertV8ParameterToString(arguments[currentArg]));
        }

        // Create function object
        WebNavigatorJSFunction functionCalled(scolFunctionName, scolParameters);

        if(name == "scolExternalCall")
        {
          // Send message to scol
          SendMessage(scolMainWindowHandle, WEB_NAVIGATOR_JS_ON_EXTERNAL_CALL_WINMSG, (int)parentWebNavigator, (LPARAM)&functionCalled);

          // Return information to JS
          switch(functionCalled.returnValue)
          {
          case WN_JS_FUNCTION_RETURN_VALUE_UNDEFINED:
            exception = "The cb function was not setted in scol code!";
            break;
          case WN_JS_FUNCTION_RETURN_VALUE_FALSE:
            retval = CefV8Value::CreateBool(false);
            break;
          case WN_JS_FUNCTION_RETURN_VALUE_TRUE:
            retval = CefV8Value::CreateBool(true);
            break;
          }
        }
        else if (name == "scolExternalCallStr")
        {
          // Send message to scol
          SendMessage(scolMainWindowHandle, WEB_NAVIGATOR_JS_ON_EXTERNAL_STR_CALL_WINMSG, (int)parentWebNavigator, (LPARAM)&functionCalled);

          // Return information to JS
          switch(functionCalled.returnValue)
          {
          case WN_JS_FUNCTION_RETURN_VALUE_UNDEFINED:
            exception = "The cb function was not setted in scol code!";
            break;

          default:
            retval = CefV8Value::CreateString(functionCalled.returnValueStr.c_str());
          }
        }
        return true;
      }
    }
  }

  // Function called not handled, return "undefined" to JS
  return false;
}

std::string ScolExternalCallHandler::ConvertV8ParameterToString(CefRefPtr<CefV8Value> argument)
{
  // Return value
  std::string returnValue("");

  // Convert to string
  if(argument->IsUndefined())
  {
    // No conversion?
  }
  else
  {
    // Array conversion
    if(argument->IsArray())
    {
      returnValue = ConvertV8ArrayToString(argument);
    }

    // bool conversion
    if(argument->IsBool())
    {
      if(argument->GetBoolValue())
        returnValue = "1";
      else
        returnValue = "0";
    }

    // date conversion
    if(argument->IsDate())
    {
      time_t time = argument->GetDateValue().GetTimeT();
      returnValue = ctime(&time);
    }

    // double conversion
    if(argument->IsDouble())
    {
      std::ostringstream os;
      os << argument->GetDoubleValue();
      returnValue = os.str();
    }

    // function conversion
    if(argument->IsFunction())
    {
      // No conversion?
    }

    // int conversion
    if(argument->IsInt())
    {
      std::ostringstream os;
      os << argument->GetIntValue();
      returnValue = os.str();
    }

    // null conversion
    if(argument->IsNull())
    {
      // No conversion?
    }

    // object conversion
    if(argument->IsObject())
    {
      // No conversion?
    }

    // string conversion
    if(argument->IsString())
    {
      returnValue = argument->GetStringValue();
    }
  }

  // Converted value
  return returnValue;
}

std::string ScolExternalCallHandler::ConvertV8ArrayToString(CefRefPtr<CefV8Value> argument)
{
  // Return value
  std::string returnValue("");

  // Convert to string
  if(!argument->IsUndefined())
  {
    if(argument->IsArray())
    {
      std::string elemValue = "";
      int nbElems = argument->GetArrayLength();
      for(int i = 0; i < nbElems; i++)
      {
        // Convert the array element to string
        elemValue = ConvertV8ParameterToString(argument->GetValue(i));

        // Replace special characters
        /*
          char(0) : \z
          char(10) : \n
          ' ' : \space
          \ : \\
          other special : \code (3 decimal digits) 
        */
        // TODO

        // Add to the final string
        if(i == 0)
        {
          returnValue = elemValue;
        }
        else
        {
          returnValue = returnValue +" "+ elemValue;
        }
      }
    }
  }

  // Converted value
  return returnValue;
}

  }
}