/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011 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 <QUrl>
#include <QIcon>
#include <QBuffer>
#include <QSettings>
#include <QScrollBar>
#include <QFileIconProvider>

#include "dooble.h"
#include "dcookiewindow.h"

dcookiewindow::dcookiewindow(QWidget *parent):QMainWindow()
{
  Q_UNUSED(parent);
  ui.setupUi(this);
#ifdef Q_WS_MAC
  setAttribute(Qt::WA_MacMetalStyle, true);
#endif
  ui.deleteAllPushButton->setMenu(new QMenu(this));
  ui.deleteAllPushButton->menu()->addAction(tr("Include Preserved Sites"));
  ui.deleteAllPushButton->menu()->actions()[0]->setCheckable(true);
  ui.deleteAllPushButton->menu()->actions()[0]->setChecked
    (dooble::s_settings.value("cookiesWindow/deleteAllPreservedChecked",
			      false).toBool());
  connect(ui.deleteAllPushButton->menu()->actions()[0],
	  SIGNAL(toggled(bool)),
	  this,
	  SLOT(slotActionToggled(bool)));
  m_model = new QStandardItemModel(this);
  m_model->setSortRole(Qt::UserRole);

  QStringList list;

  list << tr("Site") << tr("Name") << tr("Path")
       << tr("Expiration Date") << tr("HTTP")
       << tr("Secure") << tr("Session Cookie")
       << tr("Value") << tr("Preserve");
  m_model->setHorizontalHeaderLabels(list);
  list.clear();
  ui.cookies->setModel(m_model);
  ui.cookies->setEditTriggers(QAbstractItemView::NoEditTriggers);
  ui.cookies->setSelectionMode(QAbstractItemView::SingleSelection);
  ui.cookies->setSortingEnabled(true);
  ui.cookies->setAlternatingRowColors(true);
  ui.cookies->header()->setSortIndicator(0, Qt::AscendingOrder);
  ui.cookies->header()->setSortIndicatorShown(true);
  ui.cookies->header()->setStretchLastSection(true);

  for(int i = 0; i < ui.cookies->header()->count(); i++)
    ui.cookies->resizeColumnToContents(i);

  ui.cookies->header()->setResizeMode(QHeaderView::Interactive);
  ui.searchLineEdit->setPlaceholderText(tr("Search Sites"));
  connect(ui.cookies->header(),
	  SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
	  this,
	  SLOT(slotSort(int, Qt::SortOrder)));
  connect(ui.deletePushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(slotDeleteCookie(void)));
  connect(ui.deleteAllPushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(slotDeleteAll(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.searchLineEdit,
	  SLOT(slotSetIcons(void)));
  connect(ui.searchLineEdit, SIGNAL(textEdited(const QString &)),
	  this, SLOT(slotTextChanged(const QString &)));
  connect(ui.closePushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(close(void)));
  connect(m_model,
	  SIGNAL(itemChanged(QStandardItem *)),
	  this,
	  SLOT(slotCheckBoxItemChanged(QStandardItem *)));

  if(dooble::s_settings.contains("cookiesWindow/tableColumnsState"))
    if(!ui.cookies->header()->restoreState
       (dooble::s_settings.value
	("cookiesWindow/tableColumnsState", "").toByteArray()))
      {
	ui.cookies->header()->setSortIndicator(0, Qt::AscendingOrder);
	ui.cookies->header()->setSortIndicatorShown(true);
	ui.cookies->header()->setStretchLastSection(true);

	for(int i = 0; i < ui.cookies->header()->count(); i++)
	  ui.cookies->resizeColumnToContents(i);
      }

  ui.cookies->header()->setMovable(true);
  slotSetIcons();
}

dcookiewindow::~dcookiewindow()
{
  saveState();
}

void dcookiewindow::show(QWidget *parent)
{
  Q_UNUSED(parent);

  if(!isVisible())
   {
     /*
     ** Don't annoy the user.
     */

     if(dooble::s_settings.contains("cookiesWindow/geometry"))
       setGeometry(dooble::s_settings.value("cookiesWindow/geometry",
					    "").toRect());
   }

  QMainWindow::showNormal();
  raise();
  ui.cookies->setFocus();
}

void dcookiewindow::slotSort(int column, Qt::SortOrder order)
{
  m_model->sort(column, order);
}

void dcookiewindow::slotSetIcons(void)
{
  QSettings settings
    (dooble::s_settings.value("iconSet").toString(),
     QSettings::IniFormat);

  setWindowIcon
    (QIcon(settings.value("cookiesWindow/windowIcon").toString()));
  
  ui.closePushButton->setIcon
    (QIcon(settings.value("cancelButtonIcon").toString()));
  ui.deletePushButton->setIcon
    (QIcon(settings.value("cookiesWindow/deleteButtonIcon").toString()));
  ui.deleteAllPushButton->setIcon
    (QIcon(settings.value("cookiesWindow/deleteAllButtonIcon").toString()));
  emit iconsChanged();
}

void dcookiewindow::slotDeleteCookie(void)
{
  QModelIndexList list(ui.cookies->selectionModel()->selectedRows(1));

  if(!list.isEmpty())
    {
      QModelIndex index1(list.takeFirst());

      if(index1.parent().isValid())
	{
	  QModelIndex index2(m_model->index(index1.row(), 2,
					    index1.parent()));

	  dooble::s_cookies->removeCookie
	    (index1.data().toString(),          // Name
	     m_model->itemFromIndex(index1.parent())->
	     data(Qt::UserRole + 1).toString(), // Domain
	     index2.data().toString());         // Path
	  m_model->removeRows(index1.row(), 1, index1.parent());

	  if(m_model->rowCount(index1.parent()) == 0 &&
	     !dooble::s_cookies->
	     isFavorite(m_model->itemFromIndex(index1.parent())->
			data(Qt::UserRole + 1).toString()))
	    {
	      /*
	      ** Remove the domain if it does not have
	      ** children and the domain is not considered
	      ** a favorite
	      */

	      index1 = m_model->index(index1.parent().row(), 0);
	      dooble::s_cookies->removeCookie
		(m_model->itemFromIndex(index1)->data(Qt::UserRole + 1).
		 toString());
	      m_model->removeRows(index1.row(), 1);
	    }
	}
      else
	{
	  /*
	  ** Remove all the cookies for the selected domain.
	  */

	  index1 = m_model->index(index1.row(), 0);
	  dooble::s_cookies->removeCookie
	    (m_model->itemFromIndex(index1)->data(Qt::UserRole + 1).
	     toString());
	  m_model->removeRows(index1.row(), 1);
	}
    }

  slotTextChanged(ui.searchLineEdit->text());
}

void dcookiewindow::slotDeleteAll(void)
{
  bool deleteFavorites = dooble::s_settings.value
    ("cookiesWindow/deleteAllPreservedChecked", false).toBool();

  /*
  ** Remove only the current view's cookies.
  */

  for(int i = m_model->rowCount() - 1; i >= 0; i--)
    if(!ui.cookies->isRowHidden(i, ui.cookies->rootIndex()))
      if(!deleteFavorites && !dooble::s_cookies->
	 isFavorite(m_model->item(i)->data(Qt::UserRole + 1).toString()))
	{
	  dooble::s_cookies->removeCookie
	    (m_model->item(i)->data(Qt::UserRole + 1).toString());
	  m_model->removeRow(i);
	}

  slotTextChanged(ui.searchLineEdit->text());
}

void dcookiewindow::slotPopulate(const QList<QNetworkCookie> &list)
{
  QFileIconProvider icon;

  for(int i = 0; i < list.size(); i++)
    {
      int row = 0;
      QStandardItem *item1 = 0;
      QStandardItem *item2 = 0;
      QNetworkCookie cookie(list.at(i));
      QList<QStandardItem *> items;

      if(cookie.domain().isEmpty())
	continue;

      QByteArray a(cookie.name());

      a.append(cookie.domain());
      a.append(cookie.path());
      items = m_model->findItems(cookie.domain());

      if(!items.isEmpty())
	{
	  /*
	  ** Prevent duplicate entries. Just like in dcookies,
	  ** a cookie's uniqueness is defined by its name,
	  ** domain, and path.
	  */

	  bool found = false;

	  item1 = items.at(0);

	  if(dooble::s_cookies->
	     isFavorite(item1->data(Qt::UserRole + 1).toString()))
	    {
	      m_model->item(item1->row(), 8)->setData(true, Qt::UserRole);
	      m_model->item(item1->row(), 8)->setCheckState(Qt::Checked);
	    }
	  else
	    {
	      m_model->item(item1->row(), 8)->setData(false, Qt::UserRole);
	      m_model->item(item1->row(), 8)->setCheckState(Qt::Unchecked);
	    }

	  items.clear();

	  for(int j = 0; j < item1->rowCount(); j++)
	    if(item1->child(j, 1)->text() == cookie.name() &&
	       item1->child(j, 2)->text() == cookie.path())
	      {
		/*
		** Update fields.
		*/

		item1->child(j, 3)->setText
		  (cookie.expirationDate().toString("MM/dd/yyyy hh:mm:ss AP"));
		item1->child(j, 3)->setData
		  (cookie.expirationDate().toString("yyyy/MM/dd hh:mm:ss"),
		   Qt::UserRole);
		item1->child(j, 4)->setText
		  (QVariant(cookie.isHttpOnly()).toString());
		item1->child(j, 4)->setData
		  (item1->child(j, 4)->text(), Qt::UserRole);
		item1->child(j, 5)->setText
		  (QVariant(cookie.isSecure()).toString());
		item1->child(j, 5)->setData
		  (item1->child(j, 5)->text(), Qt::UserRole);
		item1->child(j, 6)->setText
		  (QVariant(cookie.isSessionCookie()).toString());
		item1->child(j, 6)->setData
		  (item1->child(j, 6)->text(), Qt::UserRole);
		item1->child(j, 7)->setText
		  (QVariant(cookie.value()).toString());
		item1->child(j, 7)->setData
		  (item1->child(j, 7)->text(), Qt::UserRole);
		found = true;
		break;
	      }

	  if(found)
	    continue;

	  item1->setRowCount(item1->rowCount() + 1);
	  row = item1->rowCount() - 1;
	}
      else
	{
	  m_model->setRowCount(m_model->rowCount() + 1);
	  row = m_model->rowCount() - 1;
	  item1 = new QStandardItem(icon.icon(QFileIconProvider::Folder),
				    cookie.domain());
	  item1->setData(cookie.domain(), Qt::UserRole + 1);
	  item1->setEditable(false);
	  item1->setData(item1->text(), Qt::UserRole);
	  m_model->setItem(row, 0, item1);
	  item2 = new QStandardItem();
	  item2->setData(item1->data(Qt::UserRole + 1), Qt::UserRole + 1);
	  item2->setEditable(false);
	  item2->setCheckable(true);
	  item2->setData(false, Qt::UserRole);
	  item2->setCheckState(Qt::Unchecked);

	  if(dooble::s_cookies->
	     isFavorite(item2->data(Qt::UserRole + 1).toString()))
	    {
	      item2->setData(true, Qt::UserRole);
	      item2->setCheckState(Qt::Checked);
	    }

	  m_model->setItem(row, 8, item2);

	  if(cookie.name().isEmpty())
	    /*
	    ** Single domain, most likely a favorite.
	    */

	    continue;

	  row = 0;
	}

      item1->setColumnCount(8);
      item2 = new QStandardItem();
      item2->setEditable(false);
      item1->setChild(row, 0, item2);
      item2 = new QStandardItem(QVariant(cookie.name()).toString());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 1, item2);
      item2 = new QStandardItem(cookie.path());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 2, item2);
      item2 = new QStandardItem
	(cookie.expirationDate().toString("MM/dd/yyyy hh:mm:ss AP"));
      item2->setEditable(false);
      item2->setData
	(cookie.expirationDate().toString("yyyy/MM/dd hh:mm:ss"),
	 Qt::UserRole);
      item1->setChild(row, 3, item2);
      item2 = new QStandardItem(QVariant(cookie.isHttpOnly()).toString());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 4, item2);
      item2 = new QStandardItem(QVariant(cookie.isSecure()).toString());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 5, item2);
      item2 = new QStandardItem
	(QVariant(cookie.isSessionCookie()).toString());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 6, item2);
      item2 = new QStandardItem(QVariant(cookie.value()).toString());
      item2->setEditable(false);
      item2->setData(item2->text(), Qt::UserRole);
      item1->setChild(row, 7, item2);
    }

  statusBar()->showMessage(QString(tr("%1 cookie(s) / %2 site(s)")).
			   arg(dooble::s_cookies->count()).
			   arg(m_model->rowCount()));
  m_model->sort
    (ui.cookies->header()->sortIndicatorSection(),
     ui.cookies->header()->sortIndicatorOrder());
}

void dcookiewindow::slotUpdateView(const QList<QNetworkCookie> &list,
				   const QUrl &url)
{
  QList<QStandardItem *> items(m_model->findItems(url.host()));

  if(items.at(0))
    {
      QList<QNetworkCookie> otherList;

      for(int i = 0; i < list.size(); i++)
	{
	  QNetworkCookie cookie;

	  cookie.setDomain(list.at(i).domain());
	  cookie.setName(list.at(i).name());
	  cookie.setPath(list.at(i).path());
	  otherList.append(cookie);
	}

      /*
      ** If the item's children (cookies) do not exist
      ** in the provided list of cookies, remove the
      ** children.
      */

      for(int i = items.at(0)->rowCount() - 1; i >= 0; i--)
	{
	  QNetworkCookie cookie;

	  cookie.setDomain
	    (items.at(0)->data(Qt::UserRole + 1).toString());
	  cookie.setName
	    (QVariant(items.at(0)->child(i, 1)->data(Qt::UserRole)).
	     toByteArray());
	  cookie.setPath
	    (items.at(0)->child(i, 2)->text());

	  if(!otherList.contains(cookie))
	    {
	      dooble::s_cookies->removeCookie(cookie);
	      items.at(0)->removeRow(i);
	    }
	}

      if(items.at(0)->rowCount() == 0)
	if(!dooble::s_cookies->
	   isFavorite(items.at(0)->data(Qt::UserRole + 1).toString()))
	  m_model->removeRows(items.at(0)->row(), 1);

      otherList.clear();
    }

  items.clear();
  slotTextChanged(ui.searchLineEdit->text());
}

void dcookiewindow::closeEvent(QCloseEvent *event)
{
  saveState();
  QMainWindow::closeEvent(event);
}

void dcookiewindow::slotTextChanged(const QString &text)
{
  int n = 0;
  int count = 0;

  for(int i = 0; i < m_model->rowCount(); i++)
    if(text.trimmed().isEmpty() ||
       m_model->item(i)->text().contains(text.trimmed()))
      {
	count += m_model->item(i)->rowCount();
	ui.cookies->setRowHidden(i, ui.cookies->rootIndex(), false);
      }
    else
      {
	n += 1;
	ui.cookies->setRowHidden(i, ui.cookies->rootIndex(), true);
      }

  statusBar()->showMessage(QString(tr("%1 cookie(s) / %2 site(s)")).
			   arg(count).
			   arg(m_model->rowCount() - n));
}

void dcookiewindow::saveState(void)
{
  dooble::s_settings["cookiesWindow/tableColumnsState"] =
    ui.cookies->header()->saveState();

  if(isVisible())
    dooble::s_settings["cookiesWindow/geometry"] = geometry();

  QSettings settings;

  settings.setValue("cookiesWindow/tableColumnsState",
		    ui.cookies->header()->saveState());

  if(isVisible())
    settings.setValue("cookiesWindow/geometry", geometry());
}

void dcookiewindow::slotDeleteDomain(void)
{
  QModelIndex index = ui.cookies->currentIndex();

  if(index.isValid())
    {
      QStandardItem *item = m_model->item(index.row(), 0);

      if(item)
	{
	  dooble::s_cookies->removeCookie
	    (m_model->itemFromIndex(index)->data(Qt::UserRole + 1).
	     toString());
	  m_model->removeRows(index.row(), 1);
	}
    }

  slotTextChanged(ui.searchLineEdit->text());
}

void dcookiewindow::slotCheckBoxItemChanged(QStandardItem *item)
{
  if(item && item->isCheckable())
    {
      if(item->checkState() == Qt::Checked)
	dooble::s_cookies->allowDomain
	  (item->data(Qt::UserRole + 1).toString(), true);
      else
	dooble::s_cookies->allowDomain
	  (item->data(Qt::UserRole + 1).toString(), false);
    }
}

void dcookiewindow::slotDomainsRemoved(const QStringList &list)
{
  for(int i = m_model->rowCount() - 1; i >= 0; i--)
    {
      QStandardItem *item = m_model->item(i, 0);

      if(item && list.contains(item->data(Qt::UserRole + 1).toString()))
	m_model->removeRow(i);
    }

  slotTextChanged(ui.searchLineEdit->text());
}

void dcookiewindow::slotActionToggled(bool checked)
{
  QSettings settings;

  settings.setValue("cookiesWindow/deleteAllPreservedChecked",
		    checked);
  dooble::s_settings["cookiesWindow/deleteAllPreservedChecked"] =
    checked;
}
