#include "StdAfx.h"
#include "SKOKO.h"
#include "TraversalProgressDlg.h"
#include "Traversal.h"

//
// CFileSystemTraversal
//

CFileSystemTraversal::CFileSystemTraversal( void ) :
  m_errCount(0),
  m_fileCount(0),
  m_dirCount(0),
  m_hThread(NULL),
  m_stopFlag(0),
  m_isRecursive(false)
{
  InitializeCriticalSection(&m_cs);
}

CFileSystemTraversal::~CFileSystemTraversal( void )
{
  stop();
  DeleteCriticalSection(&m_cs);
}

void CFileSystemTraversal::run(
  const char* startPath,
  bool recursive,
  CWnd* pParentWnd )
{
  ASSERT(m_hThread == NULL);
  m_startPath = startPath;
  m_startPath.AnsiToOem();
  m_curDir.Empty();
  m_errCount = 0;
  m_fileCount = 0;
  m_dirCount = 0;
  m_stopFlag = 0;
  m_isRecursive = recursive;

  CTraversalProgressDlg progressDlg(this, pParentWnd);
  m_pProgressDlg = &progressDlg;

  DWORD id;
  m_hThread = CreateThread(NULL, 0, traverseThread, this, 0, &id);
  
  progressDlg.DoModal();

  m_pProgressDlg = 0;

  if (m_hThread)
  {
    CloseHandle(m_hThread);
    m_hThread = NULL;
  }
}

void CFileSystemTraversal::stop( void )
{
  if (m_hThread)
  {
    InterlockedExchange(&m_stopFlag, 1);
    WaitForSingleObject(m_hThread, INFINITE);
    CloseHandle(m_hThread);
    m_hThread = NULL;
  }
}

void CFileSystemTraversal::onFinished( void )
{
  m_pProgressDlg->PostMessage(
		WM_COMMAND,
		MAKEWPARAM(IDOK, IDOK),
		NULL);
}

void CFileSystemTraversal::onServer( BGDNet::Server* pSrv )
{
}

void CFileSystemTraversal::onVolume( BGDNet::Volume* pVol )
{
}

void CFileSystemTraversal::onDirectory( BGDNet::Directory* pDir )
{
  InterlockedIncrement(&m_dirCount);

  EnterCriticalSection(&m_cs);
  m_curDir = pDir->fullPath();
  m_curDir.OemToAnsi();
  LeaveCriticalSection(&m_cs);
}

void CFileSystemTraversal::onFile( BGDNet::File* pFile )
{
  InterlockedIncrement(&m_fileCount);
}

void CFileSystemTraversal::onError( const CString& obj, const CString& e )
{
  InterlockedIncrement(&m_errCount);
}

DWORD WINAPI CFileSystemTraversal::traverseThread( LPVOID pParam )
{
  try
  {
    ((CFileSystemTraversal*)pParam)->traverse();
  }
  catch (const BGDNet::Exception& e)
  {
    CString err;
    err.Format(_T("Ошибка сети: %s"), e.what());
    AfxMessageBox(err, MB_ICONSTOP);
  }
  catch (CException* pEx)
  {
    pEx->ReportError();
    pEx->Delete();
  }
  catch (...)
  {
    AfxMessageBox(_T("Неизвестная ошибка"), MB_ICONSTOP);
  }
  ((CFileSystemTraversal*)pParam)->onFinished();
  return 0;
}

void CFileSystemTraversal::traverse( void )
{
	if (m_startPath.IsEmpty())
	{
		// Стартовый каталог пуст, собираем файлы со всей сети:
		traverseNetwork();
	}
	else
	{
		// Стартовый каталог не пуст.

		// Ищем позицию двреточия и последнего слэша:
		int colon = m_startPath.Find(_T(':'));
		if (colon < 0)
		{
			// Двоеточия нет, значит выбран сервер.
      BGDNet::Server srv((LPCTSTR)m_startPath);
			traverseServer(&srv);
		}
		else
		{
			// Есть двоеточие, значит выбран том либо каталог.
      BGDNet::Path path((LPCTSTR)m_startPath, NW_NS_LONG);
      BGDNet::Server srv(path.srv());
      BGDNet::Volume vol(&srv, path.vol());
      const nuint8 ns = vol.supportsLongNames() ? NW_NS_LONG : NW_NS_DOS;
      BGDNet::Directory dir(&vol, BGDNet::Path(path.path(), ns));
			traverseDir(&dir);
		}
	}
}

void CFileSystemTraversal::traverseNetwork( void )
{
  try
  {
    BGDNet::Servers srvs;
    for (int i = 0; i < srvs.count(); ++i)
    {
      if (m_stopFlag)
        return;
      BGDNet::Server* pSrv = srvs[i];
      onServer(pSrv);
      traverseServer(pSrv);
    }
  }
  catch( const BGDNet::Exception& e)
  {
    onError(_T("Сеть NetWare"), e.what());
  }
}

void CFileSystemTraversal::traverseServer( const BGDNet::Server* pSrv )
{
  try
  {
    BGDNet::Volumes vlms;
    pSrv->getVolumes(&vlms);
    for (int i = 0; i < vlms.size(); ++i)
    {
      if (m_stopFlag)
        return;
      BGDNet::Volume* pVol = vlms[i];
      onVolume(pVol);
      traverseDir(pVol->root());
    }
  }
  catch (const BGDNet::Exception& e)
  {
    CString errObj = pSrv->name();
    errObj += _T('\\');
    errObj.OemToAnsi();
    onError(errObj, e.what());
  }
}

void CFileSystemTraversal::traverseDir( BGDNet::Directory* pDir )
{
  try
  {
    onDirectory(pDir);

    int i;

    if (m_isRecursive)
    {
      BGDNet::Directories dirs;
      pDir->getSubdirs(&dirs);
      for (i = 0; i < dirs.size(); ++i)
      {
        if (m_stopFlag)
          return;
        traverseDir(dirs[i]);
      }
    }

    BGDNet::Files fls;
    pDir->getFiles(&fls);
    for (i = 0; i < fls.size(); ++i)
    {
      if (m_stopFlag)
        return;
      onFile(fls[i]);
    }
  }
  catch (const BGDNet::Exception& e)
  {
    CString dirPath = (LPCTSTR)pDir->fullPath();
    dirPath.OemToAnsi();
    onError(dirPath, e.what());
  }
}

CString CFileSystemTraversal::curDir( void ) const
{
  EnterCriticalSection(&m_cs);
  CString ret = (LPCTSTR)m_curDir;
  LeaveCriticalSection(&m_cs);
  return ret;
}

LONG CFileSystemTraversal::errCount( void ) const
{
  return m_errCount;
}

LONG CFileSystemTraversal::fileCount( void ) const
{
  return m_fileCount;
}

LONG CFileSystemTraversal::dirCount( void ) const
{
  return m_dirCount;
}

//
// CFileListingFormation
//

CFileListingFormation::CFileListingFormation( CFileListing* pList ) :
  m_pList(pList)
{
}

void CFileListingFormation::onFile( BGDNet::File* pFile )
{
  CFileSystemTraversal::onFile(pFile);

  CFileInfo info;
  info.filePath = pFile->fullPath();
  info.filePath.OemToAnsi();
  info.fileSize = pFile->size();

  try
  {
    BGDNet::NDSObject user(m_ctx, pFile->ownerName(m_ctx));
    info.ownerName = user.dn();
    info.ownerName.OemToAnsi();
  }
  catch (const BGDNet::Exception&)
  {
    info.ownerName = UNKNOWN_OWNER;
  }

  m_pList->addFile(info);
}

void CFileListingFormation::onError( const CString& obj, const CString& e )
{
  CFileSystemTraversal::onError(obj, e);

  CFileInfo info;
  info.filePath = obj;
  info.ownerName = e;
  m_pList->addError(info);
}

//
// CGlobalFileAttrUpdate
//

CGlobalFileAttrUpdate::CGlobalFileAttrUpdate(
  CFileListing* pList,
  CFileAttrUpdate* pAttrUpdate ) :
    CFileListingFormation(pList),
    m_pAttrUpdate(pAttrUpdate)
{
}

void CGlobalFileAttrUpdate::onFile( BGDNet::File* pFile )
{
  try
  {
    if (m_pAttrUpdate->updateAttributes(pFile))
      CFileListingFormation::onFile(pFile);
  }
  catch (const BGDNet::Exception&)
  {
    CString filePath = pFile->fullPath();
    filePath.OemToAnsi();
    CString fileOwner;
    try
    {
      BGDNet::NDSObject user(m_ctx, pFile->ownerName(m_ctx));
      fileOwner = user.dn();
      fileOwner.OemToAnsi();
    }
    catch (const BGDNet::Exception&)
    {
      fileOwner = UNKNOWN_OWNER;
    }
    CFileListingFormation::onError(filePath, fileOwner);
  }
}
