#include "stdafx.h"
#include "FtpView.h"
#include "FtpViewDlg.h"

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

//============================================================
// Конструктор класса CFtpViewDlg 
//============================================================
CFtpViewDlg::CFtpViewDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CFtpViewDlg::IDD, pParent)
{
   //{{AFX_DATA_INIT(CFtpViewDlg)
   m_FtpAddress = _T("");
   //}}AFX_DATA_INIT

   // При использовании метода LoadIcon нет необходимости 
   // вызывать DestroyIcon 
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

//============================================================
// Деструктор класса CFtpViewDlg 
//============================================================
CFtpViewDlg::~CFtpViewDlg()
{
  // Если соединение с сервером FTP установлено, закрываем его
   if (m_FtpConnection != NULL)
   {
      m_FtpConnection -> Close();
      delete m_FtpConnection;
   }
   // Завершаем сеанс связи с Internet
   if (m_InternetSession != NULL)
   {
      m_InternetSession -> Close();
      delete m_InternetSession;
   }
   // Удаляем список изображений
   delete m_ImageList;
}

//============================================================
// Метод DoDataExchange класса CFtpViewDlg
// Выполняет привязку органов управления диалоговой панели 
// IDD_FTPVIEW_DIALOG и соответствующих элементов класса 
// CFtpViewDlg
//============================================================
void CFtpViewDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CFtpViewDlg)
   DDX_Control(pDX, IDC_STATUS, m_Status);
   DDX_Control(pDX, IDOK, m_Ok);
   DDX_Control(pDX, IDC_ON_TOP, m_OnTop);
   DDX_Control(pDX, IDC_CONNECT, m_Connect);
   DDX_Control(pDX, IDC_FTP_LIST, m_FtpList);
   DDX_Text(pDX, IDC_FTP_ADDRESS, m_FtpAddress);
   //}}AFX_DATA_MAP
}

//============================================================
// Таблица сообщений класса CFtpViewDlg 
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewDlg, CDialog)
   //{{AFX_MSG_MAP(CFtpViewDlg)
   ON_WM_PAINT()
   ON_WM_QUERYDRAGICON()

   // Сообщение от кнопки Connect
   ON_BN_CLICKED(IDC_CONNECT, OnConnect)
   // Сообщение от кнопки On Top
   ON_BN_CLICKED(IDC_ON_TOP, OnOnTop)
   // Сообщение с кодом извещения NM_DBLCLK от списка
   ON_NOTIFY(NM_DBLCLK, IDC_FTP_LIST, OnDblclkFtpList)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//============================================================
// Метод OnInitDialog класса CFtpViewDlg 
// Выполняет инициализацию диалоговой панели, а также сеанса
// связи с Internet (создает объект класса CInternetSession)
//============================================================
BOOL CFtpViewDlg::OnInitDialog()
{
   // Вызываем метод OnInitDialog базового класса CDialog
   CDialog::OnInitDialog();

   // Устанавливаем пиктограммы, которые будут отображаться 
   // в случае минимизации диалоговой панели 
   SetIcon(m_hIcon,TRUE);  // Пиктограмма стандартного размера
   SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера
   
   //=========================================================
   // Выполняем инициализацию списка IDC_FTP_LIST
   //=========================================================

   // Структура, для описания характеристик колонок списка
   LV_COLUMN   lv_column;
   // Переменная для определения размера списка
   CRect       rectList;

   // Названия для колонок списка
   TCHAR szColHeader[COL_NUM][10] = { 
         _T("Name"), 
         _T("Length"), 
         _T("Date"),
         _T("Time"), 
   };

   // Определяем размер области, которую занимает список 
   // IDC_FTP_LIST в диалоговой панели 
   m_FtpList.GetWindowRect(&rectList);

   // Указываем поля структуры lv_column, которые будут 
   // использоваться
   lv_column.mask = LVCF_FMT |   // Используется поле fmt
                LVCF_SUBITEM |   // Используется поле iSubItem
                   LVCF_TEXT |   // Используется поле pszText
                  LVCF_WIDTH;    // Используется поле cx

   // Задаем выравнивание по левому краю
   lv_column.fmt = LVCFMT_LEFT;

   // Ширина колонки
   lv_column.cx = (rectList.Width() / COL_NUM) - 1; 

   // Определяем характеристики колонок списка 
   for (int i = 0; i < COL_NUM; i++)  
   {
      // Номер колонки
      lv_column.iSubItem = i;
      // Зааголовок колонки 
      lv_column.pszText = szColHeader[i];
      // Добавляем колонку с заданными свойствами к списку
      m_FtpList.InsertColumn(i, &lv_column);
   }

   // Создаем список из двух изображений размера 16 х 16
   m_ImageList = new CImageList();
   m_ImageList -> Create(16, 16, TRUE, 2, 2);

   // Добавляем в список две пиктограммы - 
   // IDI_DIRECTORY (изображение каталога) и 
   // IDI_FILE(изображение файла ) 
   m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_DIRECTORY));
   m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_FILE));

   // Выбираем список изображений m_ImageList для 
   // использования в списке IDC_FTP_LIST
   m_FtpList.SetImageList(m_ImageList, LVSIL_SMALL);

   //=========================================================
   // Выбираем адрес сервера FTP, который используется по 
   // умолчанию
   //=========================================================

   m_FtpAddress = "dials.ccas.ru"; // Сервер FTP "ДиалогНаука"
   // Отображаем адрес на экране
   UpdateData(FALSE);

   //=========================================================
   // Инициализируем сеанс связи с Internet
   //=========================================================

   // Создаем сеанс связи с Internet, указываем в качестве 
   // имени программы-клиента название приложения FtpView
   m_InternetSession = new CInternetSession("FtpView");

   // В случае ошибки отображаем сообщение и завершаем 
   // приложение
   if(!m_InternetSession)
   {
      AfxMessageBox("New Session Error", MB_OK);
      OnOK();
   }
   // Инициализируем указатель m_FtpConnection
   m_FtpConnection = NULL;

   return TRUE;
}

//============================================================
// Метод OnPaint класса CFtpViewDlg 
// Отображает на экране пиктограмму в случае минимизации
// главной диалоговой панели приложения
//============================================================
void CFtpViewDlg::OnPaint() 
{
   if (IsIconic())
   {
      CPaintDC dc(this); 

      SendMessage(WM_ICONERASEBKGND, 
         (WPARAM) dc.GetSafeHdc(), 0);

      int cxIcon = GetSystemMetrics(SM_CXICON);
      int cyIcon = GetSystemMetrics(SM_CYICON);

      CRect rect;
      GetClientRect(&rect);
      
      int x = (rect.Width() - cxIcon + 1) / 2;
      int y = (rect.Height() - cyIcon + 1) / 2;

      dc.DrawIcon(x, y, m_hIcon);
   }
   else
   {
      CDialog::OnPaint();
   }
}

//============================================================
// Метод OnQueryDragIcon класса CFtpViewDlg 
// Сообщает идентификатор пиктограммы, отображаемой в случае 
// минимизации приложения
//============================================================
HCURSOR CFtpViewDlg::OnQueryDragIcon()
{
   return (HCURSOR) m_hIcon;
}

//============================================================
// Метод OnOnTop класса CFtpViewDlg 
// Переходит в каталог верхнего уровня
//============================================================
void CFtpViewDlg::OnOnTop() 
{
   // Блокируем список IDC_FTP_LIST
   m_FtpList.EnableWindow(FALSE);

   // Изменяем строку текущего каталога sCurentDirectory так, 
   // чтобы она показывала на каталог верхнего уровня.
   // Ищем последнее вхождение символа / в строку с именем 
   // каталога 
   int iNum = sCurentDirectory.ReverseFind('/');
   if(iNum == -1)
   {
      // Если символ / не обнаружен, значит мы находимся в 
      // корневом каталоге
      AfxMessageBox("No top directory");
   }
   else
   {
      // Удаляем из строки с именем текущего каталога названия 
      // последнего каталога
      sCurentDirectory = sCurentDirectory.Left(iNum);

      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   
   
      // Отображаем содержимое каталога верхнего уровня
      DirectoryView();   
   }

   // Снимаем блокировку списка IDC_FTP_LIST
   m_FtpList.EnableWindow(TRUE);

   // Отображаем на диалоговой панели новый путь каталога
   m_Status.SetWindowText(sCurentDirectory);

   return;
}

//============================================================
// Метод OnDblclkFtpList класса CFtpViewDlg 
// Переходит в выбранный каталог
//============================================================
void CFtpViewDlg::OnDblclkFtpList(NMHDR* pNMHDR, 
   LRESULT* pResult) 
{
   int       iTotalNumber; // Количество элементов списка
   CString   sSelItem;     // Название каталога
   CString   sLength_Dir;   // Длина файла или строка Dir

   // Блокируем список IDC_FTP_LIST
   m_FtpList.EnableWindow(FALSE);

   // Определяем количество элементов в списке IDC_FTP_LIST
   iTotalNumber = m_FtpList.GetItemCount();

   // Определяем, какой объект списка выбран
   for(int i = 0; i < iTotalNumber; i++)
   {
      // Атрибут LVIS_SELECTED установлен
      // у выбранного элемента списка 
      if(LVIS_SELECTED == m_FtpList.GetItemState( i, 
         LVIS_SELECTED ))
      {
         // Определяем название выбранного элемента списка
         // (имя файла или каталога)
         sSelItem = m_FtpList.GetItemText( i, 0 );

         // Считываем данные из колонки Length
         sLength_Dir = m_FtpList.GetItemText( i, 1 );

         if(sLength_Dir == "Dir") // Выбран каталог
			{
            // Переходим в выбранный каталог
            sCurentDirectory  = 
               sCurentDirectory + "/" + sSelItem;

				// Меняем форму курсора
				CWaitCursor wait;   

				// Отображаем содержимое выбранного каталога 
				DirectoryView();

				// Отображаем на диалоговой панели новый путь каталога
				m_Status.SetWindowText(sCurentDirectory);
			}

         else                     // Выбран файл
			{
			   // Меняем форму курсора
				CWaitCursor wait;   

				FtpFileDownload(sSelItem);
			}
         break;
      }
   }
   
   // Снимаем блокировку списка IDC_FTP_LIST
   m_FtpList.EnableWindow(TRUE);

   *pResult = 0;
}

//============================================================
// Метод OnConnect класса CFtpViewDlg 
// Соединяется с указанным сервером FTP
//============================================================
void CFtpViewDlg::OnConnect() 
{
   // Текущий каталог на сервере FTP
   CString  sCurrentFtpDirectory = "";

   // Блокируем кнопки Connect, OK и On Top
   m_Connect.EnableWindow(FALSE);
   m_Ok.EnableWindow(FALSE);
   m_OnTop.EnableWindow(FALSE);

   // Если вы ранее уже соединились с сервером FTP, разрываем 
   // эту связь и удаляем объект m_FtpConnection
   if (m_FtpConnection != NULL)
   {
      m_FtpConnection -> Close();
      
      delete m_FtpConnection;   
      m_FtpConnection = NULL;
   }

   // Считываем из диалоговой панели адрес сервера FTP, так 
   // как пользователь мог его уже изменить
   UpdateData(TRUE);

   // Пытаемся соединиться с сервером FTP 
   try
   {
      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   

      // Соединяемся с сервером FTP. Эта операция
      // может вызвать исключение CInternetException
      m_FtpConnection = 
         m_InternetSession->GetFtpConnection(m_FtpAddress);
   }
   catch (CInternetException* pEx)
   {
      // Обрабатываем исключение CInternetException
      TCHAR szErr[1024];  // временный буфер для сообщения

      // Выводим сообщение об ошибке
      if (pEx->GetErrorMessage(szErr, 1024))
         AfxMessageBox(szErr);
      else
         AfxMessageBox("GetFtpConnection Error");

      // Удаляем иссключение
      pEx->Delete();

      // Обнуляем указатель m_FtpConnection
      m_FtpConnection = NULL;
   }

   // Если соединение не установлено сообщяем об этом
   if( m_FtpConnection == NULL )
      m_Status.SetWindowText("Connect not established");

   // Если соединение установлено, определяем текущий каталог 
   // и отображаем его содержимое на экране
   else
   {
      // Определяем текущий каталог сервера FTP 
      BOOL fResult= 
         m_FtpConnection -> 
            GetCurrentDirectory(sCurrentFtpDirectory);
      if(fResult)
         sCurentDirectory = sCurrentFtpDirectory;
      else 
         AfxMessageBox("GetCurrentDirectory Error");

      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   

      // Отображаем содержимое выбранного каталога 
      DirectoryView();

      // Отображаем на диалоговой панели новый путь каталога
      m_Status.SetWindowText(sCurentDirectory);
	}

   // Снимаем блокировку кнопок Connect, OK и On Top
   m_Connect.EnableWindow(TRUE);
   m_Ok.EnableWindow(TRUE);
   m_OnTop.EnableWindow(TRUE);
}

//============================================================
// Метод DirectoryView класса CFtpViewDlg 
// Просматривает содержимое каталога и отображает его в 
// таблице m_FtpList
//============================================================
BOOL CFtpViewDlg::DirectoryView()
{
   // Переменная, сигнализирующая о получении последнего 
   // элемента каталога
   BOOL     fResult; 

   // Временная переменная, определяющая тип объекта -
   // файл или каталог 
   BOOL     fDirectory;

   // Структура для добавления нового элемента к списку
   LV_ITEM   lv_item;   

   // Удалить все элементы из списка IDC_FTP_LIST
   m_FtpList.DeleteAllItems();

   // Создаем объект класса CFtpFileFind, указывая 
   // объект, представляющий уже установленное соединение
   // с сервером FTP 
   CFtpFileFind m_FtpFileFind(m_FtpConnection);

   // Получаем имена всех объектов текущего каталога
   if(fResult = 
      m_FtpFileFind.FindFile(_T(sCurentDirectory + "/*")))
   {
      for(int n = 0;fResult; n++)
      {
         // Получаем очередной объект из каталога
         fResult = m_FtpFileFind.FindNextFile();

         // Определяем что это - каталог или файл
         fDirectory = m_FtpFileFind.IsDirectory();

         //============= Определяем имя объекта ==============
         // Временные строка для имени каталога или файла
         CString   fileName;

         // Определяем имя объекта
         fileName = m_FtpFileFind.GetFileName();

         // Заполняем структуру lv_item, сведениями об 
         // очередном объекте каталога сервера FTP. Указываем, 
         // что в список добавляется текст и изображение
         lv_item.mask = LVIF_TEXT | LVIF_IMAGE; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем первую колонку
         lv_item.iSubItem = 0;
         // Выбираем изображение для нового элемента списка в 
         // зависимости от типа объекта 
         lv_item.iImage = (fDirectory) ? DIRECTORY : FILE;
         // Указываем имя каталога или файла
         lv_item.pszText = fileName.GetBuffer(MIN_LEN_BUF);

         // Добавляем новый элемент к списку IDC_FTP_LIST
         m_FtpList.InsertItem(&lv_item);

         //============= Определяем длинну файла =============
         // Длинна файла
         DWORD dwLength = 0;
         // Временные строка для формирования текстового 
         // представления длинны файла
         CString sLength;

         // Заполняем колонку Length для новой записи и 
         // записываем в нее длинну файла или строку Dir, если 
         // новый объект - каталог.
         // Добавляется только текст без пиктограммы
         lv_item.mask = LVIF_TEXT; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем вторую колонку
         lv_item.iSubItem = 1;
         // Если очередной объект является каталогом, то 
         // вместо в колонке Length записываем строку Dir
         if(fDirectory)
         {
            lv_item.pszText = "Dir";
         }

         // Если очередной объект является файлом, то 
         // записываем его длину в колонку Length 
         else
         {
            // Определяем длину файла
            dwLength = m_FtpFileFind.GetLength();
            // Формируем текстовое представление длины файла
				sLength.Format("%d", dwLength);
            lv_item.pszText = sLength.GetBuffer(MIN_LEN_BUF);
         }
        
         // Добавляем запись во второй колонке списка (колонка 
         // Length)
         m_FtpList.SetItem(&lv_item);

         //======== Определяем дату и время создания =========
         // Дата и время создания каталога или файла
         CTime mTime;  
         // Временные строки для формирования текстового 
         // представления даты и времени 
         CString sDate;
         CString sTime;

         // Определяем время изменения файла или каталога
         if(!m_FtpFileFind.GetLastWriteTime(mTime))
            break;

         // Добавляется только текст без пиктограммы
         lv_item.mask = LVIF_TEXT; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем третью колонку
         lv_item.iSubItem = 2;
         // Выделяем из объекта mTime день, месяц и год 
         sDate = mTime.Format("%d.%m.%y");
         // Записываем сформированную дату в структуру lv_item
         lv_item.pszText = sDate.GetBuffer(MIN_LEN_BUF);

         // Добавляем запись во второй колонке списка (колонка 
         // Date)
         m_FtpList.SetItem(&lv_item);

         // Заполняем четвертую колонку, записываем в нее 
         // время последнего изменения файла (каталога)
         lv_item.iSubItem = 3;
         // Выделяем из объекта mTime часы, минуты и секунды 
         sTime = mTime.Format("%H:%M:%S");
         // Записываем сформированную строку, содержащую время
         lv_item.pszText = sTime.GetBuffer(MIN_LEN_BUF);

         // Добавляем запись во второй колонке списка (колонка 
         // Time)
         m_FtpList.SetItem(&lv_item);
      }
      // Заканчиваем поиск объектов в каталоге, закрываем 
      // объект m_FtpFileFind
      m_FtpFileFind.Close();
   }
   // Если каталог не содержит других объектов - каталогов и 
   // файлов, выводин соответствующее сообщение
   else
      AfxMessageBox("File's not found or error");

   return TRUE;
}

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
	BOOL fResult;
	CString sLocalFileName;


   // Определяем объект класса CFileDialog, представляющий 
   // стандартную диалоговую панель Save As, в которой
	// по умолчанию выбрано имя файла sFileName
   CFileDialog mFileOpen(FALSE, NULL, sFileName);

   // Отображаем диалоговую панель Open и позволяем 
   // пользователю выбрать с помощью нее один или 
   // несколько файлов
	int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open - 
   // по нажатию кнопки OK или Cancel
   if(result == IDCANCEL) 
   {
      // Если пользователь отказался от выбора файлов и 
      // нажал кнопку Cancel отображаем соответствующее 
      // сообщение и возвращаем значение FALSE
      AfxMessageBox("File not selected");

		return FALSE;
   }

   else if(result == IDOK)
   {
      // Если пользователь нажал кнопку OK, определяем 
      // имя выбранного файла
      sLocalFileName = mFileOpen.GetPathName();
   }

	sFileName = sCurentDirectory + "/" + sFileName;


	fResult = 
		m_FtpConnection -> GetFile(sFileName.GetBuffer(MIN_LEN_BUF),
											sLocalFileName.GetBuffer(MIN_LEN_BUF),
											FALSE
										  );
	return fResult;
}
