/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011, 2012 Alexis Megas, Bernd Stramm,
** 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
****************************************************************************/

#ifndef _dooble_h_
#define _dooble_h_

#include <QUuid>
#include <QThread>
#include <QPointer>
#include <QWebPage>
#include <QSqlQuery>
#include <QSqlDatabase>

#include "dmisc.h"
#include "dview.h"
#include "dcookies.h"
#include "dhistory.h"
#include "dsettings.h"
#include "ui_statusBar.h"
#include "dcookiewindow.h"
#include "dhistorymodel.h"
#include "ui_mainWindow.h"
#include "ddesktopwidget.h"
#include "ddownloadwindow.h"
#include "dhistorysidebar.h"
#include "dexceptionswindow.h"
#include "plugin-spec/extension.h"
#include "plugin-spec/signal-agent.h"

#define DOOBLE_VERSION 0x013500
#define DOOBLE_VERSION_STR "1.35"

using namespace simpleplugin;

class QCloseEvent;

class derrorlog;
class dplugintab;
class dnetworkcache;
class dbookmarkspopup;
class dbookmarkswindow;
class dclearcontainers;

class dooble: public QMainWindow
{
  Q_OBJECT

 public:
  static int s_instances;
  static QPointer<QMenu> s_bookmarksPopupMenu;
  static QUuid s_id; // Unique process ID.
  static QMutex s_saveHistoryMutex;
  static QString s_homePath;
  static QPointer<dcookies> s_cookies;
  static const int MAX_HISTORY_ITEMS = 15;
  static const int MAX_MOST_VISITED_ITEMS = 15;
  static const int MAX_NUMBER_OF_MENU_TITLE_CHARACTERS = 100;
  static QPointer<dhistory> s_historyWindow;
  static QPointer<derrorlog> s_errorLog;
  static QPointer<dsettings> s_settingsWindow;
  static QPointer<dcookiewindow> s_cookieWindow;
  static QPointer<dhistorymodel> s_historyModel;
  static QPointer<dnetworkcache> s_networkCache;
  static QPointer<dbookmarkspopup> s_bookmarksPopup;
  static QPointer<ddownloadwindow> s_downloadWindow;
  static QPointer<dbookmarkswindow> s_bookmarksWindow;
  static QPointer<dclearcontainers> s_clearContainersWindow;
  static QPointer<dexceptionswindow> s_dntWindow;
  static QPointer<dexceptionswindow> s_popupsWindow;
  static QPointer<dexceptionswindow> s_adBlockWindow;
  static QPointer<dexceptionswindow> s_imageBlockWindow;
  static QPointer<dexceptionswindow> s_cookiesBlockWindow;
  static QPointer<dexceptionswindow> s_httpRedirectWindow;
  static QPointer<dexceptionswindow> s_httpReferrerWindow;
  static QPointer<dexceptionswindow> s_cacheExceptionsWindow;
  static QPointer<dexceptionswindow> s_javaScriptExceptionsWindow;
  static QPointer<QStandardItemModel> s_bookmarksFolderModel;
  static QHash<QString, int> s_mostVisitedHosts;
  static QMap<QString, QString> s_applicationsActions;
  static QHash<QString, QVariant> s_settings;
  static void s_makeCrashFile(void);
  static void s_removeCrashFile(void);
  dooble(const bool isJavaScriptWindow, dooble *d); /*
						    ** Used for JavaScript
						    ** windows.
						    */
  dooble(const QUrl &url, dooble *d);
  dooble(const QByteArray &history, dooble *d);
  dooble(dview *p, dooble *d);
  dooble(const QHash<QString, QVariant> &hash, dooble *d);
  ~dooble();
  void copyLink(const QUrl &url);
  void closeTab(const int index);
  void closeEvent(QCloseEvent *event);
  void setCurrentPage(dview *p);
  dview *newTab(const QByteArray &history);
  qint64 id(void) const;
  QList<QVariantList> tabUrls(void) const;

 private:
  struct
  {
    int choice;
    QUrl url;
    QString html;
  } m_saveDialogHackContainer;

  bool m_isJavaScriptWindow;
  bool showFindFrame;
  qint64 m_id;
  QWidget *sbWidget;
  Ui_statusBar sb;
  Ui_mainWindow ui;
  dhistorysidebar *m_historySideBar;
  QPointer<dooble> m_parentWindow; /*
				   ** Used for positioning JavaScript
				   ** windows.
				   */
  QList<QByteArray> m_closedTabs;
  QPointer<ddesktopwidget> m_desktopWidget;

  struct PluginRec
  {
    PluginRec(dplugintab *t = 0, const QString &fn = QString())
      :tab(t), fileName(fn)
    {
    }

    dplugintab *tab;
    QString    fileName;
  };

  QMap<QWidget *, PluginRec> pluginMap;
  QMap<QString, int>         pluginLoadCount;
  bool warnAboutTabs(QObject *object);
  bool warnAboutDownloads(void);
  bool promptForPassphrase(const bool override = false);
  bool warnAboutLeavingModifiedTab(void);
  void find(const QWebPage::FindFlags flags);
  void init(const bool isJavaScriptWindow);
  void loadPage(const QUrl &url);
  void reinstate(void);
  void copyDooble(dooble *d);
  void newTabInit(dview *p);
  void saveHandler(const QUrl &url,
		   const QString &html,
		   const QString &fileName,
		   const int choice);
  void closeDesktop(void);
  void launchDooble(const QUrl &url);
  void saveSettings(void);
  void keyPressEvent(QKeyEvent *event);
  void setUrlHandler(dooble *d);
  void prepareTabsMenu(void);
  void unsetUrlHandler(void);
  void cleanupBeforeExit(void);
  void initializeHistory(void);
  void prepareMostVisited(void);
  void clearHistoryWidgets(void);
  void highlightBookmarkButton(void);
  void initializeBookmarksMenu(void);
  void prepareWidgetsBasedOnView(dview *p);
  void remindUserToSetPassphrase(void);
  void prepareNavigationButtonMenus(dview *p, QMenu *menu);
  dview *newTab(const QUrl &url);
  dview *newTab(dview *p);
  void startPlugin(const QString &pluginName);
  Extension *loadPlugin(const QString &pluginName);
  void addPluginAction(const QString &pluginFileName);
  QString pluginFileName(const QString &plugName);
  QString pluginPath(void);

 private slots:
  void slotBack(void);
  void slotCopy(void);
  void slotFind(void);
  void slotQuit(void);
  void slotStop(void);
  void slotAbout(void);
  void slotClose(void);
  void slotPaste(void);
  void slotPrint(void);
  void slotGoHome(void);
  void slotNewTab(void);
  void slotReload(void);
  void slotSearch(void);
  void slotForward(void);
  void slotOffline(bool state);
  void slotOpenUrl(void);
  void slotSaveUrl(const QUrl &url, const int choice);
  void slotBookmark(void);
  void slotBookmark(const int index);
  void slotCloseTab(void);
  void slotCloseTab(const int index);
  void slotCopyLink(const QUrl &url);
  void slotFindNext(void);
  void slotHideFind(void);
  void slotLoadPage(void);
  void slotLoadPage(const QUrl &url);
  void slotResetUrl(void);
  void slotSaveFile(const QString &fileName, const QUrl &url,
		    const int choice);
  void slotSavePage(void);
  void slotSetIcons(void);
  void slotShowFind(void);
  void slotTabMoved(int from, int to);
  void slotNewWindow(void);
  void slotReloadTab(const int index);
  void slotShowAddOns(void);
  void slotUrlChanged(const QUrl &url);
  void slotViewZoomIn(void);
  void slotCloseWindow(void);
  void slotEnablePaste(void);
  void slotErrorLogged(void);
  void slotHideMenuBar(void);
  void slotHideToolBar(void);
  void slotIconChanged(void);
  void slotLinkHovered(const QString &link, const QString &title,
		       const QString &textContent);
  void slotShowHistory(void);
  void slotViewZoomOut(void);
  void slotLoadStarted(void);
  void slotTabSelected(const int index);
  void slotTextChanged(const QString &text);
  void slotViewEntered(void);
  void slotAuthenticate(void);
  void slotClearHistory(void);
  void slotFindPrevious(void);
  void slotLoadFinished(bool ok);
  void slotLoadProgress(int progress);
  void slotOpenP2PEmail(void);
  void slotPrintPreview(void);
  void slotTitleChanged(const QString &title);
  void slotMarkerEntered(void);
  void slotOpenDirectory(void);
  void slotHideStatusBar(void);
  void slotShowBookmarks(void);
  void slotShowIpAddress(const bool state);
  void slotViewResetZoom(void);
  void slotFullScreenMode(void);
  void slotOpenIrcChannel(void);
  void slotPrintRequested(QWebFrame *frame);
  void slotQuitAndRestart(void);
  void slotShowDesktopTab(const bool state = true);
  void slotShowPageSource(void);
  void slotClearContainers(void);
  void slotExceptionRaised(dexceptionswindow *window,
			   const QUrl &url);
  void slotObjectDestroyed(QObject *object);
  void slotReopenClosedTab(void);
  void slotShowHiddenFiles(bool state);
  void slotBookmarksChanged(void);
  void slotIpAddressChanged(const QString &ipAddress);
  void slotOpenLinkInNewTab(const QUrl &url);
  void slotOpenUrlsFromDrop(const QList<QUrl> &list);
  void slotRepaintRequested(const QRect &dirtyRect);
  void slotSelectAllContent(void);
  void slotSelectionChanged(void);
  void slotSelectionChanged(const QString &text);
  void slotSetTabBarVisible(const bool state);
  void slotStatusBarDisplay(bool state);
  void slotStatusBarMessage(const QString &text);
  void slotViewZoomTextOnly(bool enable);
  void slotShowSettingsWindow(void);
  void slotShowHistorySideBar(bool state);
  void slotGoToBackHistoryItem(void);
  void slotLinkActionTriggered(void);
  void slotOpenLinkInNewWindow(const QUrl &url);
  void slotOpenPageInNewWindow(const int index);
  void slotHistorySideBarClosed(void);
  void slotOpenMyRetrievedFiles(void);
  void slotPercentReadFromCache(const int percent);
  void slotPercentWrittenToDisk(const int percent);
  void slotShowFavoritesToolBar(bool checked);
  void slotDisplayDownloadWindow(void);
  void slotLocationSplitterMoved(int post, int index);
  void slotShowRestoreSessionTab(void);
  void slotAuthenticationRequired(QNetworkReply *reply,
				  QAuthenticator *authenticator);
  void slotGoToForwardHistoryItem(void);
  void slotHandleQuirkySaveDialog(int result);
  void slotShowApplicationCookies(void);
  void slotClearRecentlyClosedTabs(void);
  void slotGeometryChangeRequested(const QRect &geometry);
  void slotHistoryTabSplitterMoved(int pos, int index);
  void slotAboutToShowBookmarksMenu(void);
  void slotChangeDesktopBackgrounds(void);
  void slotAboutToShowBackForwardMenu(void);
  void slotFavoritesToolButtonClicked(void);
  void slotProxyAuthenticationRequired(const QNetworkProxy &proxy,
				       QAuthenticator *authenticator);
  void slotRefreshPlugins(void);
  void slotPluginAction(QAction *plugAction);
  void slotPluginExiting(Extension *plugin, int status);
  friend class dwebpage;

 signals:
  void clearHistory(void);
  void iconsChanged(void);
  void bookmarkUpdated(void);
  void updateDesktopBackground(void);
  void passphraseWasAuthenticated(const bool state);
};

class dsavehistorythread: public QThread
{
  Q_OBJECT

 public:
  dsavehistorythread(QObject *parent, const QUrl &url, const QString &title,
		     const QVariant &userData, const QByteArray &bytes,
		     const int temporary, const bool saveHistory):
    QThread(parent)
  {
    m_url = url;
    m_bytes = bytes;
    m_title = title;
    m_userData = userData;
    m_temporary = temporary;
    m_saveHistory = saveHistory;
  }

  ~dsavehistorythread()
  {
    wait();
  }

  void run(void)
  {
    /*
    ** Do not disable database synchronization. Other methods require
    ** valid QIcon objects. Qt is sensitive.
    */

    QMutexLocker locker(&dooble::s_saveHistoryMutex);

    /*
    ** If the item is bookmarked, update the bookmark's attributes.
    */

    {
      QSqlDatabase db = QSqlDatabase::addDatabase
	("QSQLITE", "bookmarks_thread");

      db.setDatabaseName(dooble::s_homePath + QDir::separator() +
			 "bookmarks.db");

      if(db.open())
	{
	  QSqlQuery query(db);

	  if(query.exec(QString("SELECT url, visits FROM bookmarks WHERE "
				"temporary = %1").arg(m_temporary)))
	    while(query.next())
	      {
		QUrl url
		  (QUrl::fromEncoded
		   (dmisc::decodedString
		    (QByteArray::fromBase64
		     (query.value(0).toByteArray())),
		    QUrl::StrictMode));

		if(url.toString(QUrl::StripTrailingSlash) !=
		   m_url.toString(QUrl::StripTrailingSlash))
		  continue;

		int count = dmisc::decodedString
		  (QByteArray::fromBase64(query.value(1).
					  toByteArray())).toInt() + 1;
		QSqlQuery updateQuery(db);

		updateQuery.prepare
		  ("UPDATE bookmarks SET visits = ?, "
		   "last_visited = ?, "
		   "icon = ? "
		   "WHERE url = ? AND "
		   "temporary = ?");
		updateQuery.bindValue
		  (0,
		   dmisc::encodedString(QString::number(count).toAscii(),
					true).toBase64());
		updateQuery.bindValue
		  (1,
		   dmisc::encodedString(QDateTime::currentDateTime().
					toString(Qt::ISODate).
					toUtf8(), true).toBase64());
		updateQuery.bindValue
		  (2,
		   dmisc::encodedString(m_bytes,
					true).toBase64());
		updateQuery.bindValue(3, query.value(0));
		updateQuery.bindValue(4, m_temporary);
		updateQuery.exec();
		break;
	      }
	}

      db.close();
    }

    QSqlDatabase::removeDatabase("bookmarks_thread");

    if(!m_saveHistory)
      return;

    {
      QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "history_thread");

      db.setDatabaseName(dooble::s_homePath + QDir::separator() +
			 "history.db");

      if(db.open())
	{
	  int count = 1;
	  QSqlQuery query(db);

	  query.exec("CREATE TABLE IF NOT EXISTS history ("
		     "title TEXT, "
		     "url TEXT NOT NULL, "
		     "last_visited TEXT NOT NULL, "
		     "icon BLOB DEFAULT NULL, "
		     "visits TEXT NOT NULL, "
		     "temporary INTEGER NOT NULL, "
		     "description TEXT, "
		     "PRIMARY KEY (url, temporary))");

	  if(query.exec(QString("SELECT visits, url FROM history WHERE "
				"temporary = %1").arg(m_temporary)))
	    while(query.next())
	      {
		QUrl url
		  (QUrl::fromEncoded
		   (dmisc::decodedString
		    (QByteArray::fromBase64
		     (query.value(1).toByteArray())),
		    QUrl::StrictMode));

		if(url.toString(QUrl::StripTrailingSlash) !=
		   m_url.toString(QUrl::StripTrailingSlash))
		  continue;

		count = dmisc::decodedString
		  (QByteArray::fromBase64
		   (query.value(0).toByteArray())).toInt() + 1;

		QSqlQuery deleteQuery(db);

		deleteQuery.prepare("DELETE FROM history WHERE url = ? AND "
				    "temporary = ?");
		deleteQuery.bindValue(0, query.value(1));
		deleteQuery.bindValue(1, m_temporary);
		deleteQuery.exec();
		break;
	      }

	  query.prepare
	    ("INSERT OR REPLACE INTO history "
	     "(url, icon, last_visited, title, visits, temporary, "
	     "description) "
	     "VALUES (?, ?, ?, ?, ?, ?, ?)");
	  query.bindValue
	    (0,
	     dmisc::encodedString(m_url.
				  toEncoded(QUrl::StripTrailingSlash),
				  true).toBase64());
	  query.bindValue
	    (1,
	     dmisc::encodedString(m_bytes,
				  true).toBase64());
	  query.bindValue
	    (2,
	     dmisc::encodedString(QDateTime::currentDateTime().
				  toString(Qt::ISODate).
				  toUtf8(), true).toBase64());

	  if(m_title.trimmed().isEmpty())
	    query.bindValue
	      (3,
	       dmisc::encodedString(m_url.
				    toEncoded(QUrl::StripTrailingSlash),
				    true).toBase64());
	  else
	    query.bindValue
	      (3,
	       dmisc::encodedString(m_title.toUtf8(), true).toBase64());

	  query.bindValue
	    (4,
	     dmisc::encodedString(QString::number(count).toAscii(),
				  true).toBase64());
	  query.bindValue(5, m_temporary);
	  query.bindValue
	    (6,
	     dmisc::encodedString(m_userData.toString().toUtf8(),
				  true).toBase64());
	  query.exec();
	}

      db.close();
    }

    QSqlDatabase::removeDatabase("history_thread");
  }

 private:
  int m_temporary;
  bool m_saveHistory;
  QUrl m_url;
  QString m_title;
  QVariant m_userData;
  QByteArray m_bytes;
};

#endif
