/*
    dirent
    Copyright (C) 2007 Kazuki IWAMOTO

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

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include "dirent.h"
#include <tchar.h>
#include <errno.h>


TDIR *
topendir (const TCHAR *name)
{
  DWORD dwResult;
  HANDLE hFind;
  LPTSTR lpszFile;
  WIN32_FIND_DATA fnData;
  TCHAR szPath[MAX_PATH];
  TDIR *nDir;

  if (!name)
    {
      errno = EFAULT;
      return NULL;
    }
  if (name[0] == '\0')
    {
      errno = ENOTDIR;
      return NULL;
    }
  hFind = FindFirstFile (name, &fnData);
  if (hFind == INVALID_HANDLE_VALUE)
    {
      errno = ENOENT;
      return NULL;
    }
  if (!FindClose (hFind))
    {
      errno = EFAULT;
      return NULL;
    }
  if (!(fnData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
      errno = ENOTDIR;
      return NULL;
    }
  dwResult = GetFullPathName (name, MAX_PATH - 2, szPath, &lpszFile);
  if (dwResult <= 0 || MAX_PATH <= dwResult)
    {
      errno = EFAULT;
      return NULL;
    }
  nDir = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof (TDIR));
  if (!nDir)
    {
      errno = ENOMEM;
      return NULL;
    }
  nDir->hFind = INVALID_HANDLE_VALUE;
  lstrcpy (nDir->szPath, szPath);
  lstrcat (nDir->szPath, _T("\\*"));
  errno = 0;
  return nDir;
}


struct tdirent *
treaddir (TDIR *nDir)
{
  WIN32_FIND_DATA fnData;

  if (!nDir)
    {
      errno = EFAULT;
      return NULL;
    }
  if (nDir->nStat == 0)
    {
      nDir->hFind = FindFirstFile (nDir->szPath, &fnData);
      nDir->nStat = nDir->hFind == INVALID_HANDLE_VALUE ? -1 : 1;
    }
  else if (nDir->nStat > 0)
    {
      if (FindNextFile (nDir->hFind, &fnData))
        {
          nDir->nStat++;
        }
      else
        {
          FindClose (nDir->hFind);
          nDir->hFind = INVALID_HANDLE_VALUE;
          nDir->nStat = -1;
        }
    }
  errno = 0;
  if (nDir->nStat <= 0)
    return NULL;
  nDir->dd_dir.d_namlen = lstrlen (fnData.cFileName);
  lstrcpy (nDir->dd_dir.d_name, fnData.cFileName);
  return &nDir->dd_dir;
}


int
tclosedir (TDIR *nDir)
{
  int result;

  if (nDir)
    {
      result = nDir->hFind == INVALID_HANDLE_VALUE || FindClose (nDir->hFind)
                                                                    ? 0 : -1;
      HeapFree (GetProcessHeap (), 0, nDir);
      errno = 0;
    }
  else
    {
      result = -1;
      errno = EFAULT;
    }
  return result;
}


void
trewinddir (TDIR *nDir)
{
  if (nDir)
    {
      if (nDir->hFind != INVALID_HANDLE_VALUE)
        {
          FindClose (nDir->hFind);
          nDir->hFind = INVALID_HANDLE_VALUE;
        }
      nDir->nStat = 0;
      errno = 0;
    }
  else
    {
      errno = EFAULT;
    }
}


long
ttelldir (TDIR *nDir)
{
  return nDir ? (errno = 0, nDir->nStat) : (errno = EFAULT, -1);
}


void
tseekdir (TDIR *nDir,
          long  pos)
{
  if (!nDir)
    {
      errno = EFAULT;
      return;
    }
  if (pos < 0)
    {
      if (nDir->hFind != INVALID_HANDLE_VALUE)
        {
          FindClose (nDir->hFind);
          nDir->hFind = INVALID_HANDLE_VALUE;
        }
      nDir->nStat = -1;
    }
  else
    {
      trewinddir (nDir);
      while ((nDir->nStat < pos) && treaddir (nDir));
    }
  errno = 0;
}
