///////////////////////////////////////////////////////////////////////////
// WindActs.c
// Code to help programs manipulate Fluid arrays and implement Window Acts

#include	"windows.h"
#include	"windacts.h"
#include	<string.h>

#define		sizearray	4
#define		sizenullstring 4

//#pragma warning(disable:4047 4024 4047)

////////////////////////////// general functions //////////////////////////
/* calculate the size of a CeSk data object given a ptr */
/* to data variable in op1 */
/* return the size IN BYTES as a function return */
long GetVarSize(DataObj  FAR *op1)
{
	short   i,j;
	long k,l;
	DataObj FAR *op2;
					                
	switch  (op1->tint.vtype) {
		case NULLTYPE :
		        return (sizeof(IntObj));
		case INTTYPE:
		        return (sizeof(IntObj));
		case FLOATTYPE:
		        return (sizeof(FloatObj));
		case DOUBLETYPE:
		        return (sizeof(DoubleObj));
		case STRINGTYPE:
		        return ((op1->tstring.length+5) & ~1);
		case BOOLTYPE:
		        return (sizeof(BoolObj));
		case ERRORTYPE:
		        return ((op1->terror.length+5) & ~1); 
		case DATETYPE:
		        return (sizeof(DateObj));
		case HMSTYPE:
		        return (sizeof(HmsObj));
		case COMPLEXTYPE:
		        return (sizeof(ComplexObj));
		case EQUATIONTYPE:
		        return ((op1->tequation.length+5) & ~1); 
		case ARRAYTYPE:
		        op2=PtrContent(op1);
		        j= op1->tarray.elements; k=4;
		        for (i=0; i<j; i++) {
		                l=GetVarSize(op2);
		                k+=l;
		                op2 = (DataObj *)(((char *)(op2)) +l);
		        };
		        return k;
	};
		
};

/* register a given string as a window message */
/* Input: msgid - the id of the resource to the string */
/* Return: a UINT representing the registered window message */
UINT	RegisterResourceString(HINSTANCE hInst, UINT msgid)
{
	char	text[256];
	
	LoadString(hInst,msgid,text,sizeof(text));
	if (!text[0]) return NULL;
	return RegisterWindowMessage(text);
};

/* construct a two dimensional CeSk array containing the strings */
/* required for a ActList command */
/* Input: baseID - the base string ID to start from, the strings */
/*	  are assumed to be string resource pairs starting at baseID */
/*	  and terminated with a string */
/* Returns a HGLOBAL to the array, the HGLOBAL is allocated as shared */
HGLOBAL ProduceActList(HINSTANCE hInst, UINT baseID)
{
	long	blocksize,writeptr,len;
	HGLOBAL result;
	DataObj	FAR *op1,FAR *op2;
	char	text[256],text2[256];
	unsigned short	count;

	blocksize=1024;
	writeptr=4;	/* sizearray is size of array header =4 bytes*/
	result=GlobalAlloc(GMEM_DDESHARE,blocksize);
	if (!result) return 0;
	op1=DataGlobalLock(result);
	op1->tarray.vtype=ARRAYTYPE;
	op1->tarray.elements=0;
	count=0;
	GlobalUnlock(result);

	do {
		LoadString(hInst,baseID++,text,sizeof(text));
		if (text[0]=='!' && !text[1]) break;		// reached the end
		LoadString(hInst,baseID++,text2,sizeof(text2));
		len=strlen(text)+strlen(text2)+sizearray+sizenullstring*2;
		if (len+32+writeptr > blocksize) {
			HGLOBAL	newresult;
			/* expand the block */
			blocksize=1024+len+writeptr;
			newresult=GlobalReAlloc(result,blocksize,0);
			if (!newresult) {
				GlobalFree(result);
				return 0;
			};
			result=newresult;
		};
		op1=DataGlobalLock(result);
		op2=op1=StepBytes(op1,writeptr);
		op1->tint.vtype=ARRAYTYPE;
		op1->tarray.elements=2;
		op1=PtrContent(op1);
		op1->tstring.vtype=STRINGTYPE;
		op1->tstring.length=strlen(text);
		memcpy(op1->tstring.value,text,op1->tstring.length);
		op1=NextDataObj(op1);
		op1->tstring.vtype=STRINGTYPE;
		op1->tstring.length=strlen(text2);
		memcpy(op1->tstring.value,text2,op1->tstring.length);
		writeptr+=GetVarSize(op2);
		GlobalUnlock(result);
		count++;
	} while (TRUE);
	op1=DataGlobalLock(result);
	op1->tarray.elements=count;
	GlobalUnlock(result);
	result=GlobalReAlloc(result,writeptr,0);
	return result;
};

/* register a list of functions as window messages */
/* Input: */
/*	  hInst - handle of the instance containing the resource strings */
/*    baseID - the base string ID to start from, the functions are */
/*	  assumed to be in pairs, the first string in a pair is the function */
/*	  name, the second a help string explaining what is does */
/*	  messID[] - ptr to array of UINTs to receive the results */
void	RegisterActList(HINSTANCE hInst, UINT baseID,UINT FAR * messID)
{
	short	count;
	char	temp[256];

	count=0;
	do {
		LoadString(hInst,baseID,temp,sizeof(temp));
		if (temp[0]=='!' && !temp[1]) break;
		messID[count++]=RegisterWindowMessage(temp);
		baseID+=2;
	} while (TRUE);
	return;
};

// given a window act id, find the corresponding name of the act
// This is a complimentary function to RegisterActList
// Input: hInst - the handle of the instance
//		  ActId - the ID of the Window act to search for
//		  baseID - the base id of the strings for window acts
//		  pBuff - ptr to the buffer to receive the act name
//		  len - length of the buffer in bytes
// The pBuff is filled with the name of the Act (or "" if none)
void FindActName(HINSTANCE hInst, UINT ActId, UINT baseID, LPSTR pBuff, int len)
{
	short	count;
	int		i;
	char	temp[256];

	count=0;
	do {
		LoadString(hInst,baseID,temp,sizeof(temp));
		if (temp[0]=='!' && !temp[1]) {
			pBuff[0]=0;
			return;
		};
		if (ActId==RegisterWindowMessage(temp)) {
			// we found a match
			for (i=0; i<(len-1) && temp[i]; i++) pBuff[i]=temp[i];
			pBuff[i]=0;
			return;
		};
		baseID+=2;
	} while (TRUE);
	return;
};


//////////////////// functions to set the FluteNames of windows ////////////////
static	ATOM FluteNameGAtom=0;

// set the "Flute Name" for a window. The Flute name is stored in a property
// Input: hWnd - handle of the window
//		  lpBuff - ptr to name to assign to the window
void SetWindowFluteName(HWND hWnd, LPCSTR lpBuff)
{
	ATOM	nAtom;

	if (!FluteNameGAtom) FluteNameGAtom = GlobalAddAtom("FluteName");
	nAtom = GlobalAddAtom(lpBuff);
	
	SetProp(hWnd, (LPCSTR)FluteNameGAtom, (HANDLE)nAtom);
};

// set the "Flute Name" for an item on a dialogue.
// Input: hDlg - handle of the dialogue
//		  id - id of the dialogue item
//		  lpBuff - ptr to the name to assign
void SetDlgItemFluteName(HWND hDlg, UINT id, LPCSTR lpBuff)
{
	SetWindowFluteName(GetDlgItem(hDlg,id),lpBuff);
};

// Clear a "FluteName" from a window
// Input: hWnd - handle of the window whose Flute name is to be cleared
void ClearWindowFluteName(HWND hWnd)
{
	ATOM	pat;
	// SetWindowFluteName must have been called previously, so
	// FluteNameGAtom *must* exist if there are any Flute names to delete
	if (!FluteNameGAtom) return;
	pat = (ATOM)GetProp(hWnd, (LPCSTR)FluteNameGAtom);
	if (pat) GlobalDeleteAtom(pat);
	RemoveProp(hWnd, (LPCSTR)FluteNameGAtom);
};

// Clear the "FluteName" from all windows on a dialogue
// Input: hDlg - handle of the dialogue whose names are to be cleared
void ClearDlgItemFluteNames(HWND hDlg)
{
	HWND	hw;
	
	hw= GetWindow(hDlg,GW_CHILD);
	while (hw) {
		ClearWindowFluteName(hw);
		hw = GetWindow(hDlg,GW_HWNDNEXT);
	};
};

//////////////////// Data construction Functions ////////////////////////////

// Make a string object given a ptr to a null terminated C string
// Input: lpBuff - ptr to the text
// Output: HGLOBAL for a StringObj containing the text
HGLOBAL	MakeString(LPCSTR lpBuff)
{
	UINT	len;
	HGLOBAL	result;
	DataObj	FAR *pos;
	
	for (len=0; lpBuff[len]; len++);
	result = GlobalAlloc(GMEM_DDESHARE, (5+len) & ~1);
	if (!result) return result;
	
	pos=DataGlobalLock(result);
	pos->tstring.vtype=STRINGTYPE;
	pos->tstring.length = len;
	for (len=0; lpBuff[len]; len++) pos->tstring.value[len]=lpBuff[len];
	GlobalUnlock(result);
	
	return result;
};

// Given a string resource ID, produce an Error Object (contained in a handle)
// Input: hInst - Instance of the program that owns the string resource
//		  id  - id of the string
// Output: HGLOBAL containing an ErrorObject
HGLOBAL	GeneralError(HINSTANCE hInst, UINT id)
{
	HGLOBAL	result;
	char	text[256];
	DataObj	FAR *pos;
	
	LoadString(hInst,id,text,sizeof(text));
	result = MakeString(text);
	if (!result) return NULL;
	pos=DataGlobalLock(result);
	pos->terror.vtype = ERRORTYPE;
	GlobalUnlock(result);
	return result;
}

// Given a long return a Flute integer object (Flute integers are longs)
// Input: lval - the long to convert
// Output: HGLOBAL containing an integer object
HGLOBAL MakeInt(long lval)
{
	HGLOBAL	result;
	DataObj	FAR *pos;
	
	result = GlobalAlloc(GMEM_DDESHARE,sizeof(IntObj));
	pos=DataGlobalLock(result);
	pos->tint.vtype = INTTYPE;
	pos->tint.value = lval;
	GlobalUnlock(result);
	
	return result;
};

// Given a double, produce a Flute double object in a HGLOBAL
// Input: dval - the value to convert
// Output: A HGLOBAL containing the value
HGLOBAL MakeDouble(double dval)
{
	HGLOBAL	result;
	DataObj	FAR *pos;
	
	result = GlobalAlloc(GMEM_DDESHARE,sizeof(DoubleObj));
	pos=DataGlobalLock(result);
	pos->tdouble.vtype = DOUBLETYPE;
	pos->tdouble.value = dval;
	GlobalUnlock(result);
	
	return result;
};

// Return a NULL object inside a HGLOBAL
HGLOBAL NullObj()
{
	HGLOBAL	result;
	DataObj	FAR *pos;
	
	result = GlobalAlloc(GMEM_DDESHARE,sizeof(IntObj));
	pos=DataGlobalLock(result);
	pos->tint.vtype = NULLTYPE;
	pos->tint.value = 0l;
	GlobalUnlock(result);
	
	return result;
};

//////////////////// Data Extraction Functions //////////////////////////

// Given a FAR ptr to a Flute data object, extract a long value
// This will convert any NUMERIC value into a long
// Input: optr - ptr to the object
//		  pTrans - ptr to a BOOLEAN which will is set on return
// Output: a long value contained within the object
//		   The boolean pointed to by pTrans is set TRUE if successfully translated
long GetALong(DataObj FAR *optr, BOOL FAR *pTrans)
{
	switch  (optr->tint.vtype) {
		case NULLTYPE :
				*pTrans = TRUE;
				return 0l;
		case INTTYPE:
				*pTrans = TRUE;
				return optr->tint.value;
		case FLOATTYPE:
				*pTrans = TRUE;
				return (long)optr->tfloat.value;
		case DOUBLETYPE:
				*pTrans = TRUE;
				return (long)optr->tdouble.value;
		case BOOLTYPE:
				*pTrans = TRUE;
				return (long)optr->tboolean.value;
		case COMPLEXTYPE:
				*pTrans = TRUE;
				return (long)optr->tcomplex.realpart;
		case ARRAYTYPE:
		case DATETYPE:
		case ERRORTYPE:
		case STRINGTYPE:
		case HMSTYPE:
		case EQUATIONTYPE:
				*pTrans = FALSE;
				return 0;
	};
	return 0;
};

// Given a FAR ptr to a Flute data object, extract a double value
// Input: optr - ptr to the object
//		  pTrans - ptr to a BOOLEAN which will be filled on return
// Output: a double value
//			The Boolean pointed to by pTrans is set TRUE if successfully translated
double GetADouble(DataObj FAR *optr, BOOL FAR *pTrans)
{
	switch  (optr->tint.vtype) {
		case INTTYPE:
				*pTrans = TRUE;
				return (double)optr->tint.value;
		case FLOATTYPE:
				*pTrans = TRUE;
				return (double)optr->tfloat.value;
		case DOUBLETYPE:
				*pTrans = TRUE;
				return (double)optr->tdouble.value;
		case BOOLTYPE:
				*pTrans = TRUE;
				return (double)optr->tboolean.value;
		case COMPLEXTYPE:
				*pTrans = TRUE;
				return (double)optr->tcomplex.realpart;
		case ARRAYTYPE:
		case DATETYPE:
		case ERRORTYPE:
		case NULLTYPE :
		case STRINGTYPE:
		case HMSTYPE:
		case EQUATIONTYPE:
				*pTrans = FALSE;
				return 0;
	};
	return 0;
};

// Given a ptr to a data object, fill a character buffer
// containing the string or error object text
// Input: optr - ptr to the object
//		  pTrans - ptr to a BOOLEAN which is filled on return
//		  pBuff - ptr to the destination buffer
//		  len - length of buffer
void GetAString(DataObj FAR *optr, BOOL FAR *pTrans, char FAR *pBuff, UINT len)
{
	UINT	slen,i;

	if (!len) {
		*pTrans=FALSE;
		return;
	};
	switch (optr->tint.vtype) {
		case ERRORTYPE:
		case STRINGTYPE:
			slen = optr->tstring.length;
			if (slen > (len-1)) slen = len-1;
			for (i=0; i<slen; i++) pBuff[i]=optr->tstring.value[i];
			*pTrans=TRUE;
			pBuff[slen]=0;
			return;
		default:
			*pBuff = 0;
			*pTrans = FALSE;
			return;
	};
};

////////////////////////// Array assembly functions //////////////////////////////////

// make an N element Flute array from a table of N HGLOBALs
// Input: pHands - ptr to the array of HGLOBALs that contain data objects
//		  N - number of elements
// Output: a HGLOBAL to a Flute array
// NOTE: THE HANDLES PASSED IN pHand ARE FREED BY THIS FUNCTION
HGLOBAL MakeArrayN(HGLOBAL FAR *pHands, UINT N)
{
	UINT	i;
	DataObj FAR *pobd, FAR *pobs;
	HGLOBAL	result;
    long	len;
	
	len=0;
	for (i=0; i<N; i++) {
		pobs = DataGlobalLock(pHands[i]);
		len+=GetVarSize(pobs);
		GlobalUnlock(pHands[i]);
	};
	
	result= GlobalAlloc(GMEM_DDESHARE,len+4);
	if (result) {
		pobd = DataGlobalLock(result);
		pobd->tarray.vtype = ARRAYTYPE;
		pobd->tarray.elements=(short)N;
		
		pobd = PtrContent(pobd);
		for (i=0; i<N; i++) {
			pobs = DataGlobalLock(pHands[i]);
			hmemcpy(pobd, pobs, GetVarSize(pobs));
			pobd = NextDataObj(pobd);
			GlobalUnlock(pHands[i]);
		};
	};
	for (i=0; i<N; i++) {
		GlobalFree(pHands[i]);
	};
	return result;
};


// Make an Array containing two elements
// Input: Obj1 - HGLOBAL to object1
//		  Obj2 - HGLOBAL to object2
// Output: HGLOBAL to array {Obj1, Obj2}
// NOTE: Obj1 and Obj2 are freed by this function
HGLOBAL	MakeArray2(HGLOBAL	Obj1, HGLOBAL Obj2)
{
	HGLOBAL	tab[2];
	tab[0]=Obj1;
	tab[1]=Obj2;
	return MakeArrayN(tab, 2);
}

// Make an Array containing two elements
// Input: Obj1 - HGLOBAL to object1
//		  Obj2 - HGLOBAL to object2
//		  Obj3 - HGLOBAL to object3
// Output: HGLOBAL to array {Obj1, Obj2, Obj3}
// NOTE: Obj1 Obj2 and Obj3 are freed by this function
HGLOBAL	MakeArray3(HGLOBAL	Obj1, HGLOBAL Obj2, HGLOBAL Obj3)
{
	HGLOBAL	tab[3];
	tab[0]=Obj1;
	tab[1]=Obj2;
	tab[2]=Obj3;
	return MakeArrayN(tab, 3);
}

// Make an Array containing two elements
// Input: Obj1 - HGLOBAL to object1
//		  Obj2 - HGLOBAL to object2
//		  Obj3 - HGLOBAL to object3
//		  Obj4 - HGLOBAL to object4
// Output: HGLOBAL to array {Obj1, Obj2, Obj3, Obj4}
// NOTE: Obj1 Obj2 Obj3, Obj4 are freed by this function
HGLOBAL	MakeArray4(HGLOBAL	Obj1, HGLOBAL Obj2, HGLOBAL Obj3, HGLOBAL Obj4)
{
	HGLOBAL	tab[4];
	tab[0]=Obj1;
	tab[1]=Obj2;
	tab[2]=Obj3;
	tab[3]=Obj4;
	return MakeArrayN(tab, 4);
}

