/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010 Alexis Megas,
** Gunther van Dooble, and the Dooble Team.
** All rights reserved.
**
** License: GPL2 only:
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; version 2 of the License only.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** or see here: http://www.gnu.org/licenses/gpl.html
**
** For the WebKit library, please see: http://webkit.org.
**
** THE CODE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
** IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS APPLICATION, EVEN IF ADVISED
** OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Please report all praise, requests, bugs, and problems to the project
** team and administrators: http://sf.net/projects/dooble.
**
** You can find us listed at our project page. New team members are welcome.
** The name of the authors should not be used to endorse or promote products
** derived from Dooble without specific prior written permission.
** If you use this code for other projects, please let us know.
**
** Web sites:
**   http://sf.net/projects/dooble
**   http://dooble.sf.net
****************************************************************************/

#include <QMenu>
#include <QHeaderView>
#include <QContextMenuEvent>
#include <QFileIconProvider>

#include "dmisc.h"
#include "dooble.h"
#include "dftpbrowser.h"

dftpbrowser::dftpbrowser(QWidget *parent):QTableWidget(parent)
{
  setColumnCount(3);
  setSelectionMode(QAbstractItemView::SingleSelection);
  setSortingEnabled(true);
  setSelectionBehavior(QAbstractItemView::SelectRows);
  sortByColumn(0, Qt::AscendingOrder);
  connect(this,
	  SIGNAL(itemDoubleClicked(QTableWidgetItem *)),
	  this,
	  SLOT(slotItemDoubleClicked(QTableWidgetItem *)));
  connect(this,
	  SIGNAL(customContextMenuRequested(const QPoint &)),
	  this,
	  SLOT(slotCustomContextMenuRequest(const QPoint &)));
  connect(horizontalHeader(),
	  SIGNAL(sectionMoved(int, int, int)),
	  this,
	  SLOT(slotSaveTableHeaderState(void)));
  connect(horizontalHeader(),
	  SIGNAL(sectionClicked(int)),
	  this,
	  SLOT(slotSaveTableHeaderState(void)));
  connect(horizontalHeader(),
	  SIGNAL(sectionResized(int, int, int)),
	  this,
	  SLOT(slotSaveTableHeaderState(void)));
  m_history = new dwebhistory();

  QStringList labels;

  labels << tr("Name") << tr("Size") << tr("Date Modified");
  setHorizontalHeaderLabels(labels);
  labels.clear();
  horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
  horizontalHeader()->setStretchLastSection(true);
  setAlternatingRowColors(true);
  verticalHeader()->setVisible(false);
  setContextMenuPolicy(Qt::CustomContextMenu);
  setShowGrid(false);
  setFrameShape(QFrame::NoFrame);

  if(dooble::s_settings.contains("mainWindow/ftpManagerColumnsState"))
    if(!horizontalHeader()->restoreState
       (dooble::s_settings.value
	("mainWindow/ftpManagerColumnsState", "").toByteArray()))
      {
	horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
	horizontalHeader()->setStretchLastSection(true);

	for(int i = 0; i < horizontalHeader()->count(); i++)
	  resizeColumnToContents(i);
      }
}

void dftpbrowser::slotSaveTableHeaderState(void)
{
  dooble::s_settings["mainWindow/ftpManagerColumnsState"] =
    horizontalHeader()->saveState();
}

dftpbrowser::~dftpbrowser()
{
  if(m_history)
    {
      delete m_history;
      m_history = 0;
    }
}

void dftpbrowser::load(const QUrl &url)
{
  clearContents();
  setRowCount(0);

  /*
  ** m_url is set to m_tmpUrl if and only if the load
  ** was successful.
  */

  m_tmpUrl = url;
  m_selectedUrl.clear();
  m_cdCommandId = -1;
  m_listCommandId = -1;

  if(m_ftp)
    {
      m_ftp->abort();
      m_ftp->deleteLater();
    }

  m_ftp = new QFtp(this);
  connect(m_ftp,
	  SIGNAL(stateChanged(int)),
	  this,
	  SLOT(slotStateChanged(int)));
  connect(m_ftp,
	  SIGNAL(listInfo(const QUrlInfo &)),
	  this,
	  SLOT(slotListInfo(const QUrlInfo &)));
  connect(m_ftp,
	  SIGNAL(commandFinished(int, bool)),
	  this,
	  SLOT(slotCommandFinished(int, bool)));

  QNetworkProxy proxy(dmisc::proxyByUrl(url));

  m_ftp->setProxy(proxy.hostName(), proxy.port());
  m_ftp->connectToHost(url.host());
  emit loadStarted();
  emit iconChanged();
  emit loadProgress(-1);
}

QString dftpbrowser::html(void) const
{
  QString str("");

  str += "<html>\n";
  str += "<table border=\"0\">\n";
  str += "<tr>\n";

  for(int i = 0; i < columnCount(); i++)
    str += QString("<th>%1</th>\n").arg(horizontalHeaderItem(i)->text());

  str += "</tr>\n";

  for(int i = 0; i < rowCount(); i++)
    {
      str += "<tr>\n";

      for(int j = 0; j < columnCount(); j++)
	if(item(i, j))
	  str += QString("<td>%1</td>").arg(item(i, j)->text());

      str += "</tr>\n";
    }

  str += "</table>\n";
  str += "</html>";
  return str;
}

QUrl dftpbrowser::url(void) const
{
  if(m_url.isValid())
    return m_url;
  else
    return m_tmpUrl;
}

void dftpbrowser::reload(void)
{
  stop();

  if(m_url.isValid())
    load(m_url);
  else
    load(m_tmpUrl);
}

QString dftpbrowser::title(void) const
{
  if(m_url.isValid())
    return m_url.toString(QUrl::StripTrailingSlash);
  else
    return m_tmpUrl.toString(QUrl::StripTrailingSlash);
}

void dftpbrowser::slotStateChanged(int state)
{
  if(state == QFtp::Connected)
    {
      if(m_ftp)
	m_ftp->login(m_tmpUrl.userName(), m_tmpUrl.password());
    }
  else if(state == QFtp::LoggedIn)
    {
      if(m_ftp)
	{
	  emit urlChanged(m_tmpUrl);
	  emit titleChanged(m_tmpUrl.toString(QUrl::StripTrailingSlash));

	  if(m_tmpUrl.path().isEmpty())
	    m_cdCommandId = m_ftp->cd(".");
	  else
	    m_cdCommandId = m_ftp->cd(m_tmpUrl.path());
	}
    }
}

void dftpbrowser::slotListInfo(const QUrlInfo &i)
{
  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  QTableWidgetItem *item = 0;

  if(rowCount() == 0)
    {
      setRowCount(1);

      for(int i = 0; i < 3; i++)
	{
	  if(i == 0)
	    {
	      QFileIconProvider iconProvider;

	      item = new QTableWidgetItem
		(iconProvider.icon(QFileIconProvider::Folder), "..", 1);
	    }
	  else
	    item = new QTableWidgetItem("", 1);

	  item->setFlags(flags);
	  setItem(0, i, item);
	}
    }

  setRowCount(rowCount() + 1);

  QFileIconProvider iconProvider;

  if(i.isValid() && i.isDir())
    item = new QTableWidgetItem
      (iconProvider.icon(QFileIconProvider::Folder), i.name(), 1);
  else
    item = new QTableWidgetItem
      (iconProvider.icon(QFileIconProvider::File), i.name(), 0);

  item->setFlags(flags);
  setItem(rowCount() - 1, 0, item);

  if(i.isValid() && i.isDir())
    item = new QTableWidgetItem("", 1);
  else
    {
      QString str("");

      if(i.size() < 1024)
	str = QString(tr("%1 Byte(s)")).arg(i.size());
      else if(i.size() < 1048576)
	str = QString(tr("%1 kB")).arg
	  (QString::number(i.size() / 1024));
      else
	str = QString(tr("%1 MB")).arg
	  (QString::number((i.size() * 1.0) / 1048576, 'f', 1));

      item = new QTableWidgetItem(str);
    }

  item->setFlags(flags);
  setItem(rowCount() - 1, 1, item);
  item = new QTableWidgetItem(i.lastModified().toString("M/dd/yy h:mm AP"),
			      i.isValid() && i.isDir());
  item->setFlags(flags);
  setItem(rowCount() - 1, 2, item);
}

void dftpbrowser::slotCommandFinished(int id, bool error)
{
  if(id == m_cdCommandId)
    {
      if(m_ftp && !error)
	{
	  clear();
	  setRowCount(0);

	  QStringList labels;

	  labels << tr("Name") << tr("Size") << tr("Date Modified");
	  setHorizontalHeaderLabels(labels);
	  labels.clear();
	  horizontalHeader()->setStretchLastSection(true);
	  m_url = m_tmpUrl;
	  emit urlChanged(m_url);
	  emit titleChanged(m_url.toString(QUrl::StripTrailingSlash));
	  m_listCommandId = m_ftp->list(m_url.path());
	  emit loadStarted();
	  emit loadProgress(-1);
	}
      else if(m_ftp)
	{
	  /*
	  ** Some directories are not really directories.
	  */

	  m_ftp->abort();
	  m_ftp->deleteLater();
	  emit loadFinished(false);
	}
    }
  else if(id == m_listCommandId)
    {
      m_history->add(m_url);
      emit loadFinished(!error);

      if(m_ftp)
	{
	  m_ftp->abort();
	  m_ftp->deleteLater();
	}
    }
}

void dftpbrowser::slotItemDoubleClicked(QTableWidgetItem *item)
{
  if(item)
    if(item->type() == 1)
      {
	item = this->item(currentRow(), 0);

	if(item)
	  load(QUrl(m_url.toString(QUrl::StripTrailingSlash) + "/" +
		    item->text()));
      }
}

void dftpbrowser::slotCustomContextMenuRequest(const QPoint &point)
{
  QTableWidgetItem *item = itemAt(point);

  if(item)
    {
      QMenu menu(this);

      item = this->item(item->row(), 0);
      m_selectedUrl = QUrl(m_url.toString(QUrl::StripTrailingSlash) + "/" +
			   item->text());
      menu.addAction(tr("Copy &Link Location"),
		     this, SLOT(slotCopyLinkLocation(void)));
      menu.addSeparator();

      if(item->type() == 1)
	{
	  menu.addAction(tr("Open Link in New &Tab"),
			 this, SLOT(slotOpenLinkInNewTab(void)));
	  menu.addAction(tr("Open Link in &New Window"),
			 this, SLOT(slotOpenLinkInNewWindow(void)));
	  menu.addSeparator();
	}
      else
	menu.addAction(tr("&Save Link"),
		       this, SLOT(slotSaveLink(void)));

      menu.exec(mapToGlobal(point));
    }
}

void dftpbrowser::slotSaveLink(void)
{
  emit saveUrl(m_selectedUrl);
}

void dftpbrowser::slotCopyLinkLocation(void)
{
  emit copyLink(m_selectedUrl);
}

void dftpbrowser::slotOpenLinkInNewTab(void)
{
  emit openLinkInNewTab(m_selectedUrl);
}

void dftpbrowser::slotOpenLinkInNewWindow(void)
{
  emit openLinkInNewWindow(m_selectedUrl);
}

void dftpbrowser::stop(void)
{
  if(m_ftp)
    {
      m_ftp->abort();
      m_ftp->deleteLater();
    }
}

void dftpbrowser::back(void)
{
  load(m_history->back().url());
}

void dftpbrowser::forward(void)
{
  load(m_history->forward().url());
}
