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

#include "BGDNetPch.h"

#include "BGDNetDirEntry.h"

#include "BGDNetVolume.h"
#include "BGDNetConnection.h"
#include "BGDNetServer.h"

namespace BGDNet
{

const NWDIR_HANDLE DirHandle::s_invalidHandle = 0;

DirHandle::DirHandle( const Volume* pVol, const Path& path, nuint8 nameSpc )
  throw(Exception) :
  m_connRef(pVol->srv()->connRef())
{
  // Формируем путь к каталогу, начиная с имени тома:
  String s = pVol->name();
  s += ':';
  s += path;
  
  // Получаем дескриптор подкаталога в указанном пространстве имен:
  NWCCODE err = NWAllocTempNSDirHandle2(
    Connection(m_connRef),
    0,
    s,
    path.ns(),
    &m_handle,
    nameSpc);
  if (SUCCESS != err)
  {
    m_handle = s_invalidHandle;
    throw Exception(err);
  }
}

DirHandle::~DirHandle( void )
{
  if (s_invalidHandle != m_handle)
    NWDeallocateDirectoryHandle(Connection(m_connRef), m_handle);
}

DirHandle::operator NWDIR_HANDLE( void ) const
{
  return m_handle;
}




DirEntry::DirEntry( const Volume* pVol, const Path& relPath ) :
  m_pVol(pVol),
  m_relPath(relPath)
{
  /**
   * \todo Нужно проверить, есть ли такой элемент!
   */
}

const Volume* DirEntry::vol( void ) const
{
  return m_pVol;
}

const Path& DirEntry::relPath( void ) const
{
  return m_relPath;
}

Path& DirEntry::relPath( void )
{
  return m_relPath;
}

Path DirEntry::fullPath( void ) const
{
  // Полный путь начинается с имени сервера,...
  String s = vol()->srv()->name();
  
  // ...за которым следует имя тома,...
  s += '\\';
  s += vol()->name();
  
  // и заканчивается относительным путем к каталогу.
  s += ':';
  s += relPath();
  
  return Path(s, relPath().ns());
}

String DirEntry::ownerName( const Context& ctx ) const
{
  return vol()->srv()->objNameById(ownerId(), ctx);
}

void DirEntry::ownerName( const Context& ctx, const char* name )
{
  ownerId(vol()->srv()->objIdByName(name, ctx));
}

void DirEntry::getEntryInfo( nuint16 attrs, NWENTRY_INFO* pInfo ) const
{
  // Получение информации осуществляется при помощи функции файловой
  // системы NWIntScanDirEntryInfo, которая работает в пространстве
  // имен DOS. Поэтому ей необходимо передать дескриптор родительского
  // каталога и имя элемента, приведенные к этому пространству имен.
  
  // Получаем дескриптор родительского каталога и имя элемента
  // в пространстве имен DOS:
  String parentDir = m_relPath.dir();
  String entryName = dosName();
  const char* nm = entryName;
  Connection conn(m_pVol->srv()->connRef());
  DirHandle dirHandle(
    m_pVol,
    Path(parentDir, m_relPath.ns()),
    NW_NS_DOS);
  
  // Получаем информацию об элементе каталога:
  nuint32 i = -1;
  NWRCODE err = NWIntScanDirEntryInfo(
    conn,
    dirHandle,
    attrs,
    &i,
    (pnuint8)nm,
    pInfo,
    0);
  if (SUCCESS != err)
    throw Exception(err);
}

void DirEntry::setEntryInfo(
  nuint16 attrs,
  const NWENTRY_INFO& info,
  nuint32 changeBits )
{
  NWENTRY_INFO entryInfo = info;
  entryInfo.nameSpace = NW_NS_DOS;
  
  // Получаем дескриптор родительского каталога и имя элемента
  // в пространстве имен DOS:
  String parentDir = m_relPath.dir();
  String entryName = dosName();
  entryInfo.nameLength = entryName.length();
  if (entryInfo.nameLength > sizeof(entryInfo.name))
    entryInfo.nameLength = sizeof(entryInfo.name);
  memcpy(entryInfo.name, entryName.ptr(), entryInfo.nameLength);
  Connection conn(m_pVol->srv()->connRef());
  DirHandle dirHandle(
    m_pVol,
    Path(parentDir, m_relPath.ns()),
    NW_NS_DOS);
  
  // Изменяем информацию об элементе каталога:
  NWRCODE err = NWSetDirEntryInfo(
    conn,
    dirHandle,
    attrs,
    0,
    changeBits,
    &entryInfo);
  if (SUCCESS != err)
    throw Exception(err);
}

String DirEntry::dosName( void ) const
{
  return (NW_NS_DOS == m_relPath.ns()) ? m_relPath.name() : name(NW_NS_DOS);
}

String DirEntry::name( void ) const
{
  if (NW_NS_LONG == m_relPath.ns())
    return m_relPath.name();
  else
    return vol()->supportsLongNames() ? name(NW_NS_LONG) : m_relPath.name();
}

String DirEntry::name( nuint8 nameSpc ) const
{
  assert(nameSpc != m_relPath.ns());
  // Не имеет смысла вызывать этот метод в случае, если относительный
  // путь уже задан в необходимом пространстве имен.

  // Получаем имя элемента каталога в заданном пространстве имен:
  String entryName = m_relPath.name();
  Connection conn(m_pVol->srv()->connRef());
  String parentDir = m_relPath.dir();
  DirHandle dirHandle(
    m_pVol,
    Path(parentDir, m_relPath.ns()),
    m_relPath.ns());
  NW_ENTRY_INFO info;
  NWRCODE err = NWGetNSEntryInfo(
    conn,
    dirHandle,
    entryName,
    m_relPath.ns(),
    nameSpc,
    SA_ALL,
    IM_ENTRY_NAME,
    &info);
  if (SUCCESS != err)
    throw Exception(err);
  
  return String(info.entryName);
}

} // namespace BGDNet
