/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009 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://dooble.net/projects/dooble
**   http://dooble.sf.net
**   http://www.getadooble.com
****************************************************************************/

#include <QMenu>
#include <QtDebug>
#include <QFileInfo>
#include <QSettings>
#include <QWebHistory>
#include <QNetworkProxy>
#include <QContextMenuEvent>
#include <QWebHitTestResult>

#include "dmisc.h"
#include "dview.h"
#include "dwebpage.h"

dview::dview(QWidget *parent):QStackedWidget(parent),
			      clear_history_after_next_load_(false)
{
  webView = new QWebView(this);
  ftpBrowser = new dftpbrowser(this);
  fileManager = new dfilemanager(this);
  directoryView = new QWebView();

  dwebpage *page(new dwebpage(webView));

  webView->setPage(page);
  addWidget(webView);
  addWidget(fileManager);
  addWidget(ftpBrowser);
  setCurrentIndex(0);
  pageLoaded = false;
  percentLoaded = 0;
  selectedUrl = "";
  selectedImageUrl = "";
  webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
  webView->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(webView, SIGNAL(urlChanged(const QUrl &)), this,
	  SLOT(slotUrlChanged(const QUrl &)));
  connect(webView, SIGNAL(titleChanged(const QString &)), this,
	  SLOT(slotTitleChanged(const QString &)));
  connect(webView, SIGNAL(loadFinished(bool)), this,
	  SLOT(slotLoadFinished(bool)));
  connect(webView, SIGNAL(loadStarted(void)), this,
	  SLOT(slotLoadStarted(void)));
  connect(webView, SIGNAL(loadProgress(int)), this,
	  SLOT(slotLoadProgress(int)));
  connect(webView, SIGNAL(iconChanged(void)), this,
	  SLOT(slotIconChanged(void)));
  connect(fileManager, SIGNAL(loadFinished(bool)), this,
	  SLOT(slotLoadFinished(bool)));
  connect(fileManager, SIGNAL(loadStarted(void)), this,
	  SLOT(slotLoadStarted(void)));
  connect(fileManager, SIGNAL(urlChanged(const QUrl &)),
	  this, SLOT(slotUrlChanged(const QUrl &)));
  connect(fileManager, SIGNAL(titleChanged(const QString &)),
	  this, SLOT(slotTitleChanged(const QString &)));
  connect(fileManager, SIGNAL(iconChanged(void)),
	  this, SLOT(slotIconChanged(void)));
  connect(ftpBrowser, SIGNAL(loadFinished(bool)), this,
	  SLOT(slotLoadFinished(bool)));
  connect(ftpBrowser, SIGNAL(loadStarted(void)), this,
	  SLOT(slotLoadStarted(void)));
  connect(ftpBrowser, SIGNAL(urlChanged(const QUrl &)),
	  this, SLOT(slotUrlChanged(const QUrl &)));
  connect(ftpBrowser, SIGNAL(titleChanged(const QString &)),
	  this, SLOT(slotTitleChanged(const QString &)));
  connect(ftpBrowser, SIGNAL(iconChanged(void)),
	  this, SLOT(slotIconChanged(void)));
  connect(ftpBrowser, SIGNAL(saveUrl(const QUrl &)),
	  this, SLOT(slotSaveLink(const QUrl &)));
  connect(ftpBrowser, SIGNAL(copyLink(const QUrl &)),
	  this, SLOT(slotCopyLinkLocation(const QUrl &)));
  connect(ftpBrowser, SIGNAL(openLinkInNewTab(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewTab(const QUrl &)));
  connect(ftpBrowser, SIGNAL(openLinkInNewWindow(const QUrl &)),
	  this, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
  connect(webView, SIGNAL(customContextMenuRequested(const QPoint &)),
	  this, SLOT(slotCustomContextMenuRequested(const QPoint &)));
  connect(webView->page()->networkAccessManager(),
	  SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
	  this,
	  SLOT(slotSslErrors(QNetworkReply *, const QList<QSslError> &)));
  connect(webView->page(),
	  SIGNAL(unsupportedContent(QNetworkReply *)),
	  this,
	  SLOT(slotHandleUnsupportedContent(QNetworkReply *)));
  connect(webView->page(),
	  SIGNAL(selectionChanged(void)),
	  this,
	  SLOT(slotSelectionChanged(void)));
}

dview::~dview()
{
  directoryView->deleteLater();
}

void dview::slotLoadFinished(bool ok)
{
  pageLoaded = true;

  if(qobject_cast<QWebView *> (sender()))
    webView->unsetCursor();
  else if(qobject_cast<dfilemanager *> (sender()))
    {
      directoryView->setHtml(fileManager->html());
      fileManager->unsetCursor();
    }
  else if(qobject_cast<dftpbrowser *> (sender()))
    {
      directoryView->setHtml(ftpBrowser->html());
      ftpBrowser->unsetCursor();
    }

  if(qobject_cast<QWebView *> (sender()))
    {
      if(clear_history_after_next_load_)
	webView->history()->clear();
    }

  emit loadFinished(ok);
}

void dview::slotLoadStarted(void)
{
  pageLoaded = false;

  if(qobject_cast<QWebView *> (sender()))
    {
      setCurrentIndex(0);
      webView->setCursor(Qt::BusyCursor);
    }
  else if(qobject_cast<dfilemanager *> (sender()))
    {
      setCurrentIndex(1);
      fileManager->setCursor(Qt::BusyCursor);
    }
  else if(qobject_cast<dftpbrowser *> (sender()))
    {
      setCurrentIndex(2);
      ftpBrowser->setCursor(Qt::BusyCursor);
    }

  emit loadStarted();
}

void dview::slotLoadProgress(int progress)
{
  if(currentIndex() == 0)
    if(webView->cursor().shape() != Qt::BusyCursor)
      webView->setCursor(Qt::BusyCursor);

  if(progress >= 0)
    {
      percentLoaded = progress;
      emit loadProgress(percentLoaded);
    }
}

int dview::progress(void) const
{
  return percentLoaded;
}

bool dview::isLoaded(void) const
{
  return pageLoaded;
}

void dview::slotCustomContextMenuRequested(const QPoint &pos)
{
  QMenu menu(this);
  QWebHitTestResult result = webView->page()->currentFrame()->
    hitTestContent(pos);

  if(!result.linkUrl().isEmpty())
    {
      selectedUrl = result.linkUrl();
      menu.addAction(tr("Copy &Link Location"),
		     this, SLOT(slotCopyLinkLocation(void)));
      menu.addAction(tr("Copy &Selected Text"),
		     this, SLOT(slotCopySelectedText(void)));
      menu.addSeparator();
      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();
      menu.addAction(tr("&Save Link"),
		     this, SLOT(slotSaveLink(void)));
    }
  else
    {
      menu.addAction(tr("&Copy Selected Text"),
		     this,
		     SLOT(slotCopySelectedText(void)));
      menu.addSeparator();
      menu.addAction(tr("&Reload"),
		     this,
		     SLOT(slotReload(void)));
    }

  if(!result.imageUrl().isEmpty())
    {
      selectedImageUrl = result.imageUrl();

      if(!result.linkUrl().isEmpty())
	menu.addSeparator();

      menu.addAction(tr("Copy &Image Location"),
		     this, SLOT(slotCopyImageLocation(void)));
      menu.addSeparator();
      menu.addAction(tr("Sa&ve Image"),
		     this, SLOT(slotSaveImage(void)));
      menu.addSeparator();
      menu.addAction(tr("View I&mage"),
		     this, SLOT(slotViewImage(void)));
      menu.addAction(tr("View Im&age in New Tab"),
		     this, SLOT(slotOpenImageInNewTab(void)));
      menu.addAction(tr("View Ima&ge in New Window"),
		     this, SLOT(slotOpenImageInNewWindow(void)));
    }

  if(!menu.isEmpty())
    {
      menu.exec(mapToGlobal(pos));
      return;
    }
}

void dview::slotViewImage(void)
{
  emit viewImage(selectedImageUrl);
}

void dview::slotOpenLinkInNewTab(void)
{
  emit openLinkInNewTab(selectedUrl);
}

void dview::slotOpenLinkInNewTab(const QUrl &url)
{
  emit openLinkInNewTab(url);
}

void dview::slotOpenLinkInNewWindow(void)
{
  emit openLinkInNewWindow(selectedUrl);
}

void dview::slotOpenLinkInNewWindow(const QUrl &url)
{
  emit openLinkInNewWindow(url);
}

void dview::slotOpenImageInNewTab(void)
{
  emit openLinkInNewTab(selectedImageUrl);
}

void dview::slotOpenImageInNewWindow(void)
{
  emit openLinkInNewWindow(selectedImageUrl);
}

void dview::slotCopyLinkLocation(void)
{
  emit copyLink(selectedUrl);
  emit textCopied();
}

void dview::slotCopyLinkLocation(const QUrl &url)
{
  emit copyLink(url);
  emit textCopied();
}

void dview::slotCopyImageLocation(void)
{
  emit copyLink(selectedImageUrl);
  emit textCopied();
}

void dview::slotSaveLink(void)
{
  emit saveUrl(selectedUrl);
}

void dview::slotSaveLink(const QUrl &url)
{
  emit saveUrl(url);
}

void dview::slotSaveImage(void)
{
  emit saveUrl(selectedImageUrl);
}

void dview::clearHistoryAfterNextLoad()
{
  clear_history_after_next_load_ = true;
}

void dview::slotClearHistory(void)
{
  webView->history()->clear();
  fileManager->history()->clear();
}

void dview::load(const QUrl &url)
{
  if(url.isEmpty())
    return;

  webView->stop();
  ftpBrowser->stop();

  if(url.scheme().toLower() == "ftp")
    {
      setCurrentIndex(2);
      ftpBrowser->load(url);
    }
  else if(!QFileInfo(url.toLocalFile()).isDir())
    {
      setCurrentIndex(0);
      webView->page()->networkAccessManager()->setProxy
	(dmisc::proxyByUrl(url));
      webView->setUrl(url);
    }
  else
    {
      /*
      ** Display the directory's contents.
      */

      setCurrentIndex(1);
      fileManager->load(url);
    }
}

QIcon dview::icon(void) const
{
  if(!url().isEmpty() && url().toLocalFile().isEmpty())
    {
      QIcon icon(QWebSettings::iconForUrl(url()));

      if(icon.isNull())
	{
	  if(url().scheme().toLower() == "http")
	    return webView->icon();
	}
      else
	return icon;
    }

  QSettings settings;
  QSettings cfgSettings
    (settings.value("iconSet").toString(), QSettings::IniFormat);

  return QIcon(cfgSettings.value("mainWindow/emptyIcon").toString());
}

void dview::slotCopySelectedText(void)
{
  webView->triggerPageAction(QWebPage::Copy);
  emit textCopied();
}

dwebpage *dview::page(void) const
{
  return qobject_cast<dwebpage *> (webView->page());
}

QUrl dview::url(void) const
{
  if(currentIndex() == 0)
    return webView->url();
  else if(currentIndex() == 1)
    return fileManager->url();
  else if(currentIndex() == 2)
    return ftpBrowser->url();
  else
    return QUrl();
}

void dview::stop(void)
{
  webView->stop();
  ftpBrowser->stop();
  webView->unsetCursor();
  ftpBrowser->unsetCursor();
}

void dview::reload(void)
{
  if(currentIndex() == 0)
    {
      webView->page()->networkAccessManager()->setProxy
	(dmisc::proxyByUrl(webView->url()));
      webView->reload();
    }
  else if(currentIndex() == 1)
    fileManager->reload();
  else if(currentIndex() == 2)
    ftpBrowser->reload();
}

void dview::back(void)
{
  webView->stop();

  if(currentIndex() == 0)
    {
      if(webView->history()->canGoBack())
	{
	  webView->page()->networkAccessManager()->setProxy
	    (dmisc::proxyByUrl(webView->history()->backItem().url()));
	  webView->history()->back();
	}
      else if(!fileManager->url().isEmpty())
	load(fileManager->url());
    }
  else if(currentIndex() == 1)
    {
      if(fileManager->history()->canGoBack())
	fileManager->back();
      else if(!webView->url().isEmpty())
	load(webView->url());
    }
}

void dview::forward(void)
{
  if(currentIndex() == 0)
    {
      if(webView->history()->canGoForward())
	{
	  webView->page()->networkAccessManager()->setProxy
	    (dmisc::proxyByUrl(webView->history()->forwardItem().url()));
	  webView->history()->forward();
	}
      else if(!fileManager->url().isEmpty())
	load(fileManager->url());
    }
  else if(currentIndex() == 1)
    {
      if(fileManager->history()->canGoForward())
	fileManager->forward();
      else if(!webView->url().isEmpty())
	load(webView->url());
    }
}

QString dview::title(void) const
{
  if(currentIndex() == 0)
    return webView->title();
  else if(currentIndex() == 1)
    return fileManager->title();
  else if(currentIndex() == 2)
    return ftpBrowser->title();
  else
    return QString();
}

void dview::slotUrlChanged(const QUrl &url)
{
  emit urlChanged(url);
}

void dview::slotTitleChanged(const QString &title)
{
  emit titleChanged(title);
}

void dview::slotIconChanged(void)
{
  emit iconChanged();
}

bool dview::canGoBack(void) const
{
  if(currentIndex() == 0)
    {
      if(webView->history()->canGoBack())
	return true;
      else
	return !fileManager->url().isEmpty();
    }
  else if(currentIndex() == 1)
    {
      if(fileManager->history()->canGoBack())
	return true;
      else
	return !webView->url().isEmpty();
    }
  else
    return false;
}

bool dview::canGoForward(void) const
{
  if(currentIndex() == 0)
    {
      if(webView->history()->canGoForward())
	return true;
      else
	return !fileManager->url().isEmpty();
    }
  else if(currentIndex() == 1)
    {
      if(fileManager->history()->canGoForward())
	return true;
      else
	return !webView->url().isEmpty();
    }
  else
    return false;
}

QList<dwebhistoryitem> dview::backItems(const int n) const
{
  if(n <= 0)
    return QList<dwebhistoryitem> ();

  if(currentIndex() == 0)
    {
      QList<dwebhistoryitem> list;

      for(int i = 0; i < webView->history()->backItems(n).size(); i++)
	{
	  dwebhistoryitem item(webView->history()->backItems(n).at(i).url());

	  if(!list.contains(item))
	    list.append(item);
	}

      if(list.size() < n)
	list += fileManager->history()->backItems(n - list.size());

      if(list.size() < n && !fileManager->url().isEmpty())
	{
	  dwebhistoryitem item(fileManager->url());

	  if(!list.contains(item))
	    list.append(item);
	}

      return list;
    }
  else if(currentIndex() == 1)
    {
      QList<dwebhistoryitem> list(fileManager->history()->backItems(n));

      if(list.size() < n)
	{
	  int s = list.size();

	  for(int i = 0; i < webView->history()->backItems(n - s).size(); i++)
	    {
	      dwebhistoryitem item
		(webView->history()->backItems(n - s).at(i).url());

	      if(!list.contains(item))
		list.append(item);
	    }
	}

      if(list.size() < n && !webView->url().isEmpty())
	{
	  dwebhistoryitem item(webView->url());

	  if(!list.contains(item))
	    list.append(item);
	}

      return list;
    }
  else
    return QList<dwebhistoryitem> ();
}

QList<dwebhistoryitem> dview::forwardItems(const int n) const
{
  if(n <= 0)
    return QList<dwebhistoryitem> ();

  if(currentIndex() == 0)
    {
      QList<dwebhistoryitem> list;

      for(int i = 0; i < webView->history()->forwardItems(n).size(); i++)
	{
	  dwebhistoryitem item
	    (webView->history()->forwardItems(n).at(i).url());

	  if(!list.contains(item))
	    list.append(item);
	}

      if(list.size() < n)
	list += fileManager->history()->forwardItems(n - list.size());

      if(list.size() < n && !fileManager->url().isEmpty())
	{
	  dwebhistoryitem item(fileManager->url());

	  if(!list.contains(item))
	    list.append(item);
	}

      return list;
    }
  else if(currentIndex() == 1)
    {
      QList<dwebhistoryitem> list(fileManager->history()->forwardItems(n));

      if(list.size() < n)
	{
	  int s = list.size();

	  for(int i = 0; i < webView->history()->forwardItems(n - s).size();
	      i++)
	    {
	      dwebhistoryitem item
		(webView->history()->forwardItems(n - s).at(i).url());

	      if(!list.contains(item))
		list.append(item);
	    }
	}

      if(list.size() < n && !webView->url().isEmpty())
	{
	  dwebhistoryitem item(webView->url());

	  if(!list.contains(item))
	    list.append(item);
	}

      return list;
    }
  else
    return QList<dwebhistoryitem> ();
}

void dview::print(QPrinter *printer)
{
  if(!printer)
    return;

  QWebFrame *frame = currentFrame();

  if(frame)
    frame->print(printer);
}

QWebFrame *dview::currentFrame(void) const
{
  if(currentIndex() == 0)
    return webView->page()->currentFrame();
  else if(currentIndex() == 1)
    {
      directoryView->setHtml(fileManager->html());
      return directoryView->page()->currentFrame();
    }
  else if(currentIndex() == 2)
    {
      directoryView->setHtml(ftpBrowser->html());
      return directoryView->page()->currentFrame();
    }
  else
    return 0;
}

QString dview::html(void) const
{
  if(currentIndex() == 0)
    return webView->page()->currentFrame()->toHtml();
  else if(currentIndex() == 1)
    {
      directoryView->setHtml(fileManager->html());
      return directoryView->page()->currentFrame()->toHtml();
    }
  else if(currentIndex() == 2)
    {
      directoryView->setHtml(ftpBrowser->html());
      return directoryView->page()->currentFrame()->toHtml();
    }
  else
    return "";
}

void dview::slotReload(void)
{
  load(url());
}

bool dview::isDir(void) const
{
  if(QFileInfo(url().toLocalFile()).isDir())
    return true;
  else
    return false;
}

bool dview::isFtp(void) const
{
  if(url().scheme().toLower() == "ftp")
    return true;
  else
    return false;
}

QWebHistory *dview::webHistory(void) const
{
  return webView->history();
}

void dview::slotSslErrors(QNetworkReply *reply,
			  const QList<QSslError> &errors)
{
  /*
  ** Dooble must, at some point, allow the user to configure SSL
  ** certificates.
  */

  Q_UNUSED(errors);

  if(!reply)
    return;

  reply->ignoreSslErrors();
}

void dview::setFocus(void)
{
  if(currentIndex() == 0)
    webView->setFocus();
  else if(currentIndex() == 1)
    fileManager->setFocus();
  else if(currentIndex() == 2)
    ftpBrowser->setFocus();
}

void dview::slotHandleUnsupportedContent(QNetworkReply *reply)
{
  if(reply && reply->error() == QNetworkReply::NoError)
    {
      reply->abort();
      webView->stop();
      emit saveUrl(reply->url());
    }
}

void dview::enterEvent(QEvent *event)
{
  QWidget::enterEvent(event);
  emit viewEntered();
}

void dview::slotSelectionChanged(void)
{
  emit selectionChanged(webView->selectedText());
}
