/*******************************************************************************
 *  RProxy: Connector implementation between application and R language
 *  Copyright (C) 1999--2006 Thomas Baier
 *
 *  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 along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 *
 ******************************************************************************/

#define NONAMELESSUNION
#include <windows.h>
#include <stdio.h>
#include <config.h>
#include <Rversion.h>
#include "bdx.h"
#include "bdx_util.h"
#include "bdx_com.h"
#include "SC_proxy.h"
#include "rproxy.h"
#include "rproxy_impl.h"
#include <assert.h>
#include <stdlib.h>

#include <Rinternals.h>
#include <Graphics.h>
#include <Rdevices.h>

/* static connector information */
#define CONNECTOR_NAME          "R Statistics Interpreter Connector"
#define CONNECTOR_DESCRIPTION   "Implements abstract connector interface to R"
#define CONNECTOR_COPYRIGHT     "(C) 1999-2006, Thomas Baier"
#define CONNECTOR_LICENSE       "GNU Library General Public License version 2 or greater"
#define CONNECTOR_VERSION_MAJOR "1"
#define CONNECTOR_VERSION_MINOR "2"

/* interpreter information here at the moment until I know better... */
#define INTERPRETER_NAME        "R"
#define INTERPRETER_DESCRIPTION "A Computer Language for Statistical Data Analysis"
#define INTERPRETER_COPYRIGHT   "(C) R Development Core Team"
#define INTERPRETER_LICENSE     "GNU General Public License version 2 or greater"

typedef enum
{
  ps_none,
  ps_initialized,
  ps_reuser
} R_Proxy_Object_State;

SC_CharacterDevice* __output_device;
struct __tag_graphics_device __graphics_device;

typedef struct _R_Proxy_Object_Impl
{
  SC_Proxy_Object_Vtbl* vtbl;
  R_Proxy_Object_State state;
  int                   ref_count;
} R_Proxy_Object_Impl;

/* 01-01-25 | baier | new parameters */
/* 06-08-20 | baier | new name, restructured */
int R_Proxy_Graphics_Driver_CB (R_Proxy_Graphics_CB* pDD,
				char* pDisplay,
				double pWidth,
				double pHeight,
				double pPointSize,
				Rboolean pRecording,
				int pResize);

int SYSCALL R_get_version (R_Proxy_Object_Impl* object,unsigned long* version)
{
  if ((object == NULL)
      || (version == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  *version = SC_PROXY_INTERFACE_VERSION;

  return SC_PROXY_OK;
}

extern struct _R_Proxy_init_parameters g_R_Proxy_init_parameters;

/* 00-02-18 | baier | R_init(), R_Proxy_init() now take parameter-string */
/* 04-10-20 | baier | special state to reuse a running R (rgui) */
/* 06-06-18 | baier | parse parameters if state is ps_reuser */
int SYSCALL R_init (R_Proxy_Object_Impl* object,char const* parameters)
{
  int lRc = SC_PROXY_ERR_UNKNOWN;

  if (object == NULL) {
    return SC_PROXY_ERR_INVALIDARG;
  }

  /* parse parameters */
  R_Proxy_parse_parameters(parameters,&g_R_Proxy_init_parameters);

  if(object->state != ps_none) {
    return SC_PROXY_ERR_INITIALIZED;
  }

  if(g_R_Proxy_init_parameters.reuseR) {
    RPROXY_TRACE(printf("R_init: re-use R for proxy DLL (inproc RCOM)\n"));
    object->state = ps_reuser;
    return SC_PROXY_OK;
  }
  lRc = R_Proxy_init (parameters);

  if(lRc == SC_PROXY_OK) {
    object->state = ps_initialized;
  }

  return lRc;
}

/* 04-10-20 | baier | special state to reuse a running R (rgui) */
int SYSCALL R_terminate (R_Proxy_Object_Impl* object)
{
  int lRc = SC_PROXY_ERR_UNKNOWN;

  if (object == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if(object->state == ps_reuser) {
    return SC_PROXY_OK;
  }

  if (object->state != ps_initialized)
    {
      return SC_PROXY_ERR_NOTINITIALIZED;
    }

  lRc = R_Proxy_term ();

  if (lRc == SC_PROXY_OK)
    {
      object->state = ps_none;
    }

  return lRc;
}

int SYSCALL R_retain (R_Proxy_Object_Impl* object)
{
  if (object == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  assert (object->ref_count > 0);

  (object->ref_count)++;

  return SC_PROXY_OK;
}

/* 00-06-19 | baier | release graphics device */
/* 06-05-17 | baier | changed layout of __graphics_device */
int SYSCALL R_release (R_Proxy_Object_Impl* object)
{
  if (object == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  /* reference count must not be 0 here */
  assert (object->ref_count > 0);

  (object->ref_count)--;

  if (object->ref_count > 0)
    {
      return SC_PROXY_OK;
    }

  if (object->state != ps_none)
    {
      return SC_PROXY_ERR_INITIALIZED;
    }

  if (__output_device)
    {
      __output_device->vtbl->release (__output_device);
      __output_device = NULL;
    }

  if(HASGFXDEV()) {
    GFXDEV()->vtbl->release (GFXDEV());
    CLRGFXDEV();
  }

  free (object);

  return SC_PROXY_OK;
}

/* 04-10-20 | baier | special state to reuse a running R (rgui) */
int SYSCALL R_set_symbol (R_Proxy_Object_Impl* object,
			  char const* symbol,
			  BDX_Data* data)
{
  int lRc = 0;

  /* check parameters */
  if ((object == NULL)
      || (symbol == NULL)
      || (strlen (symbol) == 0)
      || (data == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (data->version != BDX_VERSION)
    {
      RPROXY_TRACE(printf("R_set_symbol: BDX_Data with version %d, expected %d\n",
			  data->version,BDX_VERSION));
      return SC_PROXY_ERR_INVALIDFORMAT;
    }

  if ((object->state != ps_initialized) && (object->state != ps_reuser)) {
    return SC_PROXY_ERR_NOTINITIALIZED;
  }

  lRc = R_Proxy_set_symbol (symbol,data);

  return lRc;
}

/* 04-10-20 | baier | special state to reuse a running R (rgui) */
int SYSCALL R_get_symbol (R_Proxy_Object_Impl* object,
			  char const* symbol,
			  BDX_Data** data)
{
  int lRc = 0;

  /* check parameters */
  if ((object == NULL)
      || (symbol == NULL)
      || (strlen (symbol) == 0)
      || (data == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if ((object->state != ps_initialized) && (object->state != ps_reuser)) {
    return SC_PROXY_ERR_NOTINITIALIZED;
  }

  lRc = R_Proxy_get_symbol (symbol,data);
  if (lRc == SC_PROXY_OK)
    {
      (*data)->version = BDX_VERSION;
    }

  return lRc;
}

/* 04-10-20 | baier | special state to reuse a running R (rgui) */
int SYSCALL R_evaluate (R_Proxy_Object_Impl* object,
			char const* command,
			BDX_Data** data )
{
  if ((object == NULL)
      || (command == NULL)
      || (strlen (command) == 0)
      || (data == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if ((object->state != ps_initialized) && (object->state != ps_reuser)) {
    return SC_PROXY_ERR_NOTINITIALIZED;
  }

  return R_Proxy_evaluate (command,data);
}

/* 04-10-20 | baier | special state to reuse a running R (rgui) */
int SYSCALL R_evaluate_noreturn (R_Proxy_Object_Impl* object,
				 char const* command)
{
  if ((object == NULL)
      || (command == NULL)
      || (strlen (command) == 0))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if ((object->state != ps_initialized) && (object->state != ps_reuser)) {
    return SC_PROXY_ERR_NOTINITIALIZED;
  }

  return R_Proxy_evaluate_noreturn (command);
}


int SYSCALL R_query_types (R_Proxy_Object_Impl* object,
			   long* type_mask)
{
  if ((object == NULL)
      || (type_mask == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  *type_mask = (SC_TM_SCALAR_ALL | SC_TM_ARRAY_ALL | SC_TM_VECTOR_ALL);

  return SC_PROXY_OK;
}


int SYSCALL R_query_ops (R_Proxy_Object_Impl* object,
			 long* op_mask)
{
  if ((object == NULL)
      || (op_mask == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  *op_mask = 0;

  return SC_PROXY_ERR_NOTIMPL;
}

int SYSCALL R_free_data_buffer (R_Proxy_Object_Impl* object,
				BDX_Data* data)
{
  if ((data == NULL)
      || (object == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (data->version != BDX_VERSION)
    {
      return SC_PROXY_ERR_INVALIDFORMAT;
    }

  assert (data != NULL);
  assert (data->version == BDX_VERSION);

  bdx_free (data);
  /*  free (data); */

  return SC_PROXY_OK;
}

/* 00-06-19 | baier | only set if version matches */
int SYSCALL R_set_output_device (R_Proxy_Object_Impl* object,
				 struct _SC_CharacterDevice* device)
{
  unsigned long lCurrentVersion = 0;

  if (object == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (__output_device)
    {
      __output_device->vtbl->release (__output_device);
      __output_device = NULL;
    }

  if (device == NULL)
    {
      return SC_PROXY_OK;
    }

  if (device->vtbl->get_version (device,
				 &lCurrentVersion) != SC_PROXY_OK)
    {
      return SC_PROXY_ERR_UNKNOWN;
    }

  if (lCurrentVersion != SC_CHARACTERDEVICE_VERSION)
    {
      return SC_PROXY_ERR_INVALIDINTERFACEVERSION;
    }

  __output_device = device;
  __output_device->vtbl->retain (device);

  return SC_PROXY_OK;
}

int SYSCALL R_query_info (R_Proxy_Object_Impl* object,
			  long main_key,
			  long sub_key,
			  char const** information)
{
  if ((object == NULL)
      || (information == NULL))
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  switch (main_key)
    {
    case SC_INFO_MAIN_CONNECTOR:
      switch (sub_key)
	{
	case SC_INFO_SUB_NAME:
	  *information = INTERPRETER_NAME;
	  break;
	case SC_INFO_SUB_DESCRIPTION:
	  *information = INTERPRETER_DESCRIPTION;
	  break;
	case SC_INFO_SUB_COPYRIGHT:
	  *information = INTERPRETER_COPYRIGHT;
	  break;
	case SC_INFO_SUB_LICENSE:
	  *information = INTERPRETER_LICENSE;
	  break;
	case SC_INFO_SUB_MINORVERSION:
	  *information = R_MINOR;
	  break;
	case SC_INFO_SUB_MAJORVERSION:
	  *information = R_MAJOR;
	  break;
	default:
	  *information = "";
	}
      break;
    case SC_INFO_MAIN_INTERPRETER:
      switch (sub_key)
	{
	case SC_INFO_SUB_NAME:
	  *information = CONNECTOR_NAME;
	  break;
	case SC_INFO_SUB_DESCRIPTION:
	  *information = CONNECTOR_DESCRIPTION;
	  break;
	case SC_INFO_SUB_COPYRIGHT:
	  *information = CONNECTOR_COPYRIGHT;
	  break;
	case SC_INFO_SUB_LICENSE:
	  *information = CONNECTOR_LICENSE;
	  break;
	case SC_INFO_SUB_MINORVERSION:
	  *information = CONNECTOR_VERSION_MINOR;
	  break;
	case SC_INFO_SUB_MAJORVERSION:
	  *information = CONNECTOR_VERSION_MAJOR;
	  break;
	default:
	  *information = "";
	}
      break;
    default:
      *information = "";
    }

  return SC_PROXY_OK;
}


/* 06-05-17 | baier | changed layout of __graphics_device */
/* 06-08-20 | baier | use R_Proxy_Graphics_CB, only add device once  */
int SYSCALL R_set_graphics_device (struct _SC_Proxy_Object* object,
				   struct _SC_GraphicsDevice* device)
{
  unsigned long lCurrentVersion = 0;
  static GEDevDesc* lDD = NULL;

  if (object == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (HASGFXDEV()) {
    /* remove the graphics device from the set of drivers */
    GFXDEV()->vtbl->release (GFXDEV());
    CLRGFXDEV();
  }

  if (device == NULL)
    {
      return SC_PROXY_OK;
    }

  if (device->vtbl->get_version (device,
				 &lCurrentVersion) != SC_PROXY_OK)
    {
      return SC_PROXY_ERR_UNKNOWN;
    }

  if (lCurrentVersion != SC_GRAPHICSDEVICE_VERSION)
    {
      return SC_PROXY_ERR_INVALIDINTERFACEVERSION;
    }

  SETGFXDEV(device);
  GFXDEV()->vtbl->retain (GFXDEV());

  /* add the graphics device to the set of drivers */
  if(!lDD) {
    R_Proxy_Graphics_CB* lDev =
      (R_Proxy_Graphics_CB*) calloc (1,sizeof (R_Proxy_Graphics_CB));

    /* Do this for early redraw attempts */
    DEVDESC(lDev)->displayList = R_NilValue;
    /* Make sure that this is initialised before a GC can occur.
     * This (and displayList) get protected during GC
     */
    DEVDESC(lDev)->savedSnapshot = R_NilValue;
    R_Proxy_Graphics_Driver_CB (lDev,
				"ActiveXDevice 1",
				100.0,
				100.0,
				10.0,
				0,
				0);
    gsetVar(install(".Device"),
	    mkString("ActiveXDevice 1"), R_BaseEnv);
    lDD = GEcreateDevDesc(DEVDESC(lDev));
    addDevice((DevDesc*) lDD);
    GEinitDisplayList(lDD);
  }
  return SC_PROXY_OK;
}

/* global object table */
SC_Proxy_Object_Vtbl global_proxy_object_vtbl =
{
  (SC_PROXY_GET_VERSION) R_get_version,
  (SC_PROXY_INIT) R_init,
  (SC_PROXY_TERMINATE) R_terminate,
  (SC_PROXY_RETAIN) R_retain,
  (SC_PROXY_RELEASE) R_release,
  (SC_PROXY_SET_SYMBOL) R_set_symbol,
  (SC_PROXY_GET_SYMBOL) R_get_symbol,
  (SC_PROXY_EVALUATE) R_evaluate,
  (SC_PROXY_EVALUATE_NORETURN) R_evaluate_noreturn,
  (SC_PROXY_QUERY_TYPES) R_query_types,
  (SC_PROXY_QUERY_OPS) R_query_ops,
  (SC_PROXY_FREE_DATA_BUFFER) R_free_data_buffer,
  (SC_PROXY_SET_CHARACTERDEVICE) R_set_output_device,
  (SC_PROXY_QUERY_INFO) R_query_info,
  (SC_PROXY_SET_GRAPHICSDEVICE) R_set_graphics_device
};

int SYSCALL EXPORT SC_Proxy_get_object (SC_Proxy_Object** obj,
					unsigned long version)
{
  R_Proxy_Object_Impl* proxy_object = NULL;

  /* break to debugger */
  if(getenv("DEBUG_RPROXY")) {
    OutputDebugString("Debugging of rproxy.dll initiated, breaking to debugger\n");
    DebugBreak();
  } else {
    OutputDebugString("No Debugging of rproxy\n");
  }

  if (obj == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (version != SC_PROXY_INTERFACE_VERSION)
    {
      return SC_PROXY_ERR_INVALIDINTERFACEVERSION;
    }

  proxy_object = (R_Proxy_Object_Impl*) malloc (sizeof (R_Proxy_Object_Impl));

  proxy_object->vtbl = &global_proxy_object_vtbl;
  proxy_object->state = ps_none;
  proxy_object->ref_count = 1;

  *obj = (SC_Proxy_Object*) proxy_object;

  return SC_PROXY_OK;
}


/* global object table */
BDX_Vtbl global_bdx_object_vtbl =
{
  (BDX_FREE) bdx_free,
  (BDX_TRACE) bdx_trace,
  (BDX_VARIANT2BDX) Variant2BDX,
  (BDX_BDX2VARIANT) BDX2Variant
};

int SYSCALL EXPORT BDX_get_vtbl (BDX_Vtbl** obj,
				   unsigned long version)
{
  if (obj == NULL)
    {
      return SC_PROXY_ERR_INVALIDARG;
    }

  if (version != BDX_VTBL_VERSION)
    {
      return SC_PROXY_ERR_INVALIDINTERFACEVERSION;
    }

  *obj = &global_bdx_object_vtbl;

  return SC_PROXY_OK;
}


syntax highlighted by Code2HTML, v. 0.9.1