/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:49:32 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Emulates POSIX directory readin functions: opendir(), readdir(), etc in Win32.
 * @remarks Directory stream type.
 *          The miscellaneous Unix readdir implementations read directory data
 *          into a buffer and return "struct dirent *" pointers into it.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *------------------------------------------------------------------------------
 * Copyright (C) 2001, 2006 Free Software Foundation, Inc.
 * GNU Lesser General Public License 2.1.
 *****************************************************************************/

#if OS_WINDOWS

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <windows.h>
#include <direct.h>

#include "base/sys.hh"
#include "base/file_dirent.hh"

namespace base {

DIR*
opendir( const char * name )
{
    DIR *dir;
    HANDLE hnd;
    char *file;
    WIN32_FIND_DATA find;

    if (!name or !*name) 
        return NULL;
    file = (char*) malloc (strlen (name) + 3);
    strcpy (file, name);
    if (file[strlen (name) - 1] != '/' and file[strlen (name) - 1] != '\\')
        strcat (file, "/*");
    else
        strcat (file, "*");
  
    if ((hnd = FindFirstFile (file, &find)) == INVALID_HANDLE_VALUE)
    {
        free (file);
        return NULL;
    }

    dir = (DIR*) malloc (sizeof (DIR));
    dir->mask = file;
    dir->fd = (int) hnd;
    dir->data = (char*) malloc (sizeof (WIN32_FIND_DATA));
    dir->allocation = sizeof (WIN32_FIND_DATA);
    dir->size = dir->allocation;
    dir->filepos = 0;
    memcpy (dir->data, &find, sizeof (WIN32_FIND_DATA));
    return dir;
}

struct dirent*
readdir( DIR * dir )
{
    static struct dirent entry;
    WIN32_FIND_DATA *find;

    entry.d_ino = 0;
    entry.d_type = 0;
    find = (WIN32_FIND_DATA *) dir->data;

    if (dir->filepos)
    {
        if ( ! FindNextFile ((HANDLE) dir->fd, find) )
            return NULL;
    }

    entry.d_off = dir->filepos;
    strncpy (entry.d_name, find->cFileName, sizeof (entry.d_name));
    entry.d_reclen = strlen (find->cFileName);
    dir->filepos++;
    return &entry;
}

int
closedir( DIR * dir )
{
    HANDLE hnd = (HANDLE) dir->fd;
    free (dir->data);
    free (dir->mask);
    free (dir);
    return FindClose (hnd) ? 0 : -1;
}

void 
rewinddir( DIR * dir )
{
    HANDLE hnd = (HANDLE) dir->fd;
    WIN32_FIND_DATA *find = (WIN32_FIND_DATA *) dir->data;

    FindClose (hnd);
    hnd = FindFirstFile (dir->mask, find);
    dir->fd = (int) hnd;
    dir->filepos = 0;
}

void 
seekdir( DIR * dir, off_t offset )
{
    off_t n;

    rewinddir (dir);
    for (n = 0; n < offset; n++)
    {
        if (FindNextFile ((HANDLE) dir->fd, (WIN32_FIND_DATA *) dir->data))
            dir->filepos++;
    }
}

off_t 
telldir( DIR * dir )
{
    return dir->filepos;
}

int 
dirfd( DIR * dir )
{
  return dir->fd;
}

} // namespace base

#endif // OS_WINDOWS
