/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- April 1998
 */

/*
 * MacBinMaps.cpp --- class to store the mac binary encoding mappings
 */

#include <stdlib.h>
#include <Resources.h>
#include <TextUtils.h>

#include "MacBinMaps.h"
#include "Persistent.h"

static const	OSType		kTypesListResType	= 'bin#';
static const	short		kTypesListResID		= 128;
static const	short		kTypesDescResID		= 128;

class PMacBinMaps : public MacBinMaps, public CPersistent
{
public:
	PMacBinMaps(const char *uniqueName) : CPersistent(uniqueName, kNoClass)
	{
		if(entries != 0L)
		{
			free(entries);
			entries = 0L;
		}
	}
	
	virtual ~PMacBinMaps()
	{
	}
	
	virtual unsigned int SizeOf(void) const
	{
		if(entries == 0L)
			return 0;
		return sizeof(MACBINMAPENTRIES) + (entries->numMaps - 1) * sizeof(MACBINMAP);
	}
	
	virtual const void *GetData(void) const
	{
		return entries;
	}
	virtual void SetData(const void *ptr, unsigned int size)
	{
		if(entries != 0L)
		{
			free(entries);
			entries = 0L;
		}
		
		if(size < sizeof(int))
			return;
		MACBINMAPENTRIES *newEntries = (MACBINMAPENTRIES *)ptr;
		if((sizeof(MACBINMAPENTRIES) + (newEntries->numMaps - 1) * sizeof(MACBINMAP)) != size)
			return;
		
		entries = (MACBINMAPENTRIES *)malloc(size);
		if(entries == 0L)
			return;
		
		memcpy(entries, ptr, size);
	}
} gAllBinMaps("P_MacBinMaps");

MacBinMaps::MacBinMaps()
{
	Reset();
}

MacBinMaps::~MacBinMaps()
{
	if(entries != 0L)
		free(entries);
}

void MacBinMaps::Reset(void)
{
	entries = (MACBINMAPENTRIES *)malloc(sizeof(int));
	if(entries == 0L)
		return;
	entries->numMaps = 0;
}

const char *MacBinMaps::GetEnvPlainTypes(void)
{
	static CStr result;
	result = "";
	const MACBINMAPENTRIES * allEntries = gAllBinMaps.Entries();
	if(allEntries == 0L)
		return 0L;
	
	int i;
	for(i = 0; i < allEntries->numMaps; i++)
	{
		const MACBINMAP & curmap = allEntries->maps[i];
		if(curmap.plainBin)
		{
			result << ((char *)&curmap.sig)[0];
			result << ((char *)&curmap.sig)[1];
			result << ((char *)&curmap.sig)[2];
			result << ((char *)&curmap.sig)[3];
			result << ';';
		}
	}
	return result;
}

const char *MacBinMaps::GetEnvTypes(MACENCODING type)
{
	static CStr result;
	result = "";
	const MACBINMAPENTRIES * allEntries = gAllBinMaps.Entries();
	if(allEntries == 0L)
		return 0L;
	
	int i;
	for(i = 0; i < allEntries->numMaps; i++)
	{
		const MACBINMAP & curmap = allEntries->maps[i];
		if(!curmap.plainBin && !curmap.useDefault && curmap.encod == type)
		{
			result << ((char *)&curmap.sig)[0];
			result << ((char *)&curmap.sig)[1];
			result << ((char *)&curmap.sig)[2];
			result << ((char *)&curmap.sig)[3];
			result << ';';
		}
	}
	return result;
}

void MacBinMaps::LoadDefaults(void)
{
	if(gAllBinMaps.SizeOf() == 0L)
	{
		short index = 1;
		
		// take defaults from the '#bin' resource
		Handle	typesHandle = Get1Resource (kTypesListResType, kTypesListResID);
		if (typesHandle == nil)
			return;
		
		HLock(typesHandle);
		Ptr typesPtr = *typesHandle;
		
		short		typesCount = *(short*)typesPtr;
		typesPtr += (2 * sizeof(short)) / sizeof(char); // count + align
		OSType*		walker = (OSType *)typesPtr;

		while (typesCount-- > 0)
		{
			Str255 theString;
			char theCString[256];
			theString[0] = '\0';
			GetIndString(theString, kTypesDescResID, index++);
			p2cstrcpy(theCString, theString);
			
			AddEntry(*walker++, theCString[0] != '\0' ? theCString : "Default type",
				MAC_HQX, true, true);
		}
		HUnlock(typesHandle);
	}
	else
	{
		*this = gAllBinMaps;
	}
}

void MacBinMaps::SaveDefaults(void)
{
	*(MacBinMaps *)&gAllBinMaps = *this;
}

const MACBINMAP *MacBinMaps::AddEntry(OSType sig, const char *desc, MACENCODING encod, bool plainBin, bool useDefault)
{
	if(entries == 0L)
		return 0L;
	
	int i;
	for(i = 0; i < entries->numMaps; i++)
	{
		if(entries->maps[i].sig == sig)
			break;
	}
	
	if(i != entries->numMaps)
	{
		MACBINMAP & curmap = entries->maps[i];
		curmap.sig = sig;
		curmap.desc[0] = '\0';
		if(desc != 0L)
			strncpy(curmap.desc, desc, MAX_MACBIN_DESC);
		curmap.encod = encod;
		curmap.plainBin = plainBin;
		curmap.useDefault = useDefault;
		return &curmap;
	}
	
	entries = (MACBINMAPENTRIES *)realloc(entries, sizeof(MACBINMAPENTRIES) + entries->numMaps * sizeof(MACBINMAP));
	if(entries == 0L)
		return 0L;
	
	MACBINMAP & newmap = entries->maps[entries->numMaps++];
	newmap.sig = sig;
	newmap.desc[0] = '\0';
	if(desc != 0L)
		strncpy(newmap.desc, desc, MAX_MACBIN_DESC);
	newmap.encod = encod;
	newmap.plainBin = plainBin;
	newmap.useDefault = useDefault;
	return &newmap;
}

void MacBinMaps::RemoveEntry(OSType sig)
{
	if(entries == 0L)
		return;
	
	int i;
	for(i = 0; i < entries->numMaps; i++)
	{
		if(entries->maps[i].sig == sig)
			break;
	}
	
	if(i == entries->numMaps)
		return;

	memmove(&entries->maps[i], &entries->maps[i + 1],
		(entries->numMaps - i - 1) * sizeof(MACBINMAP));
	entries->numMaps--;
	entries = (MACBINMAPENTRIES *)realloc(entries, sizeof(MACBINMAPENTRIES) + (entries->numMaps - 1) * sizeof(MACBINMAP));
}

MacBinMaps & MacBinMaps::operator=(const MacBinMaps & otherMap)
{
	if(entries != 0L)
		free(entries);
	Reset();
	if(entries == 0L)
		return *this;

	const MACBINMAPENTRIES *theseEntries = otherMap.Entries();
	if(theseEntries != 0L) for(int i = 0; i < theseEntries->numMaps; i++)
	{
		const MACBINMAP & newmap = theseEntries->maps[i];
		AddEntry(newmap.sig, newmap.desc, newmap.encod, newmap.plainBin, newmap.useDefault);
	}
	return *this;
}
