// test.c : Defines the exported functions for the DLL application.
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <math.h>
#include "maplec.h"

#if !defined(EXPORT_DECL)
#  ifdef _MSC_VER
#    define EXPORT_DECL __declspec(dllexport)
#  else
#    define EXPORT_DECL
#  endif /* _MSC_VER */
#endif /* EXT_DECL */

#define arrsize(x) sizeof(x)/sizeof(x[0])

typedef struct _TFuncCache
{
  const char *Name;
  ALGEB DirectFastPointer;
} TFuncCache;
//names of fuctions called from main maple code
static TFuncCache FuncNames[]= {{"MainF"}, {"MainFDerivativeA"}, {"MainFDerivativeY"}, {"MainFDerivative2AA"}, 
  {"MainFDerivative2AY"}, {"MainFDerivative2YY"}, {"MainFDerivative3AAA"}, {"MainFDerivative3AAY"}, {"MainFDerivative3AYY"}, 
  {"MainFDerivative3YYY"}, {"MainFDerivative4AAAA"}, {"MainFDerivative4AAAY"}, {"MainFDerivative4AAYY"}, 
  {"MainFDerivative4AYYY"}, {"MainFDerivative4YYYY"}};

static FLOAT64 UniformUnitRandom(MKernelVector ExternalKernel)
{
  FLOAT64 res;
  //
  
  res= ((FLOAT64)rand())/(RAND_MAX + 1.0);
  if (res<0.0||res>=1.0)
    MaplePrintf(ExternalKernel, "God damn!\n");
   return res;
}


static void InitFuncPointers(MKernelVector ExternalKernel)
{
  int i;
  ALGEB f;
  char expr[1024]; //general expression
  //
  for(i=0;i<sizeof(FuncNames)/sizeof(FuncNames[0]);i++)
  {
    f=ToMapleName(ExternalKernel, (char *)FuncNames[i].Name, TRUE);
    if( !f || !IsMapleProcedure(ExternalKernel,f) ) 
    {
    #define _CRT_SECURE_NO_WARNINGS
      sprintf(expr, "unable to compute function by name %s", FuncNames[i].Name);
    #undef _CRT_SECURE_NO_WARNINGS
      MapleRaiseError(ExternalKernel, expr);
      return;
    }
    FuncNames[i].DirectFastPointer=f;
  }
}

static void ice_TableUtils_GetRTableSettings(MKernelVector AExternalKernel, RTableSettings *rts)
{
  RTableGetDefaults(AExternalKernel, rts);
  rts->num_dimensions = 2;
  rts->subtype = RTABLE_MATRIX;
  rts->data_type = RTABLE_FLOAT64;
  rts->order = RTABLE_C;
  rts->foreign=0;
  return;
}

static ALGEB ice_TableUtils_CreateRTable(MKernelVector AExternalKernel, int raws, int columns)
{
  RTableSettings rts;
  M_INT bounds[4];
  //
  ice_TableUtils_GetRTableSettings(AExternalKernel, &rts);
  bounds[0] = 1;
  bounds[1] = raws;
  bounds[2] = 1;
  bounds[3] = columns;
  //
  return RTableCreate(AExternalKernel,&rts,NULL,bounds);
}

static FLOAT64 MyExternalFunctionEval(MKernelVector ExternalKernel, FLOAT64 *args, const int FuncNo)
{
  return EvalhfMapleProc(ExternalKernel, FuncNames[FuncNo].DirectFastPointer, 3, args);
}

static ALGEB rt;
static FLOAT64  *data;
static long long code;

static long long GetDataCode()
{
  long long res;
  int i;
  //
  res=0LL;
  for(i=0;i<500;i++)
    res^=(long long)data[i];
  return res;
}

EXPORT_DECL ALGEB M_DECL InitInternal(MKernelVector ExternalKernel, ALGEB *args)
{
  rt=ice_TableUtils_CreateRTable(ExternalKernel, 100, 5);
  data = (FLOAT64*)RTableDataBlock(ExternalKernel,rt);
  //code=GetDataCode();
  return ToMapleNULL(ExternalKernel);
}

EXPORT_DECL ALGEB M_DECL BombardByRandoms(MKernelVector ExternalKernel, ALGEB *args)
{
  int rand_cnt, i, CallCounter[arrsize(FuncNames)], FuncNo;
  FLOAT64 fargs[4], lower_bounds[3], upper_bounds[3], *row;
  //
  /*
  if (code!=GetDataCode())
    MaplePrintf(ExternalKernel, "God damn!\n");
  */
  for (i=0;i<3;i++)
  {
    lower_bounds[i]=(FLOAT64)(MapleToFloat64(ExternalKernel, args[2*i+1])); // min
    upper_bounds[i]=(FLOAT64)(MapleToFloat64(ExternalKernel, args[2*i+2])); // max
  }
  for (i=0;i<arrsize(FuncNames);i++)
    CallCounter[i]=0;
  InitFuncPointers(ExternalKernel);
  rand_cnt=(int)(MapleToInteger32(ExternalKernel, args[7]));
  while(rand_cnt>0)
  {
    rand_cnt--;
    for (i=0;i<3;i++)
      fargs[i+1]=lower_bounds[i]+(upper_bounds[i]-lower_bounds[i])*UniformUnitRandom(ExternalKernel);
    FuncNo=(int)(arrsize(FuncNames)*UniformUnitRandom(ExternalKernel));
    CallCounter[FuncNo]++;
    fargs[0]=MyExternalFunctionEval(ExternalKernel, fargs, FuncNo);
    row=data+MATRIX_OFFSET_C_RECT((int)(UniformUnitRandom(ExternalKernel)*100.0)+1,1,100,5);
    for (i=0;i<3;i++)
      row[i]=fargs[i+1];
    row[3]=(FLOAT64)FuncNo;
    row[4]=fargs[0];
  }
  return rt;
}
