#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "dirent.h"


DIR*
opendir(const char* pDirName)
{
	struct stat sb;
	DIR*	pDir;
	char*	pEndDirName;
	int	nBufferLen;

	/* sanity checks */
	if (!pDirName) {
		errno = EINVAL;
		return NULL;
	}
	if (stat(pDirName, &sb) != 0) {
		errno = ENOENT;
		return NULL;
	}
	if ((sb.st_mode & S_IFMT) != S_IFDIR) {
		errno = ENOTDIR;
		return NULL;
	}

	/* allocate a DIR structure to return */
	pDir = (DIR *) malloc(sizeof (DIR));

	if (!pDir)
		return NULL;

	/* input directory name length */
	nBufferLen = strlen(pDirName);

	/* copy input directory name to DIR buffer */
	strcpy(pDir->dir_pDirectoryName, pDirName);

	/* point to end of the copied directory name */
	pEndDirName = &pDir->dir_pDirectoryName[nBufferLen - 1];

	/* if directory name did not end in '/' or '\', add '/' */
	if ((*pEndDirName != '/') && (*pEndDirName != '\\')) {
		pEndDirName++;
		*pEndDirName = '/';
	}

	/* now append the wildcard character to the buffer */
	pEndDirName++;
	*pEndDirName = '*';
	pEndDirName++;
	*pEndDirName = '\0';

	/* other values defaulted */
	pDir->dir_nNumFiles = 0;
	pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;	
	pDir->dir_ulCookie = __DIRENT_COOKIE;

	return pDir;
}

void
closedir(DIR *pDir)
{
	/* got a valid pointer? */
	if (!pDir) {
		errno = EINVAL;
		return;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return;
	}

	/* close the WINDOWS32 directory handle */
	if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
		FindClose(pDir->dir_hDirHandle);

	free(pDir);

	return;
}

struct dirent *
readdir(DIR* pDir)
{
	WIN32_FIND_DATA wfdFindData;

	if (!pDir) {
		errno = EINVAL;
		return NULL;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return NULL;
	}

	if (pDir->dir_nNumFiles == 0) {
		pDir->dir_hDirHandle = FindFirstFile(pDir->dir_pDirectoryName, &wfdFindData);
		if (pDir->dir_hDirHandle == INVALID_HANDLE_VALUE)
			return NULL;
	} else if (!FindNextFile(pDir->dir_hDirHandle, &wfdFindData))
			return NULL;

	/* bump count for next call to readdir() or telldir() */
	pDir->dir_nNumFiles++;

	/* fill in struct dirent values */
	pDir->dir_sdReturn.d_ino = -1;
	strcpy(pDir->dir_sdReturn.d_name, wfdFindData.cFileName);

	return &pDir->dir_sdReturn;
}

void
rewinddir(DIR* pDir)
{
	if (!pDir) {
		errno = EINVAL;
		return;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return;
	}

	/* close the WINDOWS32 directory handle */
	if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
		if (!FindClose(pDir->dir_hDirHandle))
			errno = EBADF;

	/* reset members which control readdir() */
	pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
	pDir->dir_nNumFiles = 0;

	return;
}

int
telldir(DIR* pDir)
{
	if (!pDir) {
		errno = EINVAL;
		return -1;
	}

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
		errno = EINVAL;
		return -1;
	}

	/* return number of times readdir() called */
	return pDir->dir_nNumFiles;
}

void
seekdir(DIR* pDir, long nPosition)
{
	if (!pDir)
		return;

	/* sanity check that this is a DIR pointer */
	if (pDir->dir_ulCookie != __DIRENT_COOKIE)
		return;

	/* go back to beginning of directory */
	rewinddir(pDir);

	/* loop until we have found position we care about */
	for (--nPosition; nPosition && readdir(pDir); nPosition--);

	/* flag invalid nPosition value */
	if (nPosition)
		errno = EINVAL;

	return;
}
