// SKOKOView.cpp : implementation of the CSKOKOView class
//

#include "stdafx.h"
#include "SKOKO.h"
#include "MainFrm.h"
#include "SKOKODoc.h"
#include "ProgressDlg.h"
#include "FileAttrUpdate.h"
#include "SKOKOView.h"

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

/////////////////////////////////////////////////////////////////////////////
// CSKOKOView

IMPLEMENT_DYNCREATE(CSKOKOView, CListView)

BEGIN_MESSAGE_MAP(CSKOKOView, CListView)
  //{{AFX_MSG_MAP(CSKOKOView)
  ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
  ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispInfo)
	ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
	ON_WM_KEYUP()
	ON_WM_CONTEXTMENU()
	ON_UPDATE_COMMAND_UI(ID_OPERATION_CALCULATE_SIZE, OnUpdateOperationCalculateSize)
	ON_COMMAND(ID_OPERATION_CALCULATE_SIZE, OnOperationCalculateSize)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSKOKOView construction/destruction

CSKOKOView::CSKOKOView() :
  m_firstUpdate(TRUE),
  m_sortColumn(0),
  m_totalSize(0)
{
}

CSKOKOView::~CSKOKOView()
{
}

BOOL CSKOKOView::PreCreateWindow(CREATESTRUCT& cs)
{
  // TODO: Modify the Window class or styles here by modifying
  //  the CREATESTRUCT cs

  return CListView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CSKOKOView drawing

void CSKOKOView::OnDraw(CDC* pDC)
{
  CSKOKODoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  // TODO: add draw code for native data here
}

void CSKOKOView::OnInitialUpdate()
{
  if (m_firstUpdate)
  {
    Init();
    m_firstUpdate = FALSE;
  }

  CListView::OnInitialUpdate();
}

/////////////////////////////////////////////////////////////////////////////
// CSKOKOView diagnostics

#ifdef _DEBUG
void CSKOKOView::AssertValid() const
{
  CListView::AssertValid();
}

void CSKOKOView::Dump(CDumpContext& dc) const
{
  CListView::Dump(dc);
}

CSKOKODoc* CSKOKOView::GetDocument() // non-debug version is inline
{
  ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSKOKODoc)));
  return (CSKOKODoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSKOKOView message handlers

void CSKOKOView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
  switch (lHint)
  {
    case 10: // Очистка списка
      CleanListCtrl();
      break;
    case 11: // Обновление списка
      RefreshListCtrl();
      break;
    default:
      Invalidate();
  }

  UpdateCountIndicator();
  UpdateSizeIndicator();
}

void CSKOKOView::Init( void )
{
  CListCtrl& ctrl = GetListCtrl();
  const DWORD dwAddLvStyle = LVS_ALIGNLEFT | LVS_REPORT | LVS_SHOWSELALWAYS;
  const DWORD dwRemoveLvStyle = LVS_SORTASCENDING | LVS_SORTDESCENDING;
  ctrl.ModifyStyle(dwRemoveLvStyle, dwAddLvStyle, 0);
  ctrl.InsertColumn(0, _T("Файл"), LVCFMT_LEFT, 300, 0);
  ctrl.InsertColumn(1, _T("Владелец"), LVCFMT_LEFT, 120, 0);
  ctrl.InsertColumn(2, _T("Размер"), LVCFMT_LEFT, 80, 0);
  ctrl.SetImageList(&theApp.m_imageList, LVSIL_SMALL);
  m_sortOrder.resize(ctrl.GetHeaderCtrl()->GetItemCount(), 0);
  ctrl.SortItems(CompareFunc, (DWORD)this);
}

void CSKOKOView::OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  CWaitCursor wc;
  CListCtrl& ctrl = GetListCtrl();
  m_sortColumn = pNMListView->iSubItem;
  ASSERT(m_sortColumn < m_sortOrder.size());

  // Если порядок сортировки уже определен - меняем его
  // на противоположный, иначе принимаем "по возрастанию".
  int sortOrder = (m_sortOrder[m_sortColumn]) ?
    -m_sortOrder[m_sortColumn] : 1;

  // Делаем "неопределенным" порядок сортировки всех колонок
  // кроме выбранной:
  for (int i = 0; i < m_sortOrder.size(); ++i)
    m_sortOrder[i] = 0;
  m_sortOrder[m_sortColumn] = sortOrder;

  VERIFY(ctrl.SortItems(CompareFunc, (DWORD)this));
  
  *pResult = 0;
}

int CALLBACK CSKOKOView::CompareFunc(
  LPARAM lParam1,
  LPARAM lParam2, 
  LPARAM lParamSort)
{
  CSKOKOView& self = *((CSKOKOView*)lParamSort);
  const CSKOKODoc* pDoc = self.GetDocument();
  int col = self.m_sortColumn;

  CFileInfo fi1, fi2;
  pDoc->GetFileInfo(lParam1, &fi1);
  pDoc->GetFileInfo(lParam2, &fi2);

  int cmp;
  switch (col)
  {
    case 0: // имя файла
      cmp = fi1.filePath.CompareNoCase(fi2.filePath);
      break;
    case 1: // владелец
      cmp = fi1.ownerName.CompareNoCase(fi2.ownerName);
      break;
    case 2: // размер
      cmp = fi1.fileSize < fi2.fileSize ?
        -1 : fi1.fileSize > fi2.fileSize ? 1 : 0;
      break;
    default:
      cmp = 0;
      break;
  }
  return cmp*self.m_sortOrder[col];
}

void CSKOKOView::UpdateCountIndicator( void )
{
  theApp.FrameWnd()->SetCountIndicator(
    GetListCtrl().GetSelectedCount(),
    GetListCtrl().GetItemCount());
}

void CSKOKOView::UpdateSizeIndicator( void )
{
  theApp.FrameWnd()->SetSizeIndicator(0, m_totalSize);
}

void CSKOKOView::OnGetdispInfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
  LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
  
  if (pDispInfo->item.mask & LVIF_TEXT)
  {
    const CSKOKODoc* pDoc = GetDocument();
    static CFileInfo fi;
    pDoc->GetFileInfo(pDispInfo->item.lParam, &fi);
    static CString sText;
    switch (pDispInfo->item.iSubItem)
    {
      case 0: // путь
        sText = fi.filePath;
        break;
      case 1: // владелец
        sText = fi.ownerName;
        break;
      case 2: // размер
        sText.Format(_T("%u"), fi.fileSize);
        break;
      default:
        sText.Empty();
        break;
    }
    pDispInfo->item.pszText = (LPTSTR)(LPCTSTR)sText;
  }
  
  *pResult = 0;
}

void CSKOKOView::RefreshListCtrl( void )
{
  CListCtrl& ctrl = GetListCtrl();
  const CSKOKODoc& doc = *GetDocument();

  CProgressDlg progress(
    _T("Формируется список файлов"), 
    doc.GetFileCount(),
    this);

  ctrl.DeleteAllItems();
  ctrl.SetItemCount(doc.GetFileCount());
  m_totalSize = 0;
  for (int i = 0; i < doc.GetFileCount(); ++i)
  {
    if (i%10 == 0)
      progress.SetProgress(i);

    CFileInfo fi;
    doc.GetFileInfo(i, &fi);
    // Помещаем в список только файлы с заданным владельцем
    // либо все файлы, если владелец не задан:
    if (doc.Owner().IsEmpty() || fi.ownerName == doc.Owner())
    {
      int item = ctrl.InsertItem(i, LPSTR_TEXTCALLBACK, CSKOKOApp::ICON_FILE);
      ctrl.SetItemText(item, 1, LPSTR_TEXTCALLBACK);
      ctrl.SetItemText(item, 2, LPSTR_TEXTCALLBACK);
      
      // LPARAM каждого элемента равен его номеру в журнале!
      ctrl.SetItemData(item, i);

      m_totalSize += fi.fileSize;
    }
  }

  progress.DestroyWindow();
}

void CSKOKOView::CleanListCtrl( void )
{
  GetListCtrl().DeleteAllItems();
  m_totalSize = 0;
}

void CSKOKOView::OnClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
  UpdateCountIndicator();
  *pResult = 0;
}

void CSKOKOView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
  CListView::OnKeyUp(nChar, nRepCnt, nFlags);
  UpdateCountIndicator();
}

void CSKOKOView::UpdateAllFiles(CFileAttrUpdate *pUpdate)
{
  CListCtrl& ctrl = GetListCtrl();
  CSKOKODoc& doc = *GetDocument();

  CProgressDlg progress(_T("Передача файлов"), ctrl.GetItemCount(), this);

  for (int i = 0; i < ctrl.GetItemCount(); ++i)
  {
    progress.SetProgress(i);
    int nFile = ctrl.GetItemData(i);
    CFileInfo fi;
    doc.GetFileInfo(nFile, &fi);
    if (UpdateFile(&fi, pUpdate))
      doc.UpdateFileInfo(nFile, fi);
  }

  doc.RefreshErrView();
  progress.DestroyWindow();
  Invalidate();
}

void CSKOKOView::UpdateSelectedFiles(CFileAttrUpdate *pUpdate)
{
  CListCtrl& ctrl = GetListCtrl();
  CSKOKODoc& doc = *GetDocument();

  CProgressDlg progress(_T("Передача файлов"), ctrl.GetSelectedCount(), this);

  int i = 0;
  POSITION pos = ctrl.GetFirstSelectedItemPosition();
  while (pos)
  {
    if (i%10 == 0)
      progress.SetProgress(i);
    ++i;
    
    int nItem = ctrl.GetNextSelectedItem(pos);
    int nFile = ctrl.GetItemData(nItem);
    CFileInfo fi;
    doc.GetFileInfo(nFile, &fi);
    if (UpdateFile(&fi, pUpdate))
      doc.UpdateFileInfo(nFile, fi);
  }

  doc.RefreshErrView();
  progress.DestroyWindow();
  Invalidate();
}

bool CSKOKOView::UpdateFile(CFileInfo* pFileInfo, CFileAttrUpdate *pUpdate)
{
  CString s = pFileInfo->filePath;
  s.AnsiToOem();
  try
  {
    BGDNet::Path curPath((LPCTSTR)s, NW_NS_LONG);
    BGDNet::Server srv(curPath.srv());
    BGDNet::Volume vol(&srv, curPath.vol());
    const nuint8 nameSpc = vol.supportsLongNames() ? NW_NS_LONG : NW_NS_DOS;
    BGDNet::File file(&vol, BGDNet::Path(curPath.path(), nameSpc));
    if (!pUpdate->updateAttributes(&file))
      return false;
    BGDNet::NDSObject owner(m_ctx, file.ownerName(m_ctx));
    pFileInfo->ownerName = owner.dn();
    pFileInfo->ownerName.OemToAnsi();
    return true;
  }
  catch (const BGDNet::Exception& e)
  {
    CFileInfo err = *pFileInfo;
    err.ownerName = e.what();
    GetDocument()->AddErrorInfo(err);
    return false;
  }
}

UINT CSKOKOView::GetSelectedFileCount()
{
  return GetListCtrl().GetSelectedCount();
}

void CSKOKOView::OnContextMenu(CWnd* pWnd, CPoint point) 
{
  CMenu popup;
  popup.LoadMenu(IDR_MENU_POPUP);
  CMenu* pSubMenu = popup.GetSubMenu(0);
  UINT nEnable = (GetListCtrl().GetSelectedCount() > 0) ? MF_ENABLED : MF_GRAYED;
  pSubMenu->EnableMenuItem(
    ID_OPERATION_CALCULATE_SIZE,
    MF_BYCOMMAND | nEnable);
  popup.GetSubMenu(0)->TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y, pWnd);
}

void CSKOKOView::OnUpdateOperationCalculateSize(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable(GetListCtrl().GetSelectedCount() > 0);
}

void CSKOKOView::OnOperationCalculateSize() 
{
  CListCtrl& ctrl = GetListCtrl();
  if (ctrl.GetSelectedCount() > 0)
  {
    CSKOKODoc& doc = *GetDocument();
    DWORD sz = 0;
    POSITION pos = ctrl.GetFirstSelectedItemPosition();
    while (pos)
    {
      int nItem = ctrl.GetNextSelectedItem(pos);
      int nFile = ctrl.GetItemData(nItem);
      CFileInfo fi;
      doc.GetFileInfo(nFile, &fi);
      sz += fi.fileSize;
    }
    CString s;
    s.Format(
      _T("Суммарный размер: %lu байт%s"),
      sz,
      (sz%10 == 1 && sz != 11) ?
        "" :
        ((sz < 10 || sz > 20) && sz%10 >= 2 && sz%10 <= 4) ?
          "а" :
          "ов");
    AfxMessageBox(s, MB_ICONINFORMATION);
  }
}
