/*
 * Copyright ©2026 Soham Pardeshi. All rights reserved.
 * Permission is hereby granted to students registered for University of
 * Washington CSE 333 for use solely during Summer Quarter 2026 for
 * purposes of the course. No other use, copying, distribution, or
 * modification is permitted without prior written consent. Copyrights
 * for third-party components of this work must be honored. Instructors
 * interested in reusing these course materials should contact the author.
 */

#include "dirutil.h"

#include <dirent.h>  // for DIR, struct dirent, opendir, readdir, closedir
#include <stdlib.h>  // for malloc, free
#include <string.h>  // for strcmp

// The definition of struct dir_iter, hidden from callers.
struct dir_iter {
  DIR* dirp;               // the underlying POSIX directory stream
  struct dirent* current;  // the entry we're currently pointing at, or NULL
};

// Helper: reads the next entry from dirp, skipping "." and "..".
// Returns the resulting dirent pointer (NULL if there are no more entries).
static struct dirent* GetNextRealEntry(DIR* dirp) {
  struct dirent* entry = readdir(dirp);

  // No more entries
  if (entry == NULL) {
    return NULL;
  }

  char* entry_name = entry->d_name;

  // Skip the special entries "." and ".."
  if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
    return GetNextRealEntry(dirp);
  }

  return entry;
}

DirIter* DirIter_Allocate(const char* dirname) {
  DIR* dirp = opendir(dirname);
  if (dirp == NULL) {
    return NULL;
  }

  DirIter* iter = (DirIter*) malloc(sizeof(DirIter));
  if (iter == NULL) {
    closedir(dirp);
    return NULL;
  }

  iter->dirp = dirp;
  iter->current = GetNextRealEntry(dirp);  // prime the first entry
  return iter;
}

void DirIter_Free(DirIter* iter) {
  closedir(iter->dirp);
  free(iter);
}

bool DirIter_IsValid(DirIter* iter) {
  return iter->current != NULL;
}

bool DirIter_Next(DirIter* iter) {
  iter->current = GetNextRealEntry(iter->dirp);
  return iter->current != NULL;
}

const char* DirIter_Get(DirIter* iter) {
  return iter->current->d_name;
}