/* Swfdec
 * Copyright (C) 2010 Bob Jiang <bobjiang@msn.com>
 *
 * This library 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.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 */

#include "swfdec_np_scripting.h"


/*** GTK-DOC ***/

/**
 * SECTION:SwfdecPlayerScripting
 * @title: SwfdecPlayerScripting
 * @short_description: integration with external scripts
 *
 * The @SwfdecPlayerScripting object is used to implement the ExternalInterface
 * object inside Flash. By providing your own custom implementation of this 
 * object using swfdec_player_set_scripting(), you can allow a Flash file to 
 * call your own custom functions. And you can call functions inside the Flash 
 * player.
 *
 * The functions are emitted using a custom XML format. The same format is used
 * to call functions in the Flash player. FIXME: describe the XML format in use. 
 */

/**
 * SwfdecPlayerScripting:
 *
 * The object you have to create.
 */

/**
 * SwfdecPlayerScriptingClass:
 * @js_get_id: optional function. Returns the string by which the scripting 
 *             object can reference itself in calls to the @js_call function.
 * @js_call: optional function. Emits Javascript code that you have to 
 *           interpret. You must also implement the @js_get_id function to make
 *           this function work. The return value must be in the custom XML 
 *           format.
 * @xml_call: optional function. If the Javascript functions aren't implemented,
 *            an xml format is used to encode the function call and the call is
 *            then done using this function. The return value is to be provided
 *            in the same custom XML format.
 *
 * You have to implement the virtual functions in this object to get a working
 * scripting implementation.
 */

/*** SWFDEC_PLAYER_SCRIPTING ***/

G_DEFINE_TYPE (SwfdecNPScripting, swfdec_np_scripting, SWFDEC_TYPE_PLAYER_SCRIPTING)

struct CallbackData
{
  SwfdecAsObject * obj;
  SwfdecAsObject *fun;
};

gboolean plugin_evaluate(NPP npp, NPObject* obj, NPString *script,
                  NPVariant *result);
NPIdentifier plugin_get_string_identifier(const NPUTF8 *name);

gboolean		swfdec_np_scripting_addCallback(SwfdecPlayerScripting *scripting,
					 const char * name,SwfdecAsObject * obj,SwfdecAsObject *fun)
{ 
  NPIdentifier id;
  SwfdecAsContext *context;
  struct CallbackData * pData=(struct CallbackData *)g_malloc(sizeof(struct CallbackData));
  context = swfdec_gc_object_get_context (fun->relay);
  
  printf("----Context %p , Context Global %p\n",context,context->global);
  printf("----swfdec_np_scripting_addCallback name %s obj %p fun %p relay %p\n",name,obj,fun,fun->relay);
  if(!pData)
    return 0;
   
  pData->obj=obj;
  pData->fun=fun;
  id=plugin_get_string_identifier(name);
  g_hash_table_insert(SWFDEC_NP_SCRIPTING(scripting)->scripting_callbacks,id,pData);
  
  return 1;
}

struct obj_each_prop_data
{
  SwfdecPlayerScripting *scripting;
  GString * str;
};

static gboolean obj_each_prop(SwfdecAsObject *object, 
    const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
{
  GString * url=0;
  SwfdecPlayerScripting *scripting=0;
  if(SWFDEC_AS_VARIABLE_HIDDEN&flags)
    return 1;
  url=((struct obj_each_prop_data *)data)->str;
  scripting=((struct obj_each_prop_data *)data)->scripting;
  
  if(url->str[url->len-1]!='{')
    g_string_append (url,",");
  g_string_append_printf(url,"\"%s\":\"%s\"",variable,
    swfdec_as_value_to_string(&(SWFDEC_NP_SCRIPTING(scripting)->player->context),*value));
  
  return 1;
}

gboolean		swfdec_np_scripting_call(SwfdecPlayerScripting *scripting,
					 const char * name,guint argc, SwfdecAsValue *argv,SwfdecAsValue *ret)
{
  guint i;
  NPString script1;
  NPVariant result;
  GString * url=0;
  
  printf("----swfdec_np_scripting_call name %s \n",name);
  
  /*Fixme: Marshal Args*/
  url=g_string_new(name);
  
  g_string_append (url,"(");
  for(i=0;i<argc;i++)
  {
    printf("--------%d:type %d \n",i,SWFDEC_AS_VALUE_GET_TYPE(argv[i]));
    switch(SWFDEC_AS_VALUE_GET_TYPE(argv[i]))
    {
      case  SWFDEC_AS_TYPE_NULL:
      case  SWFDEC_AS_TYPE_BOOLEAN:
      case  SWFDEC_AS_TYPE_INT:
      case  SWFDEC_AS_TYPE_NUMBER:
      case  SWFDEC_AS_TYPE_STRING:
        g_string_append (url,
          swfdec_as_value_to_string(&(SWFDEC_NP_SCRIPTING(scripting)->player->context),argv[i]));
        break;
      case  SWFDEC_AS_TYPE_OBJECT:
        {
          SwfdecAsObject *pObj=swfdec_as_value_to_object	(&(SWFDEC_NP_SCRIPTING(scripting)->player->context),
						 argv[i]);
					struct obj_each_prop_data each_data={scripting,url};	 
					
					g_string_append (url,"{");
					
          swfdec_as_object_foreach(pObj,obj_each_prop,&each_data);
          g_string_append (url,"}");
        }
        break;
      case  SWFDEC_AS_TYPE_MOVIE:
        break;
      case  SWFDEC_AS_TYPE_UNDEFINED:
      default:
        break;
    }
    if(i+1<argc)
      g_string_append (url,",");
  }
  g_string_append (url,");");
  
  script1.utf8length = strlen(url->str);
  script1.utf8characters = strdup(url->str);
  
  printf("----swfdec_np_scripting_call script %s \n",url->str);
  plugin_evaluate(SWFDEC_NP_SCRIPTING(scripting)->instance, SWFDEC_NP_SCRIPTING(scripting)->windowObj, &script1, &result);
  
  g_string_free(url,1);
  
  /*Fixme: Marshal Return*/
  SWFDEC_AS_VALUE_SET_BOOLEAN (ret, 1);
  return 1;
}

static void each_mark_callback(gpointer key,gpointer value,gpointer user_data)
{
  struct CallbackData * pData=(struct CallbackData *)value;
  if(!value)
    return;
  
  swfdec_as_object_mark(pData->obj);
  swfdec_as_object_mark(pData->fun);
}

void    swfdec_np_scripting_mark(SwfdecPlayerScripting *scripting)
{
  GHashTable *		scripting_callbacks=SWFDEC_NP_SCRIPTING(scripting)->scripting_callbacks;
  printf("----------swfdec_np_scripting_mark\n");
  g_hash_table_foreach(scripting_callbacks,each_mark_callback,0);
}

gboolean    swfdec_np_scripting_set_np(SwfdecNPScripting *scripting,NPP i,NPObject *w,SwfdecPlayer *player)
{
  scripting->instance=i;
  scripting->windowObj=w;
  scripting->player=player;
  
  printf("----swfdec_np_scripting_set_np instance %p windowObj %p player %p\n",i,w,player);
  return 1;
}

gboolean    swfdec_np_scripting_has_method(SwfdecNPScripting *scripting,NPIdentifier name)
{
  return g_hash_table_lookup(SWFDEC_NP_SCRIPTING(scripting)->scripting_callbacks,name)!=0;
}

gboolean    swfdec_np_scripting_has_invoke(SwfdecNPScripting *scripting,
            NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
  SwfdecAsValue ret;
  guint i;
  SwfdecAsValue * pValues=0;
  SwfdecAsContext *context=0;
  
  struct CallbackData * pData=
    (struct CallbackData *)g_hash_table_lookup(SWFDEC_NP_SCRIPTING(scripting)->scripting_callbacks,name);
  printf("--------swfdec_np_scripting_has_invoke enter\n");
  if(!pData || !SWFDEC_IS_AS_FUNCTION(pData->fun->relay))
    return 0;
  /*Fixme: Marshal Args*/
  if(argCount>0)
    pValues=(SwfdecAsValue *)g_malloc0(sizeof(SwfdecAsValue)*argCount);
  for(i=0;i<argCount;i++)
	{
		if(args[i].type==NPVariantType_String)
		{
			printf("-----JS Var String %s, len %d\n",
				args[i].value.stringValue.utf8characters,args[i].value.stringValue.utf8length);
			SWFDEC_AS_VALUE_SET_STRING(&pValues[i],
			  swfdec_as_context_get_string(&SWFDEC_NP_SCRIPTING(scripting)->player->context,
			    args[i].value.stringValue.utf8characters));
		}
		else if(args[i].type==NPVariantType_Int32)
		{
			printf("-----JS Var Int %d\n",args[i].value.intValue);
			pValues[i]=swfdec_as_value_from_integer(
			  &(SWFDEC_NP_SCRIPTING(scripting)->player->context),args[i].value.intValue);
		}
		else if(args[i].type==NPVariantType_Double)
		{
			printf("-----JS Var Double %f\n",args[i].value.doubleValue);
			pValues[i]=swfdec_as_value_from_number(
			  &(SWFDEC_NP_SCRIPTING(scripting)->player->context),args[i].value.doubleValue);
		}
		else if(args[i].type==NPVariantType_Bool)
		{
			printf("-----JS Var Boolean %d\n",args[i].value.boolValue);
			SWFDEC_AS_VALUE_SET_BOOLEAN(&pValues[i],args[i].value.boolValue);
		}
		
		
	}
  
  context = swfdec_gc_object_get_context (pData->fun->relay);
  
  printf("----Context %p , Context Global %p\n",context,context->global);
  
  printf("--------swfdec_np_scripting_has_invoke call,fun %p,obj %p,relay %p\n",pData->fun,pData->obj,pData->fun->relay);
  swfdec_player_call(SWFDEC_NP_SCRIPTING(scripting)->player, pData->obj,pData->fun,argCount,pValues, &ret);
  
  /*Fixme: Marshal Return*/
  if(pValues)
    g_free(pValues);
  if(result)
	{
		//result->type=NPVariantType_Void;
		result->type = NPVariantType_Int32;
		result->value.intValue = 0;
	}
  return 1;
}

static void
swfdec_np_scripting_class_init (SwfdecNPScriptingClass *klass)
{
  klass->script_class.addCallback=swfdec_np_scripting_addCallback;
  klass->script_class.call=swfdec_np_scripting_call;
  klass->script_class.mark=swfdec_np_scripting_mark;
}

static void destroy_value(gpointer data)
{
  g_free(data);
}

static void
swfdec_np_scripting_init (SwfdecNPScripting *np_scripting)
{
  np_scripting->scripting_callbacks = g_hash_table_new_full  (g_direct_hash, g_direct_equal,0,destroy_value);
  np_scripting->instance=0;
  np_scripting->windowObj=0;
}

