/****************************************************************************
 * fs/nxffs/nxffs_dirent.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/fs/fs.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/kmalloc.h>

#include "nxffs.h"

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct nxffs_dir_s
{
  struct fs_dirent_s base;
  off_t offset;
};

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: nxffs_opendir
 *
 * Description:
 *   Open a directory for read access
 *
 ****************************************************************************/

int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
                  FAR struct fs_dirent_s **dir)
{
  FAR struct nxffs_volume_s *volume;
  FAR struct nxffs_dir_s *ndir;
  int ret;

  finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL");

  /* Sanity checks */

  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);

  /* Recover the file system state from the NuttX inode instance */

  volume = mountpt->i_private;
  ndir = kmm_zalloc(sizeof(*ndir));
  if (ndir == NULL)
    {
      return -ENOMEM;
    }

  ret = nxmutex_lock(&volume->lock);
  if (ret < 0)
    {
      goto errout_with_ndir;
    }

  /* The requested directory must be the volume-relative "root" directory */

  if (relpath && relpath[0] != '\0')
    {
      ret = -ENOENT;
      goto errout_with_lock;
    }

  /* Set the offset to the offset to the first valid inode */

  ndir->offset = volume->inoffset;
  nxmutex_unlock(&volume->lock);
  *dir = &ndir->base;
  return 0;

errout_with_lock:
  nxmutex_unlock(&volume->lock);

errout_with_ndir:
  kmm_free(ndir);
  return ret;
}

/****************************************************************************
 * Name: nxffs_closedir
 *
 * Description:
 *   Close directory
 *
 ****************************************************************************/

int nxffs_closedir(FAR struct inode *mountpt,
                   FAR struct fs_dirent_s *dir)
{
  DEBUGASSERT(dir);
  kmm_free(dir);
  return 0;
}

/****************************************************************************
 * Name: nxffs_readdir
 *
 * Description: Read the next directory entry
 *
 ****************************************************************************/

int nxffs_readdir(FAR struct inode *mountpt,
                  FAR struct fs_dirent_s *dir,
                  FAR struct dirent *dentry)
{
  FAR struct nxffs_volume_s *volume;
  FAR struct nxffs_dir_s *ndir;
  struct nxffs_entry_s entry;
  off_t offset;
  int ret;

  /* Sanity checks */

  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);

  /* Recover the file system state from the NuttX inode instance */

  volume = mountpt->i_private;
  ndir = (FAR struct nxffs_dir_s *)dir;
  ret = nxmutex_lock(&volume->lock);
  if (ret < 0)
    {
      goto errout;
    }

  /* Read the next inode header from the offset */

  offset = ndir->offset;
  ret = nxffs_nextentry(volume, offset, &entry);

  /* If the read was successful, then handle the reported inode.  Note
   * that when the last inode has been reported, the value -ENOENT will
   * be returned.. which is correct for the readdir() method.
   */

  if (ret == OK)
    {
      /* Return the filename and file type */

      finfo("Offset %jd: \"%s\"\n", (intmax_t)entry.hoffset, entry.name);
      dentry->d_type = DTYPE_FILE;
      strlcpy(dentry->d_name, entry.name, sizeof(dentry->d_name));

      /* Discard this entry and set the next offset. */

      ndir->offset = nxffs_inodeend(volume, &entry);
      nxffs_freeentry(&entry);
      ret = OK;
    }

  nxmutex_unlock(&volume->lock);

errout:
  return ret;
}

/****************************************************************************
 * Name: nxffs_rewindir
 *
 * Description:
 *   Reset directory read to the first entry
 *
 ****************************************************************************/

int nxffs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
{
  FAR struct nxffs_volume_s *volume;
  int ret;

  finfo("Entry\n");

  /* Sanity checks */

  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);

  /* Recover the file system state from the NuttX inode instance */

  volume = mountpt->i_private;
  ret = nxmutex_lock(&volume->lock);
  if (ret < 0)
    {
      goto errout;
    }

  /* Reset the offset to the FLASH offset to the first valid inode */

  ((FAR struct nxffs_dir_s *)dir)->offset = volume->inoffset;
  ret = OK;

  nxmutex_unlock(&volume->lock);

errout:
  return ret;
}