/* Copyright (c) 2001-2009 by SoftIntegration, Inc. All Rights Reserved */
#include <ch.h>
#include <stdio.h>

typedef return_type2 (*FUNC)(); /* function pointer type */
static ChInterp_t interp;
static void *setDiffFunc_chdl_funptr;

static return_type2 setDiffFunc_chdl_funarg0() {
  return_type2 retval;

  Ch_CallFuncByAddr(interp, setDiffFunc_chdl_funptr, &retval);
  return retval;
}

static return_type2 setDiffFunc_chdl_funarg1(data_type11 num) {
  return_type2 retval;

  Ch_CallFuncByAddr(interp, setDiffFunc_chdl_funptr, &retval, num);
  return retval;
}

static return_type2 setDiffFunc_chdl_funarg2(data_type21 num1, data_type22 num2) {
  return_type2 retval;

  Ch_CallFuncByAddr(interp, setDiffFunc_chdl_funptr, &retval, num1, num2);
  return retval;
}

static return_type2 diff_arg_chdl_funarg3(int num, ...) {
  return_type2 retval;
  ChVaList_t ap, ap_ch;
  int x;
  double d;

  va_start(ap, num1);
  ap_ch = Ch_VarArgsCreate(interp);
  if(num1 > 1) {
    x = va_arg(ap, int);
    Ch_VarArgsAddArg(interp, &ap_ch, CH_INTTYPE, x);
  }
  if(num1 == 2) {
    d = va_arg(ap, double);
    Ch_VarArgsAddArg(interp, &ap_ch, CH_DOUBLETYPE, d);
  }
  Ch_CallFuncByAddr(interp, diff_arg_chdl_funptr, &retval, num1, ap_ch);
  Ch_VarArgsDelete(interp, ap_ch);
  return retval;
}

/* ... for other possible prototypes ... */

EXPORTCH return_type setDiffFunc_chdl(void *varg) {
  ChVaList_t ap;
  return_type retval;
  data_type arg1;
  int argnum;
  FUNC handle_ch, handle_c = NULL;

  Ch_VaStart(interp, ap, varg);
  arg1 = Ch_VaArg(interp, ap, data_type);

  /* get argument number of the Ch function */
  argnum = Ch_VaFuncArgNum(interp, ap);

  /* get the Ch function pointer */
  handle_ch = Ch_VaArg(interp, ap, FUNC);

  if(handle_ch != NULL) {
    /* replace the Ch array with the proper C one,
       the NULL pointer can't be replaced */
    if(argnum == 0) {
      handle_c = setDiffFunc_chdl_funarg0;
    }
    else if(argnum == 1) {
      handle_c = setDiffFunc_chdl_funarg1;
    }
    else if(argnum == 2) {
      handle_c = setDiffFunc_chdl_funarg2;
    }
    else if(argnum == INT_MAX) {
      handle_c = (pf_t)diff_arg_chdl_funarg3;
    }
    /* ... */
    setDiffFunc_chdl_funptr = (void *)handle_ch;
  }

  /* pass the proper C function pointer 
     instead of the Ch one, 
     the NULL pointer will not be replaced.
     If the Ch function has more than 2 argument, 
     except for INT_MAX, NULL will be passed. */
  retval = setDiffFunc(arg1, handle_c);

  Ch_VaEnd(interp, ap);
  return retval;
}

