/*
 * USEROBJS.C
 *
 * Window DLL for user defined operators.
 *
 * Copyright (c) Siemens AG 1993.  All rights reserved.
 *
 * $Header:   P:/VCS/TSEX/USEROBJS/USEROBJS.C_V   1.1   10 Dec 1993 17:34:58   ror  $
 */

#include <windows.h>
#include <fcdslib.h>
#include <memory.h>
#include <stdlib.h>
#include <fpl.h>
#include <fpli.h>

#include <ctype.h>


typedef FLOAT  FAR *LPFLOAT;
typedef FUBYTE FAR *LPFUBYTE;

#define NOTUSED(p)          p=p
HINSTANCE hInst;

// Structure needed by AVERAGE
//
typedef struct
{
    FLOAT sum;
    ULONG cnt;
} AVGSTRUCT;

typedef AVGSTRUCT far   *LPAVGSTRUCT;

// Structure needed by DELAY
//
typedef struct
{
    int iDelay;
    HGLOBAL hDelay;
} DELAYSTRUCT;

typedef DELAYSTRUCT far   *LPDELAYSTRUCT;

// Prototypes
int FAR PASCAL AVERAGEReset (GLOBALHANDLE h);
int FAR PASCAL DELAYReset (GLOBALHANDLE h);

// Library main entry point.
//
int FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
{
    NOTUSED(wDataSeg);
    NOTUSED(lpszCmdLine);

    hInst = hInstance;

    if (wHeapSize > 0)
	UnlockData (0);

    return 1;
}

// Standard WEP routine.
//
int FAR PASCAL WEP(int nParam)
{
    NOTUSED(nParam);

    return 1;
}

// Implementation of initialization routine for USER object class AVGMANY
//
// Returns NULL if errors occur, else a handle.
//
GLOBALHANDLE FAR PASCAL AVGMANYInit(LPSTR body)
{
    NOTUSED(body);

    // No special memory is needed for the AVGMANY object class, so
    // return a dummy handle
    //
    return (GLOBALHANDLE) 1;
}

// Implementation of reset routine for USER object class AVGMANY
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL AVGMANYReset(GLOBALHANDLE h)
{
    NOTUSED(h);

    // No special reset code is needed for the AVGMANY object class
    //
    return 0;
}

// Implementation of execution routine for USER object class AVGMANY
//
// Returns the average of all inputs in the output parameter.
//
// Restrictions:
//   o there must be at least 1 input
//   o all inputs and output must be of the same type
//   o all inputs and output must be crisp
//   o there must be at least 1 output
//   o the output must be of the same type as the inputs
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL AVGMANYExecute(GLOBALHANDLE h, LPSTR paramfmt, LPIOVALUE params)
{
    FLOAT sum = 0.0;
    int len;
    int i;
    char typech;

    NOTUSED(h);

    len = lstrlen (paramfmt);

    typech = (char) *paramfmt;

    // Must be at least 1 input and 1 output, or two parameters
    //
    if (len < 2)
	return 1;

    // Compute the sum of the inputs
    //
    for (i=0; i < len - 1; i++, params++, paramfmt++)
    {
	// Check for mixed input types
	//
	if (*paramfmt != typech)
	    return 2;

	switch (typech)
	{
	case TYPE_FLOAT:    sum += params->float_value;  break;
	case TYPE_UBYTE:    sum += params->ubyte_value;  break;
	case TYPE_SBYTE:    sum += params->sbyte_value;  break;
	case TYPE_UWORD:    sum += params->uword_value;  break;
	case TYPE_SWORD:    sum += params->sword_value;  break;
	case TYPE_SLONG:    sum += params->slong_value;  break;
	case TYPE_ULONG:    sum += params->ulong_value;  break;
	case TYPE_FUBYTE:   sum += params->fubyte_value; break;
	case TYPE_FSBYTE:   sum += params->fsbyte_value; break;
	case TYPE_FUWORD:   sum += params->fuword_value; break;
	case TYPE_FSWORD:   sum += params->fsword_value; break;
	default:
	    // Illegal input parameter type
	    //
	    return 3;
	}
    }

    // Compute the average
    //
    sum /= len - 1;

    // Make sure the input and output types are the same
    //
    if (tolower (typech) != *paramfmt)
	return 4;

    // Return the average in the output parameter
    //
    switch (*paramfmt)
    {
    case TYPE_FLOAT_ADDR:    *params->float_ptr =  (FLOAT)  sum;  break;
    case TYPE_UBYTE_ADDR:    *params->ubyte_ptr =  (UBYTE)  sum;  break;
    case TYPE_SBYTE_ADDR:    *params->sbyte_ptr =  (SBYTE)  sum;  break;
    case TYPE_UWORD_ADDR:    *params->uword_ptr =  (UWORD)  sum;  break;
    case TYPE_SWORD_ADDR:    *params->sword_ptr =  (SWORD)  sum;  break;
    case TYPE_SLONG_ADDR:    *params->slong_ptr =  (SLONG)  sum;  break;
    case TYPE_ULONG_ADDR:    *params->ulong_ptr =  (ULONG)  sum;  break;
    case TYPE_FUBYTE_ADDR:   *params->fubyte_ptr = (FUBYTE) sum;  break;
    case TYPE_FSBYTE_ADDR:   *params->fsbyte_ptr = (FSBYTE) sum;  break;
    case TYPE_FUWORD_ADDR:   *params->fuword_ptr = (FUWORD) sum;  break;
    case TYPE_FSWORD_ADDR:   *params->fsword_ptr = (FSWORD) sum;  break;
    default:
	// Illegal output parameter type
	//
	return 5;
    }

    // Return a good completion
    //
    return 0;
}

// Implementation of clean routine for USER object class AVGMANY
//
// Returns NULL if errors occur, else a handle.
//
VOID FAR PASCAL AVGMANYClean(GLOBALHANDLE h)
{
    NOTUSED(h);

    // No special memory is needed for the AVGMANY object class, so
    // we don't have to clean up the handle
}

// Implementation of initialization routine for USER object class AVERAGE
//
// Returns NULL if errors occur, else a handle.
//
GLOBALHANDLE FAR PASCAL AVERAGEInit(LPSTR body)
{
    GLOBALHANDLE htemp;

    NOTUSED(body);

    // Allocate temp space
    //
    htemp = GlobalAlloc (GHND, sizeof(AVGSTRUCT));

    // Initialize the temp space
    //
    if (htemp != NULL)
    {
	if (AVERAGEReset (htemp))
	{
	    GlobalFree (htemp);
	    htemp = NULL;
	}
    }

    return htemp;
}

// Implementation of reset routine for USER object class AVERAGE
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL AVERAGEReset(GLOBALHANDLE h)
{
    LPAVGSTRUCT pavg;

    // Lock the global memory handle
    //
    if ((pavg = (LPAVGSTRUCT) GlobalLock (h)) == NULL)
	return 1;

    // Reset the temp space
    //
    pavg->sum = 0.0;
    pavg->cnt = 0;

    // Unlock the global memory handle
    //
    GlobalUnlock (h);

    // Return a good completion
    //
    return 0;
}

// Implementation of execution routine for USER object class AVERAGE
//
// Returns the average of the input parameter in the output parameter.
//
// Restrictions:
//   o there must be exactly 1 input and 1 output of the same type
//   o the input and output must be crisp
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL AVERAGEExecute(GLOBALHANDLE h, LPSTR paramfmt, LPIOVALUE params)
{
    int len;
    char typech;
    LPAVGSTRUCT pavg;
    FLOAT avg;

    len = lstrlen (paramfmt);

    typech = (char) *paramfmt++;

    // Must be exactly 1 input and 1 output
    //
    if (len != 2)
	return 1;

    // Lock the global memory handle
    //
    if ((pavg = (LPAVGSTRUCT) GlobalLock (h)) == NULL)
	return 2;

    // Update the sum
    //
    switch (typech)
    {
    case TYPE_FLOAT:    pavg->sum += params->float_value;  break;
    case TYPE_UBYTE:    pavg->sum += params->ubyte_value;  break;
    case TYPE_SBYTE:    pavg->sum += params->sbyte_value;  break;
    case TYPE_UWORD:    pavg->sum += params->uword_value;  break;
    case TYPE_SWORD:    pavg->sum += params->sword_value;  break;
    case TYPE_SLONG:    pavg->sum += params->slong_value;  break;
    case TYPE_ULONG:    pavg->sum += params->ulong_value;  break;
    case TYPE_FUBYTE:   pavg->sum += params->fubyte_value; break;
    case TYPE_FSBYTE:   pavg->sum += params->fsbyte_value; break;
    case TYPE_FUWORD:   pavg->sum += params->fuword_value; break;
    case TYPE_FSWORD:   pavg->sum += params->fsword_value; break;
    default:
	// Illegal input parameter type
	//
	GlobalUnlock (h);
	return 3;
    }

    // Update the count and compute the average
    //
    pavg->cnt++;
    avg = pavg->sum / pavg->cnt;

    // Unlock the global memory handle
    //
    GlobalUnlock (h);

    // Make sure the input and output types are the same
    //
    if (tolower (typech) != *paramfmt)
	return 4;

    // Return the average in the output parameter
    //
    params++;

    switch (*paramfmt)
    {
    case TYPE_FLOAT_ADDR:    *params->float_ptr =  (FLOAT)  avg;  break;
    case TYPE_UBYTE_ADDR:    *params->ubyte_ptr =  (UBYTE)  avg;  break;
    case TYPE_SBYTE_ADDR:    *params->sbyte_ptr =  (SBYTE)  avg;  break;
    case TYPE_UWORD_ADDR:    *params->uword_ptr =  (UWORD)  avg;  break;
    case TYPE_SWORD_ADDR:    *params->sword_ptr =  (SWORD)  avg;  break;
    case TYPE_SLONG_ADDR:    *params->slong_ptr =  (SLONG)  avg;  break;
    case TYPE_ULONG_ADDR:    *params->ulong_ptr =  (ULONG)  avg;  break;
    case TYPE_FUBYTE_ADDR:   *params->fubyte_ptr = (FUBYTE) avg;  break;
    case TYPE_FSBYTE_ADDR:   *params->fsbyte_ptr = (FSBYTE) avg;  break;
    case TYPE_FUWORD_ADDR:   *params->fuword_ptr = (FUWORD) avg;  break;
    case TYPE_FSWORD_ADDR:   *params->fsword_ptr = (FSWORD) avg;  break;
    default:
	// Illegal output parameter type
	//
	return 5;
    }

    // Return a good completion
    //
    return 0;
}

// Implementation of clean routine for USER object class AVERAGE
//
// Returns NULL if errors occur, else a handle.
//
VOID FAR PASCAL AVERAGEClean(GLOBALHANDLE h)
{
    // Deallocate the temp space
    //
    GlobalFree (h);
}


// Implementation of initialization routine for USER object class DELAY
//
// Returns NULL if errors occur, else a handle.
//
GLOBALHANDLE FAR PASCAL DELAYInit(LPSTR body)
{
    long int i=-1;
    GLOBALHANDLE htemp;
    LPDELAYSTRUCT pdelay;

    NOTUSED(body);

    while (*body && (*body == ' ' || *body == '\t'))
	body++;

    while (IsCharAlphaNumeric (*body) && !IsCharAlpha (*body))
    {
	if (i==-1)
	    i = 0;

	i *= 10;
	i += *body-'0';
	*body++;
    }

    if (i<0 || i>32767)
    {
	MessageBox (NULL, "The DELAY object requires an integer value between 0 and 32767 in its body text.", "USEROBJS.DLL", MB_OK);
	return NULL;
    }

    // Allocate temp space
    //
    htemp = GlobalAlloc (GHND, sizeof (DELAYSTRUCT));

    // Initialize the temp space
    //
    if (htemp != NULL)
    {
	if ((pdelay = (LPDELAYSTRUCT) GlobalLock (htemp)) == NULL)
	    return 1;

	pdelay->iDelay = (int) i;
	pdelay->hDelay = GlobalAlloc (GHND, sizeof(FLOAT)*(pdelay->iDelay+1));

	if (!pdelay->hDelay)
	{
	    GlobalUnlock (htemp);
	    GlobalFree (htemp);
	    return NULL;
	}

	GlobalUnlock (htemp);

	if (DELAYReset (htemp))
	{
	    GlobalFree (htemp);
	    htemp = NULL;
	}
    }

    return htemp;
}

// Implementation of reset routine for USER object class DELAY
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL DELAYReset(GLOBALHANDLE h)
{
    FLOAT FAR *lpData;
    LPDELAYSTRUCT pdelay;

    // Lock the global memory handle
    //
    if ((pdelay = (LPDELAYSTRUCT) GlobalLock (h)) == NULL)
	return 1;

    lpData = (FLOAT FAR *) GlobalLock (pdelay->hDelay);

    // Reset the temp space
    //
    _fmemset ((void FAR *) lpData, 0, pdelay->iDelay*sizeof(FLOAT));

    GlobalUnlock (pdelay->hDelay);

    // Unlock the global memory handle
    //
    GlobalUnlock (h);

    // Return a good completion
    //
    return 0;
}

// Implementation of execution routine for USER object class DELAY
//
// Returns the average of the input parameter in the output parameter.
//
// Restrictions:
//   o there must be exactly 2 inputs and 1 output 
//   o the inputs and output must be crisp
//
// Returns non-zero if errors occur, else zero.
//
int FAR PASCAL DELAYExecute(GLOBALHANDLE h, LPSTR paramfmt, LPIOVALUE params)
{
    int             len;
    char            typech;
    LPDELAYSTRUCT   pdelay;
    FLOAT FAR *lpData;
    int             i,j;

    // Must be exactly 1 input and 1 output.
    //
    len = lstrlen (paramfmt);
    typech = (char) *paramfmt++;
    if (len != 2)
	return 1;

    // Lock the global memory handle
    //
    if ((pdelay = (LPDELAYSTRUCT) GlobalLock (h)) == NULL)
	return 2;

    lpData = (FLOAT FAR *) GlobalLock (pdelay->hDelay);

    for (i=pdelay->iDelay; i>0; i--)
	lpData[i] = lpData[i-1];

    switch (typech)
    {
    case TYPE_FLOAT:	lpData[0] = params->float_value;  break;
    case TYPE_UBYTE:	lpData[0] = params->ubyte_value;  break;
    case TYPE_SBYTE:	lpData[0] = params->sbyte_value;  break;
    case TYPE_UWORD:	lpData[0] = params->uword_value;  break;
    case TYPE_SWORD:	lpData[0] = params->sword_value;  break;
    case TYPE_SLONG:	lpData[0] = params->slong_value;  break;
    case TYPE_ULONG:	lpData[0] = params->ulong_value;  break;
    case TYPE_FUBYTE:	lpData[0] = params->fubyte_value; break;
    case TYPE_FSBYTE:	lpData[0] = params->fsbyte_value; break;
    case TYPE_FUWORD:	lpData[0] = params->fuword_value; break;
    case TYPE_FSWORD:	lpData[0] = params->fsword_value; break;
    default:
	// Illegal input parameter type
	//
	GlobalUnlock (pdelay->hDelay);
	GlobalUnlock (h);
	return 3;
    }

    // Make sure the input and output types are the same
    //
    if (tolower (typech) != *paramfmt)
	return 4;

    // Return the average in the output parameter
    //
    params++;

    j = pdelay->iDelay;

    switch (*paramfmt)
    {
    case TYPE_FLOAT_ADDR:    *params->float_ptr =  (FLOAT)  lpData[j];	break;
    case TYPE_UBYTE_ADDR:    *params->ubyte_ptr =  (UBYTE)  lpData[j];	break;
    case TYPE_SBYTE_ADDR:    *params->sbyte_ptr =  (SBYTE)  lpData[j];	break;
    case TYPE_UWORD_ADDR:    *params->uword_ptr =  (UWORD)  lpData[j];	break;
    case TYPE_SWORD_ADDR:    *params->sword_ptr =  (SWORD)  lpData[j];	break;
    case TYPE_SLONG_ADDR:    *params->slong_ptr =  (SLONG)  lpData[j];	break;
    case TYPE_ULONG_ADDR:    *params->ulong_ptr =  (ULONG)  lpData[j];	break;
    case TYPE_FUBYTE_ADDR:   *params->fubyte_ptr = (FUBYTE) lpData[j];	break;
    case TYPE_FSBYTE_ADDR:   *params->fsbyte_ptr = (FSBYTE) lpData[j];	break;
    case TYPE_FUWORD_ADDR:   *params->fuword_ptr = (FUWORD) lpData[j];	break;
    case TYPE_FSWORD_ADDR:   *params->fsword_ptr = (FSWORD) lpData[j];	break;
    default:
	// Illegal output parameter type
	//
	GlobalUnlock (pdelay->hDelay);
	return 5;
    }

    // Unlock the global memory handle
    //
    GlobalUnlock (pdelay->hDelay);
    GlobalUnlock (h);

    // Return a good completion
    //
    return 0;
}

// Implementation of clean routine for USER object class DELAY
//
// Returns NULL if errors occur, else a handle.
//
VOID FAR PASCAL DELAYClean(GLOBALHANDLE h)
{
    LPDELAYSTRUCT   pdelay;

    // Deallocate the temp space
    //
    if (h)
    {
	pdelay = (LPDELAYSTRUCT) GlobalLock (h);

	if (pdelay->hDelay)
	    GlobalFree (pdelay->hDelay);

	GlobalFree (h);
    }
}
