/**
 * \file BGDNetNDSObject.cpp
 * \brief Реализация классов объектов NDS
 */

#include "BGDNetPch.h"

#include <nwdsdsa.h>

#include "BGDNetNDSObject.h"
#include "BGDNetBuffer.h"
#include "BGDNetContext.h"
#include "BGDNetNDS.h"

namespace BGDNet
{

NDSObject::NDSObject( const Context& ctx, const char* name )
  throw(Exception) :
    m_ctx(ctx)
{
  // Создаем временный контекст, эквивалентный заданному для
  // объекта за исключением того, что функция NWDSReadObjectDSIInfo
  // будет возврать полное и относительное имена объектов:
  Context tempCtx = ctx;
  tempCtx.dsiFlags(DSI_ENTRY_DN | DSI_ENTRY_RDN);
  
  char infoBuf[2*(MAX_DN_CHARS + 1)];
  char buf[MAX_DN_CHARS + 1];

  // Получаем DSI-информацию об объекте, которая должна включать
  // его полное и относительное имена:
  NWDSCCODE err = NWDSReadObjectDSIInfo(
    tempCtx,
    const_cast<char*>(name),
    sizeof(infoBuf),
    infoBuf);
  if (SUCCESS != err)
    throw Exception(err);
  
  // Получаем полное имя:
  err = NWDSGetDSIInfo(
    tempCtx,
    infoBuf,
    sizeof(infoBuf),
    DSI_ENTRY_DN,
    buf);
  if (SUCCESS != err)
    throw Exception(err);

  // Полное имя начинается с точки!
  m_dn = ".";
  m_dn += buf;

  // Получаем относительное имя:
  err = NWDSGetDSIInfo(
    tempCtx,
    infoBuf,
    sizeof(infoBuf),
    DSI_ENTRY_RDN,
    buf);
  if (SUCCESS != err)
    throw Exception(err);
  
  m_rdn = buf;
}

const String& NDSObject::dn( void ) const
{
  return m_dn;
}

const String& NDSObject::rdn( void ) const
{
  return m_rdn;
}

String NDSObject::baseClass( void ) const
{
  Object_Info_T objectInfo;
  char buf[MAX_DN_CHARS + 1];
  NWDSCCODE err = NWDSReadObjectInfo(
    m_ctx,
    const_cast<char*>(dn().ptr()),
    buf,
    &objectInfo);
  if (SUCCESS != err)
    throw Exception(err);
  return String(objectInfo.baseClass);
}

bool NDSObject::isContainerEntry( void ) const
{
  return 0 != (entryFlags() & DS_CONTAINER_ENTRY);
}

bool NDSObject::isContainerAlias( void ) const
{
  return 0 != (entryFlags() & DS_CONTAINER_ALIAS);
}

bool NDSObject::isContainer( void ) const
{
  return 0 != (entryFlags() & (DS_CONTAINER_ENTRY | DS_CONTAINER_ALIAS));
}

void NDSObject::getSubordinates( NDSObjects* pColl ) const
  throw(Exception)
{
  pColl->clear();

  // Создаем временный контекст, эквивалентный заданному для
  // объекта за исключением того, что функция NWDSReadObjectDSIInfo
  // будет возврать только полные имена объектов:
  Context tempCtx = m_ctx;
  tempCtx.dsiFlags(DSI_ENTRY_DN);
  
  char buf[MAX_DN_CHARS + 1];
  buf[0] = '.';
  // Буфер предназначен для получения полных имен. Функции NDS
  // возвращают их без точки в начале, поэтому мы заранее помещаем
  // эту точку в начало буфера, а имена будем "укладывать" начиная
  // со следующей позиции.
  
  OutputBuffer outBuf(tempCtx);
  nint32 iterHandle = NO_MORE_ITERATIONS;
  try
  {
    do
    {
      // 1) Получаем очередную порцию подчиненных объектов:
      NWDSCCODE err = NWDSList(
        tempCtx,
        const_cast<char*>(dn().ptr()),
        &iterHandle,
        outBuf.buf());
      if( SUCCESS != err )
        throw Exception(err);
      
      // 2) Получаем количество имен подчиненных объектов в буфере:
      int n = outBuf.objCount();
      
      // 3) Получаем все полные имена в буфере:
      while (n--)
      {
        char* pInfo;
        outBuf.objNameAndDSIInfo(&pInfo);
        err = NWDSGetDSIInfo(
          tempCtx,
          pInfo,
          MAX_DN_CHARS + 1, // ???
          DSI_ENTRY_DN,
          buf + 1); // именно начиная с buf + 1, см. комментарий к buf выше!
        if (SUCCESS != err)
          throw Exception(err);
  
        pColl->add(new NDSObject(m_ctx, buf));
      }
    }
    while( NO_MORE_ITERATIONS != iterHandle );
  }
  catch (...)
  {
    // Если возникло исключение, необходимо завершить итерацию:
    NWDSCloseIteration(m_ctx, iterHandle, DSV_LIST);
    throw;
  }
}

nuint32 NDSObject::entryFlags( void ) const
{
  // Создаем временный контекст, эквивалентный заданному для
  // объекта за исключением того, что функция NWDSReadObjectDSIInfo
  // будет возврать только флаги объектов:
  Context tempCtx = m_ctx;
  tempCtx.dsiFlags(DSI_ENTRY_FLAGS);
  
  // Получаем DSI-информацию об объекте, которая должна включать
  // его флаги:
  char infoBuf[1024];
  NWDSCCODE err = NWDSReadObjectDSIInfo(
    tempCtx,
    const_cast<char*>(dn().ptr()),
    sizeof(infoBuf),
    infoBuf);
  if (SUCCESS != err)
    throw Exception(err);

  // Получаем флаги объекта:
  nuint32 flags = 0;
  err = NWDSGetDSIInfo(
    tempCtx,
    infoBuf,
    sizeof(infoBuf),
    DSI_ENTRY_FLAGS,
    &flags);
  if (SUCCESS != err)
    throw Exception(err);

  return flags;
}

bool NDSObject::attr( const char* name, String* pVal, int valNum /*=0*/) const
{
  Strings vals;
  if (!attr(name, &vals))
	{
    return false;
	}
  else
  {
    if (valNum < 0 ||
				vals.size() == 0 || 
				vals.size() <= valNum)
		{
      *pVal = String(); // нет значений - значит пустая строка!
    }
		else
		{
      *pVal = vals[valNum];
		}

    return true;
  }
}

bool NDSObject::attr( const char* name, Strings* pValues ) const
{
  // 1) Инициализируем входной и выходной буфера:
  InputBuffer inBuf(m_ctx, DSV_READ);
  OutputBuffer outBuf(m_ctx);
  inBuf.putAttrName(name);
  
  nint32 iterHandle = NO_MORE_ITERATIONS;
  try
  {
    // 2) Читаем информацию об объекте:
    NWDSCCODE err = NWDSRead(
      m_ctx,
      const_cast<char*>(dn().ptr()),
      DS_ATTRIBUTE_VALUES,
      N_FALSE,
      inBuf.buf(),
      &iterHandle,
      outBuf.buf());
    if (ERR_NO_SUCH_ATTRIBUTE == err)
      return false; // нет такого атрибута!
    if( SUCCESS != err )
      throw Exception(err);
    assert( NO_MORE_ITERATIONS == iterHandle );
    
    pValues->clear();
    if (outBuf.attrCount() > 0)
    {
      // Получаем атрибуты из выходного буфера:
      NDSAttrValues vals;
      outBuf.getAttr(&vals);
      for (int i = 0; i < vals.size(); ++i)
        pValues->add(String(&vals[i].front()));
    }

    return true;
  }
  catch (...)
  {
    // Если возникло исключение, необходимо завершить итерацию:
    NWDSCloseIteration(m_ctx, iterHandle, DSV_READ);
    throw;
  }
}

} // namespace BGDNet
