#include <windows.h>   // CF_TEXT

#define MV_UTILITIES    // one per project

#include "mvdll.h"

// function names (to be added to the MathViews engine)
static  char *FunctionNames [] =
    {
       "mvdlladd",        //MVDLL_ADD            0
       "mvdllmul",        //MVDLL_MUL            1
       "mvdllconcat",     //MVDLL_CONCATSTR      2
       "mvdllmax",        //MVDLL_MAX            3
       "mvdll_donothing", //MVDLL_DONOTHING      4
       0
    };

static  HANDLE  DllInst = NULL;      // Dll Instance
static  void   *mvDLL   = NULL;      // pointer to class

extern "C" {

/************************************************************************
*
*       "DllMain"       This is the Main entry point for windows NT,
*                       and is called when the library is loaded.
*
*
*       INPUTS:
*               hInstance       = DLLs instance handle
*               dwReason        = Reason why DllMain is being called
*               lpReserved      = additional status of "PROCESS" calls
*
*
*       RETURNS:
*               "DLL_PROCESS_ATTACH"
*                       TRUE if the initialization is successful;
*                       FALSE for error, abort.
*               else
*                       ignored.
*
*************************************************************************/

BOOL WINAPI DllMain( HANDLE hInstance, DWORD dwReason, LPVOID lpReserved )
{
    //      save our instance handle
    DllInst = hInstance;

    switch  ( dwReason )
    {
        case DLL_PROCESS_ATTACH:
//            MessageBox(NULL, "MvDDE","DLL_PROCESS_ATTACH", MB_OK);
            return (TRUE);          // successful attach!

        case DLL_PROCESS_DETACH:
//            MessageBox(NULL, "MvDDE","DLL_PROCESS_DETACH", MB_OK);
            break;

        case DLL_THREAD_ATTACH:
//            MessageBox(NULL, "MvDDE","DLL_THREAD_ATTACH", MB_OK);
            break;

        case DLL_THREAD_DETACH:
//            MessageBox(NULL, "MvDDE","DLL_THREAD_DETACH", MB_OK);
            break;

        default:
            break;
    }

   return (FALSE);

}

/*******************************************************************************/
/* Interface between DLL and application */

int   WINAPI InitMathViewsDll (
    MV_INTERFACE *mv,
    char    ***Names)		// OUTPUT
{
    mvDLL = (void *) (new MvDll(mv));
    *Names = FunctionNames;
    return TRUE;
}

int WINAPI ExecMathViewsDll (
    void    * program,
    int       type, 
    int     * nout, 
    Datum  ** dat_out, 
    int       nin, 
    Datum  ** dat_in)
{
    if (type == -1)
    {
       strcpy ((char *) dat_out, "MVDDEV1.11");    //SH 4/21/93
       return TRUE;
    }
    return ((MvDll *)mvDLL)->DllFunction(type, nout, dat_out, nin, dat_in);
}

int   WINAPI AbortMathViewsDll (
    void    *)
{
    ((MvDll *)mvDLL)->Abort ();
    return TRUE;
}

int   WINAPI CloseMathViewsDll (
    void    *)
{
    delete (MvDll *)mvDLL;
    return TRUE;
}

} // extern "C"

/*@Method
   A desctructor
*/
MvDll::~MvDll()
{
    // cleanup code
    if (Initialized)
    {
        // cleanup code
    }
}

/*@Method
   A constructor
*/
MvDll::MvDll(MV_INTERFACE *mv)
{
   memcpy (&MV, mv, sizeof (MV));

   Initialized = FALSE;
}

int  MvDll::DllFunction (
   int        type, 
   int *      nout, 
   Datum **   dat_out, 
   int        nin, 
   Datum **   dat_in)
{

   *nout = 0;
   if (Initialized == FALSE)
   {
      // perform initialization (if needed)
      Initialized = TRUE;
   }

   switch (type) 
   {
      case MVDLL_ADD :
         if ( nin != 2 ||
             !MVIsArray(dat_in[0]) ||
             !MVIsArray(dat_in[1]) )
         {
            IssueError (ARG_ERROR, "arg error");
            break;
         }

         ReadArg(dat_in[0], Arg1);
         ReadArg(dat_in[1], Arg2);

         AddArgs(dat_out[0]);
         break;

      case MVDLL_MUL :
         if ( nin != 2 ||
             !MVIsArray(dat_in[0]) ||
             !MVIsArray(dat_in[1]) )
         {
            IssueError (ARG_ERROR, "arg error");
            break;
         }

         ReadArg(dat_in[0], Arg1);
         ReadArg(dat_in[1], Arg2);

         MulArgs(dat_out[0]);
         break;

      case MVDLL_CONCATSTR :
         if ( nin != 2 ||
             !MVIsArray(dat_in[0]) ||
             !MVIsArray(dat_in[1]) )
         {
            IssueError (ARG_ERROR, "arg error");
            break;
         }

         ReadArg(dat_in[0], Arg1);
         ReadArg(dat_in[1], Arg2);

         ConcatArgs(dat_in[0], dat_in[1], dat_out[0]);
         break;

      case MVDLL_MAX :
         if ( nin != 1 ||
             !MVIsArray(dat_in[0]) )
         {
            IssueError (ARG_ERROR, "arg error");
            break;
         }

         ReadArg(dat_in[0], Arg1);

         MaxArg(dat_out[0]);
         break;

      case MVDLL_DONOTHING :
         DoNothing(dat_out[0]);
         break;
   }

   if(dat_out[0] && *nout == 0)
      *nout = 1;

   return TRUE;
}

void   MvDll::Abort ()
{
    // abort current execution
}


void MvDll::ReadArg(Datum *d, MvArg &arg)
{
   (* MV.MVArrayInfo)(MV.Program, d->u.a, &arg.pval, &arg.m, &arg.n, &arg.type); 
}


int MvDll::StringOf(Datum *d, char *buf, int len)
{
    MvArg    arg;
    INDINT   i;
    int      nSuccess = FALSE;

    if (MVIsString (d))
    {
        ReadArg(d, arg);

        // convert data to string
        for (i = 0; i < arg.m * arg.n && i < len-1; i++, arg.pval++)
            buf [i] = (char) *arg.pval;
        buf [i] = '\0';

        nSuccess = TRUE;
    }
    else
        IssueError(BAD_ARGUMENT, "bad argument");

    return nSuccess;
}

// add the two arguments (ignoring shape) and return the data
// in a matrix same shape as Arg1
// It also demonstrates handling of a complex arguments (we only
// deal with real+real and complex+complex
// it is fairly easy (and tedious) to deal with complex+real cases
int MvDll::AddArgs(Datum *&dout)
{
    INDINT   i, nm1, nm2, iRow = 0;
    int      nSuccess = FALSE;
    double   dAdd;
    int      cplxSize = 2;   // Complex size in doubles
    double   dAddReal, dAddImag;

    nm1 = Arg1.m * Arg1.n;
    nm2 = Arg2.m * Arg2.n;
    if(nm1 != nm2)
    {
        IssueError(BAD_ARGUMENT, "size mismatch");
        goto FuncExit;
    }

    if(!dout)
    {
        dout = (*MV.MVNewDatum)(MV.Program);
        if(!dout)
        {
            IssueError(BAD_ARGUMENT, "memory error");
            goto FuncExit;
        }
    }

    dout->Type = DARRAY;
    dout->u.a = (*MV.MVNewArray)(MV.Program, Arg1.m, Arg1.n, Arg1.type);
    if(!dout->u.a)
    {
        IssueError(BAD_ARGUMENT, "memory error");
        goto FuncExit;
    }

    switch(Arg1.type)
    {
        case REAL_TYPE:
            if(Arg2.type != REAL_TYPE)
            {
                IssueError(BAD_ARGUMENT, "can add only real to real");
                goto FuncExit;
            }

            // now add the elements
            for(i = 0; i < nm1; i++)
            {
                dAdd = Arg1.pval[i]+Arg2.pval[i];

                MVSetElement(&MV, dout->u.a, i, iRow, dAdd);
            }
            break;

        case COMPLEX_TYPE:
            if(Arg2.type != COMPLEX_TYPE)
            {
                IssueError(BAD_ARGUMENT, "can add only complex to complex");
                goto FuncExit;
            }

            nm1 *= cplxSize;
            // now add the elements
            for(i = 0; i < nm1; i += cplxSize)
            {
                dAddReal = Arg1.pval[i]+Arg2.pval[i];
                dAddImag = Arg1.pval[i+1]+Arg2.pval[i+1];

                MVSetComplexElement(&MV, dout->u.a, i, iRow, dAddReal, dAddImag);
            }
            break;

        default:
            {
                IssueError(BAD_ARGUMENT, "unknown type");
                goto FuncExit;
            }
            break;
    }

    nSuccess = TRUE;

FuncExit:
    return nSuccess;
}


// multiply the two arguments (ignoring shape)
// return the data in a row vector
int MvDll::MulArgs(Datum *&dout)
{
    INDINT   i, nm1, nm2, iRow = 0;
    int      nSuccess = FALSE;
    double   dMul;

    nm1 = Arg1.m * Arg1.n;
    nm2 = Arg2.m * Arg2.n;
    if(nm1 != nm2)
    {
        IssueError(BAD_ARGUMENT, "size mismatch");
        goto FuncExit;
    }

    if(!dout)
    {
        dout = (*MV.MVNewDatum)(MV.Program);
        if(!dout)
        {
            IssueError(BAD_ARGUMENT, "memory error");
            goto FuncExit;
        }
    }

    dout->Type = DARRAY;
    dout->u.a = (*MV.MVNewArray)(MV.Program, 1, nm2, REAL_TYPE);
    if(!dout->u.a)
    {
        IssueError(BAD_ARGUMENT, "memory error");
        goto FuncExit;
    }

    // now multiply the elements
    for(i = 0; i < nm1; i++)
    {
        dMul = Arg1.pval[i]*Arg2.pval[i];

        MVSetElement(&MV, dout->u.a, i, iRow, dMul);
    }
    nSuccess = TRUE;

FuncExit:
    return nSuccess;
}


// concat the two arguments
int MvDll::ConcatArgs(Datum *&d1, Datum *&d2, Datum *&dout)
{
    INDINT   iRow, jCol;
    int      nSuccess = FALSE;
    double   dAdd = 0.0;

    if(Arg1.m != Arg2.m)
    {
        IssueError(BAD_ARGUMENT, "size mismatch");
        goto FuncExit;
    }

    if(!dout)
    {
        dout = (*MV.MVNewDatum)(MV.Program);
        if(!dout)
        {
            IssueError(BAD_ARGUMENT, "memory error");
            goto FuncExit;
        }
    }

    dout->Type = DARRAY;
    dout->u.a = (*MV.MVNewArray)(MV.Program, Arg1.m, Arg1.n+Arg2.n, REAL_TYPE);
    if(!dout->u.a)
    {
        IssueError(BAD_ARGUMENT, "memory error");
        goto FuncExit;
    }

    // now concat the elements
    for(iRow = 0; iRow < Arg1.m; iRow++)
    {
        for(jCol = 0; jCol < Arg1.n; jCol++)
        {
            dAdd = MVGetElement (&MV, d1->u.a, jCol, iRow);
            MVSetElement(&MV, dout->u.a, jCol, iRow, dAdd);
        }
    }
    for(iRow = 0; iRow < Arg2.m; iRow++)
    {
        for(jCol = 0; jCol < Arg2.n; jCol++)
        {
            dAdd = MVGetElement (&MV, d2->u.a, jCol, iRow);
            MVSetElement(&MV, dout->u.a, Arg1.n+jCol, iRow, dAdd);
        }
    }
    nSuccess = TRUE;

FuncExit:
    return nSuccess;
}


// return the max of all elements in the argument
int MvDll::MaxArg(Datum *&dout)
{
    INDINT   i, nm1;
    int      nSuccess = FALSE;
    double   dMax;

    nm1 = Arg1.m * Arg1.n;
    if(!dout)
    {
        dout = (*MV.MVNewDatum)(MV.Program);
        if(!dout)
        {
            IssueError(BAD_ARGUMENT, "memory error");
            goto FuncExit;
        }
    }

    dout->Type = DARRAY;
    dout->u.a = (*MV.MVNewArray)(MV.Program, 1, 1, REAL_TYPE);
    if(!dout->u.a)
    {
        IssueError(BAD_ARGUMENT, "memory error");
        goto FuncExit;
    }

    // find the max value
    dMax = Arg1.pval[0];
    for(i = 1; i < nm1; i++)
    {
        if(dMax < Arg1.pval[i])
            dMax = Arg1.pval[i];
    }
    MVSetElement(&MV, dout->u.a, 0, 0, dMax);
    nSuccess = TRUE;

FuncExit:
    return nSuccess;
}

// calls some function for side effects (Initialize hardware,
// start FTP transfer, etc.). No return argument
int MvDll::DoNothing(Datum *&dout)
{
    int    nSuccess = FALSE;

    return nSuccess;
}

void  MvDll::IssueError (int Code, char *msg)
{
    (*MV.MVError) (MV.Program, Code, msg); //Code defined in header interface
}

