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

#include "dmisc.h"
#include "dooble.h"
#include "dexceptionswindow.h"

dexceptionswindow::dexceptionswindow(dexceptionsmodel *model):QMainWindow()
{
  setObjectName(model->objectName());
  ui.setupUi(this);
  ui.tableView->setModel(model);
  ui.tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
  ui.tableView->setSortingEnabled(true);
  ui.tableView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
  ui.tableView->horizontalHeader()->setSortIndicatorShown(true);
  ui.tableView->horizontalHeader()->setStretchLastSection(true);

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

#ifdef Q_WS_MAC
  setAttribute(Qt::WA_MacMetalStyle, false);
  statusBar()->setSizeGripEnabled(false);
#endif
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.searchLineEdit,
	  SLOT(slotSetIcons(void)));
  connect(ui.tableView->horizontalHeader(),
	  SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
	  this,
	  SLOT(slotSort(int, Qt::SortOrder)));
  connect(ui.lineEdit, SIGNAL(returnPressed(void)), this,
	  SLOT(slotAllow(void)));
  connect(ui.allowPushButton, SIGNAL(clicked(void)), this,
	  SLOT(slotAllow(void)));
  connect(ui.closePushButton, SIGNAL(clicked(void)), this,
	  SLOT(slotClose(void)));
  connect(ui.deletePushButton, SIGNAL(clicked(void)), this,
	  SLOT(slotDelete(void)));
  connect(ui.deleteAllPushButton, SIGNAL(clicked(void)), this,
	  SLOT(slotDeleteAll(void)));
  connect(ui.tableView->selectionModel(),
	  SIGNAL(selectionChanged(const QItemSelection &,
				  const QItemSelection &)),
	  this,
	  SLOT(slotItemsSelected(const QItemSelection &,
				 const QItemSelection &)));
  connect(ui.searchLineEdit, SIGNAL(textEdited(const QString &)),
	  this, SLOT(slotTextChanged(const QString &)));
  slotSetIcons();

  if(dooble::s_settings.contains(QString("%1/tableColumnsState").
				 arg(objectName())))
    if(!ui.tableView->horizontalHeader()->restoreState
       (dooble::s_settings.value(QString("%1/tableColumnsState").
				 arg(objectName()),
				 "").toByteArray()))
      {
	ui.tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
	ui.tableView->horizontalHeader()->setSortIndicator
	  (0, Qt::AscendingOrder);
	ui.tableView->horizontalHeader()->setSortIndicatorShown(true);
	ui.tableView->horizontalHeader()->setStretchLastSection(true);
	ui.tableView->horizontalHeader()->setResizeMode
	  (QHeaderView::Interactive);
      }
}

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

void dexceptionswindow::populate(void)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      model->populate();
      slotSort(ui.tableView->horizontalHeader()->sortIndicatorSection(),
	       ui.tableView->horizontalHeader()->sortIndicatorOrder());
      slotTextChanged(ui.searchLineEdit->text().trimmed());
    }
}

void dexceptionswindow::slotAllow(void)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      QUrl url(QUrl::fromUserInput(ui.lineEdit->text().trimmed()));

      if(!url.isEmpty() && url.isValid())
	if(model->allow(url.host()))
	  {
	    ui.lineEdit->clear();
	    slotSort(ui.tableView->horizontalHeader()->sortIndicatorSection(),
		     ui.tableView->horizontalHeader()->sortIndicatorOrder());
	    slotTextChanged(ui.searchLineEdit->text().trimmed());
	  }
    }
}

void dexceptionswindow::slotClose(void)
{
  close();
}

void dexceptionswindow::slotDelete(void)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      QModelIndexList list;

      for(int i = 0; i < model->rowCount(); i++)
	if(!ui.tableView->isRowHidden(i) &&
	   ui.tableView->selectionModel()->isRowSelected(i, ui.tableView->
							 rootIndex()))
	  list.append(model->index(i, 0));

      model->deleteList(list);
      slotTextChanged(ui.searchLineEdit->text().trimmed());

      if(!list.isEmpty())
	{
	  if(list.first().row() >= model->rowCount())
	    ui.tableView->selectRow(model->rowCount() - 1);
	  else
	    ui.tableView->selectRow(list.first().row());
	}
    }
}

void dexceptionswindow::slotDeleteAll(void)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      QModelIndexList list;

      for(int i = 0; i < model->rowCount(); i++)
	if(!ui.tableView->isRowHidden(i))
	  list.append(model->index(i, 0));

      model->deleteList(list);
      slotTextChanged(ui.searchLineEdit->text().trimmed());
    }
}

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

void dexceptionswindow::slotShow(void)
{
  QToolButton *toolButton = qobject_cast<QToolButton *> (sender());

  /*
  ** dooble::slotExceptionRaised() connects a toolbutton's clicked()
  ** signal to this slot.
  */

  if(toolButton && this != toolButton->parent())
    toolButton->disconnect(this);

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

     if(dooble::s_settings.contains(QString("%1/geometry").arg(objectName())))
       {
	 if(dmisc::isGnome())
	   setGeometry(dooble::s_settings.value(QString("%1/geometry").
						arg(objectName()),
						QRect(100, 100, 800, 600)).
		       toRect());
	 else
	   {
	     QByteArray g(dooble::s_settings.value(QString("%1/geometry").
						   arg(objectName())).
			  toByteArray());

	     if(!restoreGeometry(g))
	       setGeometry(100, 100, 800, 600);
	   }
       }
     else
       setGeometry(100, 100, 800, 600);
   }

  showNormal();
  raise();
}

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

  setWindowIcon
    (QIcon(settings.value("exceptionsWindow/windowIcon").toString()));
  ui.closePushButton->setIcon
    (QIcon(settings.value("cancelButtonIcon").toString()));
  ui.deletePushButton->setIcon
    (QIcon(settings.value("exceptionsWindow/deleteButtonIcon").toString()));
  ui.deleteAllPushButton->setIcon
    (QIcon(settings.value("exceptionsWindow/deleteAllButtonIcon").toString()));
  ui.allowPushButton->setIcon
    (QIcon(settings.value("exceptionsWindow/allowButtonIcon").
	   toString()));
}

void dexceptionswindow::saveState(void)
{
  /*
  ** geometry() may return (0, 0) coordinates if the window is
  ** not visible.
  */

  if(isVisible())
    {
      if(dmisc::isGnome())
	dooble::s_settings[QString("%1/geometry").arg(objectName())] =
	  geometry();
      else
	dooble::s_settings[QString("%1/geometry").arg(objectName())] =
	  saveGeometry();
    }

  dooble::s_settings[QString("%1/tableColumnsState").arg(objectName())] =
    ui.tableView->horizontalHeader()->saveState();

  QSettings settings;

  if(isVisible())
    {
      if(dmisc::isGnome())
	settings.setValue(QString("%1/geometry").arg(objectName()),
			  geometry());
      else
	settings.setValue(QString("%1/geometry").arg(objectName()),
			  saveGeometry());
    }

  settings.setValue
    (QString("%1/tableColumnsState").arg(objectName()),
     ui.tableView->horizontalHeader()->saveState());
}

void dexceptionswindow::keyPressEvent(QKeyEvent *event)
{
  if(event)
    {
      if(event->key() == Qt::Key_Escape)
	close();
      else if(event->key() == Qt::Key_Delete)
	slotDelete();
      else if(event->key() == Qt::Key_F &&
	      event->modifiers() == Qt::ControlModifier)
	{
	  ui.searchLineEdit->setFocus();
	  ui.searchLineEdit->selectAll();
	}
      else if(event->key() == Qt::Key_L &&
	      event->modifiers() == Qt::ControlModifier)
	{
	  ui.lineEdit->setFocus();
	  ui.lineEdit->selectAll();
	}
    }

  QMainWindow::keyPressEvent(event);
}

bool dexceptionswindow::allowed(const QString &host) const
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    return model->allowed(host);
  else
    return false;
}

void dexceptionswindow::slotAdd(const QString &host,
				const QUrl &url,
				const QDateTime &dateTime)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      model->add(host, url, dateTime);
      slotSort(ui.tableView->horizontalHeader()->sortIndicatorSection(),
	       ui.tableView->horizontalHeader()->sortIndicatorOrder());
      slotTextChanged(ui.searchLineEdit->text().trimmed());
    }
}

void dexceptionswindow::slotItemsSelected(const QItemSelection &selected,
					  const QItemSelection &deselected)
{
  Q_UNUSED(selected);
  Q_UNUSED(deselected);
  slotTextChanged(ui.searchLineEdit->text().trimmed());
}

void dexceptionswindow::slotSort(int column, Qt::SortOrder order)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    model->sort(column, order);
}

void dexceptionswindow::reencode(QProgressBar *progress)
{
  if(!dmisc::passphraseWasAuthenticated())
    return;

  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    model->reencode(progress);
}

bool dexceptionswindow::contains(const QString &host) const
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    return model->contains(host);
  else
    return false;
}

void dexceptionswindow::slotTextChanged(const QString &text)
{
  /*
  ** Search text changed.
  */

  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(!model)
    {
      statusBar()->showMessage(QString(tr("%1 Item(s) / %2 Item(s) Selected")).
			       arg(0).arg(0));
      return;
    }

  int count = 0;
  int selected = 0;

  for(int i = 0; i < model->rowCount(); i++)
    if((model->item(i, 0) &&
	model->item(i, 0)->text().toLower().
	contains(text.toLower().trimmed())) ||
       (model->item(i, 1) &&
	model->item(i, 1)->text().toLower().
	contains(text.toLower().trimmed())) ||
       (model->item(i, 2) &&
	model->item(i, 2)->text().toLower().
	contains(text.toLower().trimmed())))
      {
	count += 1;
	ui.tableView->setRowHidden(i, false);

	if(ui.tableView->selectionModel()->
	   isRowSelected(i, ui.tableView->rootIndex()))
	  selected += 1;
      }
    else
      ui.tableView->setRowHidden(i, true);

  statusBar()->showMessage(QString(tr("%1 Item(s) / %2 Item(s) Selected")).
			   arg(count).arg(selected));
}

void dexceptionswindow::clear(void)
{
  dexceptionsmodel *model = qobject_cast<dexceptionsmodel *>
    (ui.tableView->model());

  if(model)
    {
      QModelIndexList list;

      for(int i = 0; i < model->rowCount(); i++)
	list.append(model->index(i, 0));

      model->deleteList(list);
      ui.tableView->selectionModel()->clear();
      statusBar()->showMessage(QString(tr("%1 Item(s) / %2 Item(s) Selected")).
			       arg(model->rowCount()).arg(0));
    }
}
