// NovellDirListDlg.cpp : implementation file
//

#include "stdafx.h"
#include "SKOKO.h"
#include "NovellDirListDlg.h"

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




namespace
{

enum
{
  NW_DUMMY,
  NW_NETWORK,
  NW_SERVER,
  NW_VOLUME,
  NW_OPEN_DIR,
  NW_CLOSED_DIR,
  NW_NETWORK_DRIVES,
  NW_DRIVE
};

} // namespace




/////////////////////////////////////////////////////////////////////////////
// CNovellDirListDlg dialog


CNovellDirListDlg::CNovellDirListDlg(CWnd* pParent /*=NULL*/)
  : CDialog(CNovellDirListDlg::IDD, pParent)
{
  //{{AFX_DATA_INIT(CNovellDirListDlg)
  m_sTempSelDir = _T("");
  //}}AFX_DATA_INIT
}


void CNovellDirListDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CNovellDirListDlg)
  DDX_Control(pDX, IDC_TREE_DIR, m_DirTree);
  DDX_Text(pDX, IDC_EDIT_DIR, m_sTempSelDir);
  //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CNovellDirListDlg, CDialog)
  //{{AFX_MSG_MAP(CNovellDirListDlg)
  ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_DIR, OnSelchangedTreeDir)
  ON_NOTIFY(TVN_ITEMEXPANDING, IDC_TREE_DIR, OnItemExpandingTreeDir)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CNovellDirListDlg message handlers

BOOL CNovellDirListDlg::OnInitDialog() 
{
  m_sTempSelDir = m_sSelectedDir;
  CDialog::OnInitDialog();
  
  m_DirTree.SetImageList(&theApp.m_imageList, TVSIL_NORMAL);

  // Вставляем сеть NetWare:
  HTREEITEM hNetRoot = m_DirTree.InsertItem(
    _T("Сервера и тома NetWare"),
    CSKOKOApp::ICON_NETWORK,
    CSKOKOApp::ICON_NETWORK);
  m_DirTree.SetItemData(hNetRoot, NW_NETWORK);

  // Вставляем сетевые диски:
  HTREEITEM hDrvRoot = m_DirTree.InsertItem(
    _T("Сетевые диски NetWare"),
    CSKOKOApp::ICON_NETWORK,
    CSKOKOApp::ICON_NETWORK);
  m_DirTree.SetItemData(hDrvRoot, NW_NETWORK_DRIVES);

  CWaitCursor wc;
  
  InsertServers(hNetRoot);
  InsertDrives(hDrvRoot);
  
  for (
    HTREEITEM hRoot = m_DirTree.GetRootItem();
    hRoot;
    hRoot = m_DirTree.GetNextSiblingItem(hRoot))
  {
    HTREEITEM hSel = FindSelDir(hRoot);
    if (hSel != hRoot)
    {
      m_DirTree.SelectItem(hSel);
      break;
    }
  }

  return TRUE;  // return TRUE unless you set the focus to a control
                // EXCEPTION: OCX Property Pages should return FALSE
}

void CNovellDirListDlg::InsertServers( HTREEITEM hNetwork )
{
  try
  {
    BGDNet::Servers srvs;
    int i;

    // Вставляем в дерево сервера:
    for (i = 0; i < srvs.count(); ++i )
    {
      CString sSrvName = (LPCTSTR)srvs[i]->name();
      sSrvName.OemToAnsi();
      HTREEITEM hSrv = m_DirTree.InsertItem(
        sSrvName,
        CSKOKOApp::ICON_SERVER,
        CSKOKOApp::ICON_SERVER,
        hNetwork);
      m_DirTree.SetItemData(hSrv, NW_SERVER);
      try
      {
        InsertVolumes(srvs[i], hSrv);
      }
      catch (const BGDNet::Exception& e)
      {
        if (NO_SEARCH_PRIVILEGES != e.code())
          throw;
        else
          m_DirTree.DeleteItem(hSrv);
      }
    }
    m_DirTree.SortChildren(hNetwork);
  }
  catch (const BGDNet::Exception& e)
  {
    CString err;
    err.Format(_T("Ошибка сети: %s"), e.what());
    AfxMessageBox(err, MB_ICONSTOP);
  }
}

void CNovellDirListDlg::InsertDrives( HTREEITEM hNetwork )
{
  try
  {
    BGDNet::NetworkDrives drvs;
    for (int i = 0; i < drvs.count(); ++i )
    {
      CString sDrive = drvs[i]->drvLetter();
      sDrive.OemToAnsi();
      HTREEITEM hDrv = m_DirTree.InsertItem(
        sDrive,
        CSKOKOApp::ICON_NET_DRIVE,
        CSKOKOApp::ICON_NET_DRIVE,
        hNetwork);
      m_DirTree.SetItemData(hDrv, NW_DRIVE);
      InsertDirs(drvs[i]->mappedDir(), hDrv);
    }
    m_DirTree.SortChildren(hNetwork);
  }
  catch (const BGDNet::Exception& e)
  {
    CString err;
    err.Format(_T("Ошибка сети: %s"), e.what());
    AfxMessageBox(err, MB_ICONSTOP);
  }
}

void CNovellDirListDlg::InsertVolumes( const BGDNet::Server* pSrv, HTREEITEM hSrv )
{
  BGDNet::Volumes vlms;
  pSrv->getVolumes(&vlms);
  for (int i = 0; i < vlms.size(); ++i)
  {
    CString sVolName = (LPCTSTR)vlms[i]->name();
    sVolName.OemToAnsi();
    HTREEITEM hVol = m_DirTree.InsertItem(
      sVolName,
      CSKOKOApp::ICON_VOLUME,
      CSKOKOApp::ICON_VOLUME,
      hSrv);
    m_DirTree.SetItemData(hVol, NW_VOLUME);
    InsertDirs(vlms[i]->root(), hVol);
  }
  m_DirTree.SortChildren(hSrv);
}

void CNovellDirListDlg::InsertDirs( const BGDNet::Directory* pDir, HTREEITEM hParent )
{
  try
  {
    BGDNet::Directories dirs;
    pDir->getSubdirs(&dirs);
    for (int i = 0; i < dirs.size(); ++i)
    {
      CString sDirName = dirs[i]->name();
      sDirName.OemToAnsi();
      HTREEITEM hDir = m_DirTree.InsertItem(
        sDirName,
        CSKOKOApp::ICON_DIR,
        CSKOKOApp::ICON_DIR,
        hParent);
      m_DirTree.SetItemData(hDir, NW_CLOSED_DIR);
      // NW_CLOSED_DIR изменится на NW_OPEN_DIR после раскрытия каталога
      
      // Добавляем псевдо-элемент (чтобы появился "+"):
      HTREEITEM hDummy = m_DirTree.InsertItem(_T(""), -1, -1, hDir);
      m_DirTree.SetItemData(hDummy, NW_DUMMY);
    }
    m_DirTree.SortChildren(hParent);
  }
  catch (const BGDNet::Exception&)
  {
    // Ошибка обработки каталога.
    HTREEITEM hErr = m_DirTree.InsertItem(
      _T("Ошибка чтения каталога"),
      CSKOKOApp::ICON_ERROR,
      CSKOKOApp::ICON_ERROR,
      hParent);
    m_DirTree.SetItemData(hErr, NW_DUMMY);
  }
}

void CNovellDirListDlg::OnSelchangedTreeDir(NMHDR* pNMHDR, LRESULT* pResult) 
{
  NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

  m_sTempSelDir = GetFullPath(pNMTreeView->itemNew.hItem);
  UpdateData(FALSE);
  
  *pResult = 0;
}

HTREEITEM CNovellDirListDlg::FindSelDir( HTREEITEM hFrom )
{
  CString sPath = m_sTempSelDir;
  HTREEITEM hSel = hFrom;
  CString sName;
  HTREEITEM hItem = NULL;
  int pos = sPath.Find(_T(':'));
  if (1 == pos)
  {
    // Путь должен начинаться с буквы сетевого диска:
    hItem = FindChild(sPath.Left(1), hSel);
    if (!hItem)
      return hSel;
    else
      hSel = hItem;

    if (sPath.GetLength() > 2)
      ++pos; // пропускаем '\'
  }
  else
  {
    // Путь должен начинаться с сервера:
    pos = sPath.FindOneOf(_T("\\/"));
    if (pos <= 0)
      pos = sPath.GetLength();
    CString sName = sPath.Left(pos);
    hItem = FindChild(sName, hSel);
    if (!hItem)
      return hSel;
    else
      hSel = hItem;

    // Затем должен идти том:
    sPath.Delete(0, pos + 1);
    if (sPath.IsEmpty())
      return hSel;
    pos = sPath.Find(_T(':'));
    if (pos <= 0)
      return hSel;
    sName = sPath.Left(pos);
    hItem = FindChild(sName, hSel);
    if (!hItem)
      return hSel;
    else
      hSel = hItem;
  }

  // Затем идут каталоги:
  sPath.Delete(0, pos + 1);
  if (sPath.IsEmpty())
    return hSel;
  if (sPath[0] == _T('\\') || sPath[0] == _T('/'))
    sPath.Delete(0, 1);
  for (;;)
  {
    pos = sPath.FindOneOf(_T("\\/"));
    if (pos <= 0)
      pos = sPath.GetLength();
    sName = sPath.Left(pos);
    if (NW_CLOSED_DIR == m_DirTree.GetItemData(hSel))
    {
      // Надо раскрыть нераскрытый каталог:
      ExpandDir(hSel);
      m_DirTree.SetItemData(hSel, NW_OPEN_DIR);
    }
    hItem = FindChild(sName, hSel);
    if (!hItem)
      break;
    hSel = hItem;
    sPath.Delete(0, pos + 1);
    if (sPath.IsEmpty())
      break;
  }

  return hSel;
}

HTREEITEM CNovellDirListDlg::FindChild( LPCTSTR name, HTREEITEM hParent )
{
  HTREEITEM hChild = m_DirTree.GetChildItem(hParent);
  while (hChild)
  {
    CString sItemText = m_DirTree.GetItemText(hChild);
    if (!sItemText.CompareNoCase(name))
      break;
    hChild = m_DirTree.GetNextItem(hChild, TVGN_NEXT);
  }
  return hChild;
}

void CNovellDirListDlg::OnOK() 
{
  // Удаляем "мусор" в конце:
  TCHAR mess[] = "\\/";
  int pos = m_sTempSelDir.GetLength() - 1;
  while (pos >= 0 && _tcschr(mess, m_sTempSelDir[pos]))
    --pos;
  m_sTempSelDir.Delete(pos + 1, m_sTempSelDir.GetLength());
  m_sSelectedDir = m_sTempSelDir;
  CDialog::OnOK();
}

void CNovellDirListDlg::OnItemExpandingTreeDir(NMHDR* pNMHDR, LRESULT* pResult) 
{
  NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  
  if (pNMTreeView->action & (TVE_EXPAND | TVE_EXPANDPARTIAL))
  {
    if (NW_CLOSED_DIR == pNMTreeView->itemNew.lParam)
    {
      // Раскрывают каталог.
      ExpandDir(pNMTreeView->itemNew.hItem);
      m_DirTree.SetItemData(pNMTreeView->itemNew.hItem, NW_OPEN_DIR);
    }
  }
  
  *pResult = 0;
}

CString CNovellDirListDlg::GetFullPath( HTREEITEM hItem )
{
  CString path;
  do
  {
    CString sItemText = m_DirTree.GetItemText(hItem);
    switch (m_DirTree.GetItemData(hItem))
    {
      case NW_SERVER:
      case NW_OPEN_DIR:
      case NW_CLOSED_DIR:
        // Текущим является сервер или каталог:
        if (!path.IsEmpty())
          path.Insert(0, _T('\\'));
        path.Insert(0, sItemText);
        break;

      case NW_VOLUME:
        // Текущим является том:
        if (path.IsEmpty())
          path.Insert(0, _T(':'));
        else
          path.Insert(0, _T(":\\"));
        path.Insert(0, sItemText);
        break;

      case NW_DRIVE:
        // Текущим является сетевой диск:
        if (path.IsEmpty())
          path.Insert(0, _T(':'));
        else
          path.Insert(0, _T(":\\"));
        path.Insert(0, sItemText[0]);
        break;
    }
    hItem = m_DirTree.GetParentItem(hItem);
  }
  while (hItem);
  return path;
}

void CNovellDirListDlg::ExpandDir( HTREEITEM hDir )
{
  CWaitCursor wc;
  HTREEITEM hChild = m_DirTree.GetChildItem(hDir);
  while (hChild)
  {
    m_DirTree.DeleteItem(hChild);
    hChild = m_DirTree.GetChildItem(hDir);
  }
  CString parentName = GetFullPath(hDir);
  parentName.AnsiToOem();

  int iColonPos = parentName.Find(_T(':'));
  BGDNet::Path path;
  if( iColonPos > 1 )
  {
    // Путь включает сервер и том
    path = BGDNet::Path((LPCTSTR)parentName, NW_NS_LONG);
  }
  else
  {
    // Путь включает сетевой диск
    BGDNet::NetworkDrive drv(parentName[0]);
    if (drv.mappedDir())
    {
      path = drv.mappedDir()->fullPath();
      path += '\\';
      path += parentName.Mid(3);
    }
  }

  BGDNet::Server srv(path.srv());
  BGDNet::Volume vol(&srv, path.vol());
  const nuint8 nameSpc = vol.supportsLongNames() ? NW_NS_LONG : NW_NS_DOS;
  BGDNet::Directory dir(&vol, BGDNet::Path(path.path(), nameSpc));
  InsertDirs(&dir, hDir);
}

CString CNovellDirListDlg::SelectedDir() const
{
  CString s = m_sSelectedDir;
  int iColonPos = s.Find(_T(':'));
  if (1 == iColonPos)
  {
    // Необходимо преобразовать путь с буквой сетевого диска
    // в путь с сервером и каталогом.

    // Узнаем путь к каталогу NetWare:
    BGDNet::NetworkDrive drv(s[0]);
    CString mappedDir = (LPCTSTR)drv.mappedDir()->fullPath();
    mappedDir.OemToAnsi();
    
    // Удаляем букву диска и двоеточие:
    s.Delete(0, 2);

    // Вместо удаленных элементов вставляем путь:
    s.Insert(0, mappedDir);
  }
  return s;
}
