/**
 * \file BGDNetDirectory.cpp
 * \brief Реализация класса Directory
 */

#include "BGDNetPch.h"

#include "BGDNetDirectory.h"

#include <string>
#include <functional>
#include <assert.h>
#include <nwdpath.h>
#include <nwnamspc.h>
#include <nwerror.h>
#include <nwdentry.h>
#include "BGDNetFile.h"
#include "BGDNetVolume.h"
#include "BGDNetServer.h"
#include "BGDNetConnection.h"
#include "BGDNetPath.h"

#ifdef BGDNET_USE_MFC
# ifdef _DEBUG
#   define new DEBUG_NEW
#   undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
# endif
#endif

namespace BGDNet
{

Directory::Directory( const Volume* pVol, const Path& relPath )
  throw(Exception) :
  DirEntry(pVol, relPath)
{
}

Directory::Directory( const Directory* pParent, const Path& subdirName )
  throw(Exception) :
  DirEntry(pParent->vol(), pParent->relPath())
{
  // Необходимо учесть, что в общем случае родительский каталог и имя
  // подкаталога могут быть заданы в различных пространствах имен.
  // Поэтому необходимо узнать имя подкаталога в пространстве имен,
  // соответствующем родительскому каталогу:
  Connection conn(vol()->srv()->connRef());
  DirHandle parentDirHandle(vol(), relPath(), subdirName.ns());
  NW_ENTRY_INFO info;
  NWCCODE err = NWGetNSEntryInfo(
    conn,
    parentDirHandle,
    subdirName,
    subdirName.ns(),
    relPath().ns(),
    SA_ALL,
    IM_ENTRY_NAME,
    &info);
  if (SUCCESS != err)
    throw Exception(err);
  
  // На текущий момент относительный путь содержит путь к родительскому
  // каталогу. Необходимо добавить к нему полученное имя подкаталога:
  relPath() += '\\';
  relPath() += info.entryName;
}

void Directory::getSubdirs( Directories* pColl ) const
  throw(Exception)
{
  pColl->clear();

  Connection conn(vol()->srv()->connRef());
  DirHandle dirHandle(vol(), relPath(), relPath().ns());
  SEARCH_SEQUENCE sequence;
  sequence.volNumber = 0;
  sequence.dirNumber = 0;
  sequence.searchDirNumber = 0xFFFFFFFF;
  NW_ENTRY_INFO entryInfo;
  for (;;)
  {
    // Получаем информацию об очередном подкаталоге (включая скрытые
    // и системные):
    NWCCODE err = NWScanNSEntryInfo(
      conn,
      dirHandle,
      relPath().ns(),
      A_HIDDEN | A_SYSTEM | A_DIRECTORY,
      &sequence,
      "*",
      IM_ENTRY_NAME,
      &entryInfo);
    if (SUCCESS != err)
    {
      if (NO_FILES_FOUND_ERROR == err)
        break; // нормальная ситуация - подкаталогов больше нет
      else
        throw Exception(err);
    }

    // Добавляем в коллекцию новый элемент:
    pColl->add(new Directory(this, Path(entryInfo.entryName, relPath().ns())));
  }
}

void Directory::getFiles( Files* pColl ) const
  throw(Exception)
{
  pColl->clear();

  Connection conn(vol()->srv()->connRef());
  DirHandle dirHandle(vol(), relPath(), relPath().ns());
  SEARCH_SEQUENCE sequence;
  sequence.volNumber = 0;
  sequence.dirNumber = 0;
  sequence.searchDirNumber = 0xFFFFFFFF;
  NW_ENTRY_INFO entryInfo;
  for (;;)
  {
    // Получаем информацию об очередном файле (включая скрытые
    // и системные):
    NWCCODE err = NWScanNSEntryInfo(
      conn,
      dirHandle,
      relPath().ns(),
      A_HIDDEN | A_SYSTEM | A_NORMAL,
      &sequence,
      "*",
      IM_ENTRY_NAME,
      &entryInfo);
    if (SUCCESS != err)
    {
      if (NO_FILES_FOUND_ERROR == err)
        break; // нормальная ситуация - файлов больше нет
      else
        throw Exception(err);
    }

    // Добавляем в коллекцию новый элемент:
    pColl->add(new File(this, Path(entryInfo.entryName, relPath().ns())));
  }
}

nuint32 Directory::ownerId( void ) const
{
  NWENTRY_INFO info;
  getEntryInfo(FA_HIDDEN | FA_SYSTEM | FA_DIRECTORY, &info);
  return info.ownerID;
}

void Directory::ownerId( nuint32 id )
{
  NWENTRY_INFO info;
  memset(&info, 0, sizeof(info));
  info.ownerID = id;
  setEntryInfo(
    FA_HIDDEN | FA_SYSTEM | FA_DIRECTORY,
    info,
    MOwnerIDBit);
}

} // namespace BGDNet
