//	Zinc Application Framework - Z_FILE.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_cset.hpp>
#include <z_stdlib.hpp>
#include <z_unistd.hpp>
#include <z_file.hpp>
#include <z_string.hpp>
#define ZAF_FILE_INFO
#include "gbl_def.cpp"

#if defined(ZAF_BIGENDIAN)
static void ReverseEndian(void *value, int bytes)
{
	// Converts big endian to little endian or little endian to big endian.
 	ZafUInt8 *buffer = (ZafUInt8 *)value;
	ZafUInt8 temp;
	for (int i = 0; i < bytes / 2; i++)
	{
		temp = buffer[i];
		buffer[i] = buffer[bytes - i - 1];
		buffer[bytes - i - 1] = temp;
	}
}
#endif

// ----- ZafFile ------------------------------------------------------------

ZafFile::ZafFile(ZafFileMode setMode) :
	ZafElement(), error(ZAF_ERROR_NONE), mode(setMode)
{
}

ZafFile::~ZafFile(void)
{
}

// ----- Read functions -----------------------------------------------------

int ZafFile::Read(ZafInt8 &value)
{
	return (ReadData(&value, sizeof(value)));
}

int ZafFile::Read(ZafUInt8 &value)
{
	return (ReadData(&value, sizeof(value)));
}

int ZafFile::Read(ZafInt16 &value)
{
	int bytesRead = ReadData(&value, sizeof(value));
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (bytesRead);
}

int ZafFile::Read(ZafUInt16 &value)
{
	int bytesRead = ReadData(&value, sizeof(value));
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (bytesRead);
}

int ZafFile::Read(ZafInt32 &value)
{
	int bytesRead = ReadData(&value, sizeof(value));
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (bytesRead);
}

int ZafFile::Read(ZafUInt32 &value)
{
	int bytesRead = ReadData(&value, sizeof(value));
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (bytesRead);
}

int ZafFile::Read(ZafIChar *string, int length)
{
	// Initialize the string.
	*string = '\0';

	// Determine the number of ZafIChars to read.
	int i;
	ZafUInt16 readSize;
	int cnt1 = Read(readSize);
	if (!cnt1)
	{
		error = ZAF_ERROR_FILE_READ;
		return (cnt1);
	}
	else if (!readSize)
		return (cnt1);

	// Unicode support can load both Unicode and ISO8859-1 strings without 
	// data loss.  The upper 128 characters of OEM strings are invalid in 
	// Unicode mode.
	bool unicodeRead = (readSize & ZAF_UNICODE_FLAG) ? true : false;
	readSize &= (ZafUInt16)~ZAF_UNICODE_FLAG;
	if (length != -1 && (readSize + 1) > length)
	{
		error = ZAF_ERROR_FILE_READ;
		Seek(-cnt1, ZAF_SEEK_CURRENT);	// restore the file pointer.
		return (-1);
	}

	// Read the string.
	int cnt2 = 0;
	if (unicodeRead)
	{
		for (i = 0; i < readSize; i++)
		{
			ZafUInt16 value16;
			cnt2 += Read(value16);
			string[i] = (ZafIChar)value16;
		}
		string[readSize] = '\0';
		readSize *= (ZafUInt16)sizeof(ZafUInt16);
	}
	else
	{
#if defined(ZAF_UNICODE)
		for (i = 0; i < readSize; i++)
		{
			ZafUInt8 value8;
			cnt2 += Read(value8);
			string[i] = (ZafIChar)value8;
		}
#else
		cnt2 = ReadData(string, readSize);
#endif
		string[readSize] = '\0';
	}

	// Return the number of bytes read.
	if (cnt2 != readSize)
		error = ZAF_ERROR_FILE_READ;
	return (cnt1 + cnt2);
}

int ZafFile::Read(ZafIChar **string)
{
	// Initialize the string.
	*string = ZAF_NULLP(ZafIChar);

	// Determine the number of ZafIChars to read.
	int i;
	ZafUInt16 readSize;
	int cnt1 = Read(readSize);
	if (!cnt1)
	{
		error = ZAF_ERROR_FILE_READ;
		return (cnt1);
	}
	else if (!readSize)
		return (cnt1);

	// Determine the type of read operation.
	bool readUnicode = (readSize & ZAF_UNICODE_FLAG) ? true : false;
	readSize &= (ZafUInt16)~ZAF_UNICODE_FLAG;

	// Read the string.
	int cnt2 = 0;
	*string = new ZafIChar[readSize + 1];
	if (readUnicode)
	{
		for (i = 0; i < readSize; i++)
		{
			ZafUInt16 value16;
			cnt2 += Read(value16);
			(*string)[i] = (ZafIChar)value16;
		}
		(*string)[readSize] = '\0';
		readSize *= (ZafUInt16)sizeof(ZafUInt16);
	}
	else
	{
#if defined(ZAF_UNICODE)
		for (i = 0; i < readSize; i++)
		{
			ZafUInt8 value8;
			cnt2 += Read(value8);
			(*string)[i] = (ZafIChar)value8;
		}
#else
		cnt2 = ReadData(*string, readSize);
#endif
		(*string)[readSize] = '\0';
	}

	// Return the number of bytes read.
	if (cnt2 != readSize)
		error = ZAF_ERROR_FILE_READ;
	return (cnt1 + cnt2);
}

int ZafFile::Read(void *buffer, int size, int num)
{
	return (ReadData(buffer, size * num));
}

// The definition of ZAF_WORD_SIZE == 64 is the following:
// 8 bit chars, 16 bit shorts, 32 bit ints, and 64 bit longs.
// In this case, ZafInt32 is typedefed to int, rather than long, so
// Read(int &value) is the same as Read(ZafInt32 &value).
#if ZAF_WORD_SIZE != 64
int ZafFile::Read(int &value)
{
	ZafInt32 tempValue;
	int bytesRead = Read(tempValue);
	value = (int)tempValue;
	return (bytesRead);
}
#endif

// ----- Write functions ----------------------------------------------------

int ZafFile::Write(ZafInt8 value)
{
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(ZafUInt8 value)
{
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(ZafInt16 value)
{
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(ZafUInt16 value)
{
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(ZafInt32 value)
{
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(ZafUInt32 value)
{
#if defined(ZAF_BIGENDIAN)
	ReverseEndian(&value, sizeof(value));
#endif
	return (WriteData(&value, sizeof(value)));
}

int ZafFile::Write(const ZafIChar *string)
{
	// Don't write the null terminator.
	ZafUInt16 writeSize = (ZafUInt16)(string == ZAF_NULLP(ZafIChar) ? 0 : strlen(string));

	// See if the string is a completely ISO.
	int i;
	ZafUInt16 writeUnicode = 0;
#	if defined(ZAF_UNICODE)
	for (i = 0; i < writeSize; i++)
		if (string[i] > 0x00ff)
		{
			writeUnicode = ZAF_UNICODE_FLAG;
			break;
		}
#	endif

	// Write the length.
	ZafUInt16 tempSize = (ZafUInt16)(writeSize | writeUnicode);
	int cnt1 = Write(tempSize);
	if (!cnt1)
	{
		error = ZAF_ERROR_FILE_READ;
		return (cnt1);
	}
	else if (!writeSize)
		return (cnt1);

	// Write the string.
	int cnt2 = 0;
	if (writeUnicode)
	{
		for (i = 0; i < writeSize; i++)
		{
			ZafUInt16 value16 = (ZafUInt16)string[i];
			cnt2 += Write(value16);
		}
		writeSize *= (ZafUInt16)sizeof(ZafUInt16);
	}
	else
	{
#if defined(ZAF_UNICODE)
		for (i = 0; i < writeSize; i++)
		{
			ZafUInt8 value8 = (ZafUInt8)string[i];
			cnt2 += Write(value8);
		}
#else
		cnt2 = WriteData(string, writeSize);
#endif
	}

	// Return the number of bytes written.
	if (cnt2 != writeSize)
		error = ZAF_ERROR_FILE_READ;
	return (cnt1 + cnt2);
}

int ZafFile::Write(const void *buffer, int size, int num)
{
	return (WriteData(buffer, size * num));
}

// The definition of ZAF_WORD_SIZE == 64 is the following:
// 8 bit chars, 16 bit shorts, 32 bit ints, and 64 bit longs.
// In this case, ZafInt32 is typedefed to int, rather than long, so
// Write(int &value) is the same as Write(ZafInt32 &value).
#if ZAF_WORD_SIZE != 64
int ZafFile::Write(int value)
{
	ZafInt32 tempValue = (ZafInt32)value;
	return (Write(tempValue));
}
#endif

// ----- ZafFileSystem ------------------------------------------------------

ZafFileSystem::ZafFileSystem(void) :
	ZafElement(), error(ZAF_ERROR_NONE)
{
}

ZafFileSystem::~ZafFileSystem(void)
{
}

// ----- ZafPathElement & ZafPath -------------------------------------------

ZafPathElement::ZafPathElement(const ZafIChar *tPath, int length)
{
	// Copy the path name.
	SetPath(tPath, length);
}

ZafError ZafPathElement::SetPath(const ZafIChar *tPath, int length)
{
	// Copy the path name.
	if (length == -1)
		strcpy(path, tPath);
	else
	{
#if defined(ZAF_MACINTOSH)
		strncpy(path, tPath, (unsigned long)length);
#else
		strncpy(path, tPath, length);
#endif
		path[length] = '\0';
	}
	return (ZAF_ERROR_NONE);
}

ZafPath::ZafPath(const ZafIChar *pwd, bool getCwd)
{
	ZafPathElement *element;

	// Check the current working directory.
	if (getCwd)
	{
		ZafIChar cwd[ZAF_MAXPATHLEN];
		if (getcwd(cwd, ZAF_MAXPATHLEN))
		{
			element = new ZafPathElement(cwd);
			element->SetStringID("cwd");
			Add(element);
		}
	}

	// Check the program working directory.
	if (pwd)
	{
		const ZafIChar *endPath = strrchr((ZafIChar *)pwd, ZafOSDirSepStr()[0]);
		element = new ZafPathElement(pwd, endPath ? (int)(endPath - pwd) : -1);
		element->SetStringID("pwd");
		Add(element);
	}

	// Add the environment path.
	Load(ZAF_ITEXT("ZAF_PATH"));
}

ZafPathElement *ZafPath::Add(ZafPathElement *object, ZafPathElement *position)
{
	return ((ZafPathElement *)ZafList::Add(object, position));
}

bool ZafPath::Load(const ZafIChar *envname)
{
#if defined(ZAF_X11)
	const ZafIChar separator = ':';
#elif defined(ZAF_MACINTOSH)
	const ZafIChar separator = ',';
#else
	const ZafIChar separator = ';';
#endif

	// Find the requested path.
	ZafIChar *path = getenv(envname);
	if (!path)
		return (false);

	// Add each path element.
	ZafPathElement *element;
	while (path && *path)
	{
		ZafIChar *endPath = strchr(path, separator);
		if (endPath)
		{
			element = new ZafPathElement(path, (int)(endPath - path));
			path = &endPath[1];
		}
		else
		{
			element = new ZafPathElement(path);
			path = ZAF_NULLP(ZafIChar);
		}
		element->SetStringID(envname);
		Add(element);
	}
	return (true);
}

ZafPathElement *ZafPath::Subtract(ZafPathElement *object)
{
	return ((ZafPathElement *)ZafList::Subtract(object));
}

