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

#include <z_env.hpp>
#if !defined(__IBMCPP__)
#	include <direct.h>
#	include <dos.h>
#	include <errno.h>
#endif
#if !defined(ZAF_WIN32)
#	if !defined(__WATCOMC__)
#		include <dirent.h>
#	endif
#	include <sys/stat.h>
#endif

#include <z_cset.hpp>
#include <z_ctype.hpp>
#include <z_dskfil.hpp>
#include <z_stdlib.hpp>
#include <z_string.hpp>
#include <z_unistd.hpp>
#include <z_utils.hpp>

// ----- ZafDiskFileSystem --------------------------------------------------

int ZafDiskFileSystem::ChDir(const ZafIChar *newPath, ZafStringID,
	ZafNumberID)
{
	// Move to the new directory.
	return (chdir(newPath));
}
int ZafDiskFileSystem::FindNext(ZafFileInfoStruct &fileInfo,
	const ZafIChar *searchName, ZafStringID , ZafNumberID )
{
	// Initialize the search variables.
	int tError = 0;
	char osName[ZAF_MAXPATHLEN];
	ZafConvertToOSString(searchName, osName, false);

	// Prepare for the next lookup.
	if (fileInfo.name)
	{
		delete [] (ZafIChar *)fileInfo.name;
		fileInfo.name = ZAF_NULLP(ZafIChar);
	}
	fileInfo.length = -1;
	fileInfo.attributes = ZAF_FATTRIB_NONE;

	// Check for find-first implementations.
#if defined(ZAF_WIN32)
	HANDLE *searchHandle = (HANDLE *)fileInfo.internalHandle;
	WIN32_FIND_DATAA findData ;
	if (!searchHandle)
	{
		fileInfo.internalHandle = searchHandle = new HANDLE;
		*searchHandle = FindFirstFileA(osName, &findData);
		if (*searchHandle == INVALID_HANDLE_VALUE)
			tError = -1;
	}
	else if (!FindNextFileA(*searchHandle, &findData))
		tError = -1;

	// Fill the fileInfo structure.
	if (!tError)
	{
		fileInfo.name = ZafConvertToZafString(findData.cFileName);
		fileInfo.length = findData.nFileSizeLow;
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			fileInfo.attributes |= ZAF_FATTRIB_DIRECTORY;
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
			fileInfo.attributes |= ZAF_FATTRIB_READONLY;
	}
#else
	ZafIChar directoryName[ZAF_MAXPATHLEN];
	ZafIChar fileName[ZAF_MAXPATHLEN];
	StripFullPath(searchName, directoryName, fileName);

	DIR *directory = (DIR *)fileInfo.internalHandle;
	if (!directory)
	{
		if (directoryName[0])
		{
			char osDirectoryName[ZAF_MAXPATHLEN];
			ZafConvertToOSString(directoryName, osDirectoryName, false);
			fileInfo.internalHandle = directory = opendir(osDirectoryName);
		}
		else
			fileInfo.internalHandle = directory = opendir(".");
	}

	dirent *info = (directory ? readdir(directory) : ZAF_NULLP(dirent));
	while (info)
	{
		fileInfo.name = ZafConvertToZafString(info->d_name);

		struct stat fileStats;
		ZafIChar fullPath[ZAF_MAXPATHLEN];
		MakeFullPath(fullPath, directoryName, fileInfo.name);	
 		ZafConvertToOSString(fullPath, osName, false);
		stat(osName, &fileStats);
		
#	if defined(ZAF_WIN16)
		strupr((ZafIChar *)fileInfo.name);
		strupr(fileName);
#	endif
		if (WildStrcmp(fileInfo.name, fileName) == 0)
		{
			fileInfo.length = fileStats.st_size;
			if (fileStats.st_mode & S_IFDIR)
				fileInfo.attributes |= ZAF_FATTRIB_DIRECTORY;
#	if defined(S_IWUSR) && defined(S_IWGRP) && defined(S_IWOTH)
			if ((fileStats.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
#	else
			if (!(fileStats.st_mode & S_IWRITE))
#	endif
				fileInfo.attributes |= ZAF_FATTRIB_READONLY;
			break;
		}
		else
		{
			delete [] (ZafIChar *)fileInfo.name;
			fileInfo.name = ZAF_NULLP(ZafIChar);
		}

		info = readdir(directory);
	}

	if (!info)
	{
		tError = -1;
		closedir(directory);
		fileInfo.internalHandle = ZAF_NULLP(void);
	}
#endif

	// Return the error status.
	return (tError);
}

int ZafDiskFileSystem::FindClose(ZafFileInfoStruct &fileInfo)
{
	int tError = -1;
#if defined(ZAF_WIN32)
	HANDLE *searchHandle = (HANDLE *)fileInfo.internalHandle;
 	if (searchHandle)
	{
		::FindClose(*searchHandle);
		delete searchHandle;
		tError = 0;
	}
#else
	DIR *directory = (DIR *)fileInfo.internalHandle;
 	if (directory)
	{
		closedir(directory);
		tError = 0;
	}
#endif
	delete [] (ZafIChar *)fileInfo.name;
 	fileInfo.internalHandle = ZAF_NULLP(void);
	return (tError);
}

int ZafDiskFileSystem::GetDriveNames(ZafIChar **getDriveName[])
{
	int drives = 0;
	bool available[26];

	for (int drive = 0; drive < 26; drive++)
	{
#if defined(ZAF_WIN32)
		char achBuf[10];
		sprintf(achBuf, "%c:\\", drive + 'A');
		UINT uiType=GetDriveTypeA(achBuf);
		if (uiType!=DRIVE_UNKNOWN && uiType!=DRIVE_NO_ROOT_DIR)
#else
		if (GetDriveType(drive))
#endif
		{
			drives++;
			available[drive] = true;
		}
		else
			available[drive] = false;
	}

	if (getDriveName)
	{
		ZafIChar **driveName = new ZafIChar *[drives + 1];
		int availableIndex = 0;
		for (int index = 0; index < drives; index++)
		{
			while (!available[availableIndex++])
				;
			driveName[index] = new ZafIChar[3];
			sprintf(driveName[index], ZAF_ITEXT("%c:"), 'A' + availableIndex - 1);
		}
		driveName[drives] = ZAF_NULLP(ZafIChar);

		*getDriveName = driveName;
	}
	return (drives);
}

int ZafDiskFileSystem::MkDir(const ZafIChar *pathName, ZafStringID ,
	ZafNumberID)
{
	char osName[ZAF_MAXPATHLEN];
	ZafConvertToOSString(pathName, osName, false);

#if defined(ZAF_POSIX)
	return (mkdir(osName, 0777));
#else
	return (mkdir(osName));
#endif
}

int ZafDiskFileSystem::RmDir(const ZafIChar *pathName, bool deleteContents)
{
	char osPath[ZAF_MAXPATHLEN];
	ZafConvertToOSString(pathName, osPath, false);

	if (deleteContents)
	{
		ZafIChar currentDirectory[ZAF_MAXPATHLEN];
		GetCWD(currentDirectory, ZAF_MAXPATHLEN);
		ChDir(pathName);

		ZafFileInfoStruct fileInfo;
		int finished = FindFirst(fileInfo, ZAF_ITEXT("*"));
		while (!finished)
		{
			if (fileInfo.Directory() &&
				strcmp(fileInfo.name, parentDirectoryName) &&
				strcmp(fileInfo.name, currentDirectoryName))
				RmDir(fileInfo.name, true);
			else if (!fileInfo.Directory())
				Remove(fileInfo.name);
			finished = FindNext(fileInfo, ZAF_ITEXT("*"));
		}

		FindClose(fileInfo);
		ChDir(currentDirectory);
	}

	int result = 0;
#if defined(__BCPLUSPLUS__) || defined(__TCPLUSPLUS__) || defined(__WATCOMC__)
	if (rmdir(osPath) != 0)
		result = -1;
#elif defined(_MSC_VER)
	if (_rmdir(osPath) != 0)
		result = -1;
#else
	//??? Not Implemented yet.
#endif
	return (result);
}

int ZafDiskFileSystem::SetDrive(const ZafIChar *driveName)
{
#if defined(__BCPLUSPLUS__) || defined(__TCPLUSPLUS__) || defined(_MSC_VER)
	return (_chdrive(toupper(*driveName) - 'A' + 1));
#elif defined(__WATCOMC__) && !defined(__QNX__)
	unsigned setDrive = toupper(*driveName) - 'A' + 1;
	unsigned getDrive = 0;
	unsigned drives;
	_dos_setdrive(setDrive, &drives);
	_dos_getdrive(&getDrive);
	return ((getDrive == setDrive) ? 0 : -1);
#else
	//??? Not implemented yet.
	return (-1);
#endif
}

bool ZafDiskFileSystem::ValidName(const ZafIChar *name, bool create)
{
	// First, try a simple open-then try for create mode.
	FILE *tFile = fopen(name, ZAF_ITEXT("r"));
	if (tFile)
	{
		fclose(tFile);
		return (true);
	}
	else if (create)
	{
		tFile = fopen(name, ZAF_ITEXT("a"));
		if (tFile)
		{
			fclose(tFile);
			unlink(name);
			return (true);
		}
	}

	// Failure.
	return (false);
}

// ----- ZafDiskFile --------------------------------------------------------

ZafOffset ZafDiskFile::Length(void) const
{
	// Return the file length.
	ZafOffset position = -1;
	if (file)
	{
		long currentPosition = ftell(file);
		fseek(file, 0, ZAF_SEEK_END);
		position = ftell(file);
		fseek(file, currentPosition, ZAF_SEEK_START);
	}
	return (position);
}

int ZafDiskFile::Seek(ZafOffset offset, ZafSeek location)
{
	// Seek into the file.
	int result = -1;
	if (file)
		result = fseek(file, offset, (int)location);

	if (result)
		error = ZAF_ERROR_FILE_POSITION;
	return (result);
}

ZafOffset ZafDiskFile::Tell(void) const
{
	// Tell the current offset.
	ZafOffset position = -1;
	if (file)
		position = ftell(file);
	return (position);
}

// ----- I/O ----------------------------------------------------------------

int ZafDiskFile::ReadData(void *buffer, int size)
{
	int result = fread((char *)buffer, 1, size, file);
	if (ferror(file))
		error = ZAF_ERROR_FILE_READ;
	return (result);
}

int ZafDiskFile::WriteData(const void *buffer, int size)
{
	// Check for a valid size.
	if (size == 0)
		return (size);

	if (!file)
		return (0);

	int result = fwrite((char *)buffer, 1, size, file);
	if (result != size)
		error = ZAF_ERROR_FILE_WRITE;
	return (result);
}

