#include "global.h"

#include <stdio.h>
#include <string.h>
#include <exec/memory.h>
#include <libraries/asl.h>

#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/asl.h>
#include <proto/dos.h>


void PutTF(BOOL flag);
BOOL ReadStrings(FILE *filePtr, char ***theList, Remember **remKey);
BOOL WriteStrings(FILE *filePtr, char **charList);
BOOL WriteList(FILE *filePtr, List *itemList);
BOOL ReadList(FILE *filePtr, List *itemList, Remember **remKey);
void ResolveListRefs(void);

#if 0
			printf("\t%d, %d,\t\t\t// LeftEdge, TopEdge\n", gadgObj->rect.minX, gadgObj->rect.minY);
			printf("\t%d, %d,\t\t\t// Width, Height\n",
									gadgObj->rect.maxX - gadgObj->rect.minX + 1,
									gadgObj->rect.maxY - gadgObj->rect.minY + 1);
			printf("\t\"%s\",\t\t\t// GadgetText\n", gadgObj->label);
			printf("\t0L,\t\t\t// TextAttr\n");
			printf("\t%d,\t\t\t// GadgetID\n", nodePtr->itemNum);
			printf("\t%ld,\t\t\t// Flags\n", gadgObj->flags);
			printf("\tgVI,\t\t\t// VisualInfo\n");
			printf("\t0L\t\t\t// UserData\n");
#endif

#define FILEVERSION	1
#define	OFFSET(mem, st)	( (long) &(((st *)0L)->mem))

BOOL DoSaveAs(USHORT code)
{
	LinkNode		*nodePtr;
	GadgetObj	*gadgObj;
	FILE			*filePtr;
	ULONG			lval;
	short			itemNum;
	char			buf[MAX_TEXT_LEN],
					fileName[256];
	TextAttr		tempAttr;
	USHORT		len;
	
/*	gFileRequest->rf_File[0] = 0;	*/

	if (!RequestFile(gFileRequest))
		return FALSE;
	
	strcpy(fileName, gFileRequest->rf_Dir);
	if (!AddPart(fileName, gFileRequest->rf_File, 256))
	{
		printf("Error in file name\n");
		return FALSE;
	}
		
	memset(&tempAttr, 0, sizeof(TextAttr));
	
	//	Open the output file for binary access
	
	filePtr = fopen(fileName, "wb");
	if (!filePtr)
	{
		printf("Error: unable to open file '%s'\n", fileName);
		return FALSE;
	}
	
	// write out the file signature data (signature='MAKR', plus FILEVERSION)
	
	lval = 'MAKR';
	if (fwrite( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
		
	lval = FILEVERSION;
	if (fwrite( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
	
	// write out any supplemental header data
	
	lval = 4 * sizeof(UWORD) + 2 * sizeof(BOOL);			// sizeof the supplemental header data
	if (fwrite( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
	
	// fwrite here for header data
	
	if (fwrite( (void *) &gWindPtr->LeftEdge, 4L * sizeof(UWORD), 1, filePtr) != 1)
		goto error;
	if (fwrite( (void *) &gWindSizeable, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	if (fwrite( (void *) &gEditMode, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	if (fwrite( (void *) &gDisableAll, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	
	// now write out the individual data for each object in the list

	for (nodePtr=gObjList; nodePtr; nodePtr=nodePtr->next)
	{
	
		// save the data that is common to all objects
		
		if (fwrite( (void *) &nodePtr->type, sizeof(LinkNode) - OFFSET(type, LinkNode), 1, filePtr) != 1)
			goto error;
	
		// if this is a gadget based object, dispose the data common to all gadgets
		
		if (GetGadgetData(nodePtr, 0L, 0L, 0L, 0L, 0L))
		{
			
			if (fwrite( (void *) &((GadgetObj *)nodePtr)->flags, sizeof(ULONG), 1, filePtr) != 1)
				goto error;
			len = strlen(((GadgetObj *)nodePtr)->label) + 1;
			if (fwrite( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
				goto error;
			if (len)
				if (fwrite( (void *) &((GadgetObj *)nodePtr)->label, len, 1, filePtr) != 1)
					goto error;
			if (fwrite( (void *) &((GadgetObj *)nodePtr)->disable, sizeof(BOOL), 1, filePtr) != 1)
				goto error;
		}
		
		// now write out the data that is specific to each object type
		
		switch(nodePtr->type)
		{
			case OTYPE_Text:
				if (fwrite( (void *) &((TextObj *)nodePtr)->copyText,
							sizeof(TextObj) - OFFSET(copyText, TextObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Button:
				break;
	
			case OTYPE_Cycle:
				if (fwrite( (void *) &((CycleObj *)nodePtr)->activeItem, sizeof(UWORD), 1, filePtr) != 1)
					goto error;
				
				if (!WriteStrings(filePtr, ((CycleObj *)nodePtr)->textLabels))
					goto error;
					
				break;
	
			case OTYPE_MX:
				if (fwrite( (void *) &((MXObj *)nodePtr)->activeItem, 2 * sizeof(UWORD), 1, filePtr) != 1)
					goto error;
				
				if (!WriteStrings(filePtr, ((MXObj *)nodePtr)->textLabels))
					goto error;
				break;
	
			case OTYPE_CheckBox:
				if (fwrite( (void *) &((CheckBoxObj *)nodePtr)->checked,
							sizeof(CheckBoxObj) - OFFSET(checked, CheckBoxObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Scroller:
				if (fwrite( (void *) &((ScrollerObj *)nodePtr)->topItem,
							sizeof(ScrollerObj) - OFFSET(topItem, ScrollerObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Slider:
				if (fwrite( (void *) &((SliderObj *)nodePtr)->minLevel,
							sizeof(SliderObj) - OFFSET(minLevel, SliderObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_String:
				if (fwrite( (void *) &((StringObj *)nodePtr)->maxChars,
							sizeof(StringObj) - OFFSET(maxChars, StringObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Palette:
				if (fwrite( (void *) &((PaletteObj *)nodePtr)->firstColor,
							sizeof(PaletteObj) - OFFSET(firstColor, PaletteObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_ListView:
	
				// walk the gadget list & find the specified gadget
				
				itemNum = -1;
				if (((ListViewObj *)nodePtr)->stringObj)
				{
					for (gadgObj= (GadgetObj *) gObjList, itemNum=0; gadgObj;
												gadgObj=gadgObj->next, itemNum++)
						if ( ((ListViewObj *)nodePtr)->stringObj == gadgObj )
							break;
					
					if (!gadgObj || gadgObj->type != OTYPE_String)
					{
						itemNum = -1;
						((ListViewObj *)nodePtr)->stringObj = 0L;
					}
				}
				((ListViewObj *)nodePtr)->tempStringID = itemNum;
				
				if (fwrite( (void *) &((ListViewObj *)nodePtr)->topItem,
							sizeof(ListViewObj) - OFFSET(topItem, ListViewObj), 1, filePtr) != 1)
					goto error;
				
				if (!WriteList(filePtr, &((ListViewObj *)nodePtr)->itemList))
					goto error;
					
				break;
			
			case OTYPE_IText:
				if (fwrite( (void *) &((ITextObj *)nodePtr)->iText,
							3 * sizeof(UBYTE) + 2 * sizeof(SHORT), 1, filePtr) != 1)
					goto error;
				
				if (((ITextObj *)nodePtr)->iText.ITextFont)
				{
					if (fwrite( (void *) &((ITextObj *)nodePtr)->iText.ITextFont->ta_YSize,
								sizeof(UWORD) + 2 * sizeof(UBYTE), 1, filePtr) != 1)
						goto error;
					len = strlen( ((ITextObj *)nodePtr)->iText.ITextFont->ta_Name ) + 1;
					if (fwrite( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
						goto error;
					if (fwrite( (void *) ((ITextObj *)nodePtr)->iText.ITextFont->ta_Name,
								len, 1, filePtr) != 1)
						goto error;
				}
				else
				{
					memset(buf, 0, FONT_NAME_LEN);
					if (fwrite( (void *) buf, sizeof(UWORD) + 2 * sizeof(UBYTE), 1, filePtr) != 1)
						goto error;
					len = 0;
					if (fwrite( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
						goto error;
				}
				
				len = strlen( ((ITextObj *)nodePtr)->label ) + 1;
				if (fwrite( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
					goto error;
				if (fwrite( (void *) ((ITextObj *)nodePtr)->label, len, 1, filePtr) != 1)
					goto error;
					
				break;
		}
	}
	
	fclose(filePtr);
	ReDraw();
	return TRUE;
	
error:

	printf("An error occurred while writing file '%s'\n", fileName);
	fclose(filePtr);
	return FALSE;
}

BOOL DoOpen(USHORT code)
{
	LinkNode		*nodePtr;
	FILE			*filePtr;
	ULONG			lval;
	UWORD			dim[4], objType, sval, len;
	char			fileName[256];
	
/*	gFileRequest->rf_File[0] = 0;	*/

	if (!RequestFile(gFileRequest))
		return FALSE;
	
	strcpy(fileName, gFileRequest->rf_Dir);
	if (!AddPart(fileName, gFileRequest->rf_File, 256))
	{
		printf("Error in file name\n");
		return FALSE;
	}
		
	
	//	Open the file for binary read access
	
	filePtr = fopen(fileName, "rb");
	if (!filePtr)
	{
		printf("Error: unable to open file '%s'\n", fileName);
		return FALSE;
	}
	
	// read the file signature data (signature='MAKR', plus FILEVERSION)
	
	if (fread( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
	if (lval != 'MAKR')
	{
		printf("Error: file has a bad signature!\n");
		goto error;
	}
		
	if (fread( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
	if (lval != FILEVERSION)
	{
		printf("Error: file is of an incompatible version\n");
		goto error;
	}	

	// read any supplemental header data
	
	if (fread( (void *) &lval, sizeof(ULONG), 1, filePtr) != 1)
		goto error;
	
	if (fread( (void *) dim, 4L * sizeof(UWORD), 1, filePtr) != 1)
		goto error;
	if (fread( (void *) &gWindSizeable, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	if (fread( (void *) &gEditMode, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	if (fread( (void *) &gDisableAll, sizeof(BOOL), 1, filePtr) != 1)
		goto error;
	
	NewWindow(dim[0], dim[1], dim[2], dim[3], gWindSizeable);
	
	while (gObjList)
		DisposeObj( gObjList );
	gSelObj = 0L;
	
	// now read in the individual data for each object in the list

	while (fread( (void *) &objType, sizeof(UWORD), 1, filePtr) == 1)
	{
		nodePtr = AllocateObj(objType);
		if (!nodePtr)
		{
			printf("Error: failed to allocate an object!\n");
			goto error;
		}
			
		if (fread( (void *) &sval, sizeof(UWORD), 1, filePtr) != 1)
			goto error;
		if (sval != nodePtr->numBytes)
		{
			printf("Error: Object size for '%s' does not match\n", gObjName[objType]);
			goto error;
		}
		
		if (fread( (void *) &nodePtr->rect, sizeof(Rect), 1, filePtr) != 1)
			goto error;
			
		// if this is a gadget based object, read the data common to all gadgets
		
		if (GetGadgetData(nodePtr, 0L, 0L, 0L, 0L, 0L))
		{
			
			if (fread( (void *) &((GadgetObj *)nodePtr)->flags, sizeof(ULONG), 1, filePtr) != 1)
				goto error;
			if (fread( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
				goto error;
			if (len)
				if (fread( (void *) &((GadgetObj *)nodePtr)->label, len, 1, filePtr) != 1)
					goto error;
			if (fread( (void *) &((GadgetObj *)nodePtr)->disable, sizeof(BOOL), 1, filePtr) != 1)
				goto error;
		}
		
		// now read in the data that is specific to each object type
		
		switch(nodePtr->type)
		{
			case OTYPE_Text:
				if (fread( (void *) &((TextObj *)nodePtr)->copyText,
							sizeof(TextObj) - OFFSET(copyText, TextObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Button:
				break;
	
			case OTYPE_Cycle:
				if (fread( (void *) &((CycleObj *)nodePtr)->activeItem, sizeof(UWORD), 1, filePtr) != 1)
					goto error;
				
				if (!ReadStrings(filePtr, &((CycleObj *)nodePtr)->textLabels,
										&((CycleObj *)nodePtr)->remKey))
					goto error;
				break;
	
			case OTYPE_MX:
				if (fread( (void *) &((MXObj *)nodePtr)->activeItem, 2 * sizeof(UWORD), 1, filePtr) != 1)
					goto error;
				
				if (!ReadStrings(filePtr, &((MXObj *)nodePtr)->textLabels,
										&((MXObj *)nodePtr)->remKey))
					goto error;
				break;
	
			case OTYPE_CheckBox:
				if (fread( (void *) &((CheckBoxObj *)nodePtr)->checked,
							sizeof(CheckBoxObj) - OFFSET(checked, CheckBoxObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Scroller:
				if (fread( (void *) &((ScrollerObj *)nodePtr)->topItem,
							sizeof(ScrollerObj) - OFFSET(topItem, ScrollerObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Slider:
				if (fread( (void *) &((SliderObj *)nodePtr)->minLevel,
							sizeof(SliderObj) - OFFSET(minLevel, SliderObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_String:
				if (fread( (void *) &((StringObj *)nodePtr)->maxChars,
							sizeof(StringObj) - OFFSET(maxChars, StringObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_Palette:
				if (fread( (void *) &((PaletteObj *)nodePtr)->firstColor,
							sizeof(PaletteObj) - OFFSET(firstColor, PaletteObj), 1, filePtr) != 1)
					goto error;
				break;
	
			case OTYPE_ListView:
				( (ListViewObj *) nodePtr)->stringObj = 0L;
				if (fread( (void *) &((ListViewObj *)nodePtr)->topItem,
							sizeof(ListViewObj) - OFFSET(topItem, ListViewObj), 1, filePtr) != 1)
					goto error;
					
				if (!ReadList(filePtr, &((ListViewObj *)nodePtr)->itemList,
									&((ListViewObj *)nodePtr)->remKey))
					goto error;
/*					
				if (fread( (void *) &itemNum, sizeof(UWORD), 1, filePtr) != 1)
					goto error;
*/				
				break;
			
			case OTYPE_IText:
				if (fread( (void *) &((ITextObj *)nodePtr)->iText,
							3 * sizeof(UBYTE) + 2 * sizeof(SHORT), 1, filePtr) != 1)
					goto error;
				
				if (fread( (void *) &((ITextObj *)nodePtr)->textAttr.ta_YSize,
								sizeof(UWORD) + 2 * sizeof(UBYTE), 1, filePtr) != 1)
					goto error;
					
				if (fread( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
					goto error;
					
				if (len)
				{
					if (fread( (void *) ((ITextObj *)nodePtr)->fontName, len, 1, filePtr) != 1)
						goto error;				
					((ITextObj *)nodePtr)->iText.ITextFont = &((ITextObj *)nodePtr)->textAttr;
				}
				else
				{
					((ITextObj *)nodePtr)->iText.ITextFont = 0L;
					((ITextObj *)nodePtr)->fontName[0] = 0;
				}
				
				((ITextObj *)nodePtr)->textAttr.ta_Name = ((ITextObj *)nodePtr)->fontName;
					
				if (fread( (void *) &len, sizeof(USHORT), 1, filePtr) != 1)
					goto error;
					
				if (fread( (void *) ((ITextObj *)nodePtr)->label, len, 1, filePtr) != 1)
					goto error;
					
				((ITextObj *)nodePtr)->iText.IText = (STRPTR) ((ITextObj *)nodePtr)->label;
					
				break;
	
		}
		if (GetGadgetData(nodePtr, 0L, 0L, 0L, 0L, 0L))
		{
			if (MakeGadget(nodePtr) == 2)
			{
				// the operation failed, so nuke the object
				
				DisposeObj(nodePtr);
			}
		}
	}
	
	fclose(filePtr);
	ResolveListRefs();		// remake the ListView gadgets with user specified string gadgets
									// attached
	ReDraw();
	return TRUE;
	
error:

	printf("An error occurred while reading file '%s'\n", fileName);
	fclose(filePtr);
	return FALSE;
}

void ResolveListRefs(void)
{
	ListViewObj		*listPtr;
	StringObj		*stringPtr;
	
	for (listPtr=(ListViewObj *) gObjList; listPtr; listPtr=listPtr->next)
	{
		if (listPtr->type == OTYPE_ListView)
		{
			if (listPtr->tempStringID >= 0)
			{
					stringPtr = FindNode(&gObjList, listPtr->tempStringID);
					if (stringPtr && stringPtr->type == OTYPE_String)
						listPtr->stringObj = stringPtr;
						
					MakeGadget(listPtr);
			}
			else
				listPtr->stringObj = 0L;
		}
	}
}

BOOL ReadStrings(FILE *filePtr, char ***theList, Remember **remKey)
{
	UWORD		numNode, iNode;
	ULONG		totLength, iDest;
	char		*charPtr, **charList,
				*tempMem = 0L,
				*tempPtr, *strPtr;
	
	if (fread( (void *) &numNode, sizeof(UWORD), 1, filePtr) != 1)
		return FALSE;
	if (fread( (void *) &totLength, sizeof(ULONG), 1, filePtr) != 1)
		return FALSE;
					
	charPtr = AllocRemember( remKey,
							(ULONG) numNode * MAX_TEXT_LEN + (numNode + 1) * sizeof(char *),
										MEMF_CLEAR);
	if (!charPtr)
		return FALSE;
		
	*theList = (char **) charPtr;
	if (!totLength)
		return TRUE;

	strPtr = charPtr + (numNode + 1) * sizeof(char *);
	
	// read in the character data
	
	if (!GetMem( (void **) &tempMem, totLength))
		return FALSE;
	tempPtr = tempMem;
			
	if (fread( (void *) tempMem, totLength, 1, filePtr) != 1)
	{
		DropMem( (void **) &tempMem, totLength);
		return FALSE;
	}
		
	// write out the character data
					
	charList = (char **) charPtr;
	for (iNode=0; iNode<numNode; iNode++)
	{
		iDest = 0;
		while (*tempPtr && iDest < MAX_TEXT_LEN - 1)
			strPtr[iNode * MAX_TEXT_LEN + iDest++] = *tempPtr++;
		tempPtr++;
		strPtr[iNode * MAX_TEXT_LEN + iDest] = 0;
		charList[iNode] = &strPtr[iNode * MAX_TEXT_LEN];
	}
	charList[iNode] = 0L;
		
	DropMem( (void **) &tempMem, totLength);
	
	return TRUE;
}

BOOL WriteStrings(FILE *filePtr, char **charList)
{
	UWORD		numNode = 0, i;
	ULONG		totLength = 0;
	
	// Count strings & figure total length
				
	while (charList[numNode])
		totLength += strlen(charList[numNode++]) + 1;
				
	if (fwrite( (void *) &numNode, sizeof(UWORD), 1, filePtr) != 1)
		return FALSE;
	if (fwrite( (void *) &totLength, sizeof(ULONG), 1, filePtr) != 1)
		return FALSE;
					
	// write out the character data
				
	if (totLength)
	{
		for (i=0; i<numNode; i++)
		{
			if (fwrite( (void *) charList[i], strlen(charList[i]) + 1, 1, filePtr) != 1)
				return FALSE;
		}
	}
	
	return TRUE;
}

BOOL WriteList(FILE *filePtr, List *itemList)
{
	struct Node		*amigaNode;
	UWORD				numNode;
	ULONG				totLength = 0;
	
	
	// Count strings & figure total length
				
	for (		amigaNode = itemList->lh_Head, numNode=0; amigaNode->ln_Succ;
				amigaNode=amigaNode->ln_Succ, numNode++ )
		totLength += strlen(amigaNode->ln_Name) + 1;
				
	if (fwrite( (void *) &numNode, sizeof(UWORD), 1, filePtr) != 1)
		return FALSE;
	if (fwrite( (void *) &totLength, sizeof(ULONG), 1, filePtr) != 1)
		return FALSE;
	
	if (totLength)
	{
		// write out the character data
					
		for (	amigaNode = itemList->lh_Head; amigaNode->ln_Succ; amigaNode=amigaNode->ln_Succ )
		{
			if (fwrite( (void *) amigaNode->ln_Name, strlen(amigaNode->ln_Name)+1, 1, filePtr) != 1)
				return FALSE;
		}
	}
	
	return TRUE;
}

BOOL ReadList(FILE *filePtr, List *itemList, Remember **remKey)
{
	struct Node		*amigaNode;
	UWORD				numNode, iNode;
	ULONG				totLength, iDest;
	char				*charPtr,
						*tempMem = 0L,
						*tempPtr = 0L;
	
	if (fread( (void *) &numNode, sizeof(UWORD), 1, filePtr) != 1)
		return FALSE;
	if (fread( (void *) &totLength, sizeof(ULONG), 1, filePtr) != 1)
		return FALSE;
	
	NewList(itemList);

	if (totLength)
	{
		charPtr = AllocRemember(remKey, MAX_TEXT_LEN * (ULONG) numNode, MEMF_CLEAR);
		amigaNode = (Node *) AllocRemember(remKey, sizeof(struct Node) * (ULONG) numNode,
														MEMF_CLEAR);
		if (!charPtr || !amigaNode)
		{
			printf("Error: out of memory\n");
			return FALSE;
		}

		if (!GetMem( (void **) &tempMem, totLength))
			return FALSE;
		tempPtr = tempMem;
			
		if (fread( (void *) tempMem, totLength, 1, filePtr) != 1)
		{
			DropMem( (void **) &tempMem, totLength);
			return FALSE;
		}
		
		// write out the character data
					
		for (iNode=0; iNode<numNode; iNode++)
		{
			iDest = 0;
			while (*tempPtr && iDest < MAX_TEXT_LEN - 1)
				charPtr[iNode * MAX_TEXT_LEN + iDest++] = *tempPtr++;
			tempPtr++;
			charPtr[iNode * MAX_TEXT_LEN + iDest] = 0;
			amigaNode[iNode].ln_Name = &charPtr[iNode * MAX_TEXT_LEN];
			AddTail(itemList, &amigaNode[iNode]);
		}
		
		DropMem( (void **) &tempMem, totLength);
	}
	
	return TRUE;
}
       
