// -*- mode: c++; c-basic-offset: 4 -*-
/*
  Copyright (c) 2008 Laurent Montel <montel@kde.org>
  Copyright (C) 2006 Daniele Galdi <daniele.galdi@gmail.com>

  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; either version 2 of the License, or
  (at your option) any later version.

  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., 51 Franklin Street, Fifth Floor, Boston, MA
  02110-1301, USA.
*/

/* Project related */
#include "adblock.h"
#include "adblockdialog.h"

/* Kde related */
#include <kgenericfactory.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <klibloader.h>
#include <kparts/statusbarextension.h>
#include <khtml_part.h>
#include <khtml_settings.h>
#include <kstatusbar.h>
#include <kurllabel.h>
#include <kurl.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <kcmultidialog.h>
#include <klocale.h>
#include <KActionCollection>
#include <KActionMenu>
#include <KMenu>
#include <dom/html_document.h>
#include <dom/html_image.h>
#include <dom/html_inline.h>
#include <dom/html_misc.h>
#include <dom/html_element.h>
#include <dom/dom_doc.h>

using namespace DOM;

#include <qpixmap.h>
#include <qcursor.h>
#include <qregexp.h>


K_PLUGIN_FACTORY( AdBlockFactory, registerPlugin< AdBlock >(); )
K_EXPORT_PLUGIN( AdBlockFactory( "adblock" ) )

AdBlock::AdBlock(QObject *parent, const QVariantList & /*args*/) :
    Plugin(parent),
    m_label(0), m_menu(0)
{
    m_part = dynamic_cast<KHTMLPart *>(parent);
    if(!m_part)
    {
        kDebug() << "couldn't get KHTMLPart";
        return;
    }
    m_menu = new KActionMenu(KIcon( "preferences-web-browser-adblock" ), i18n("Adblock"),
                           actionCollection() );
    actionCollection()->addAction( "action adblock", m_menu );
     m_menu->setDelayed( false );

    QAction *a = actionCollection()->addAction(  "configure");
    a->setText(i18n("Configure..."));
    connect(a, SIGNAL(triggered()), this, SLOT(showKCModule()));
    m_menu->addAction(a);


    a = actionCollection()->addAction(  "show_elements");
    a->setText(i18n("Show Elements..."));
    connect(a, SIGNAL(triggered()), this, SLOT(slotConfigure()));
    m_menu->addAction(a);

    a = actionCollection()->addAction( "separator" );
    a->setSeparator( true );
    m_menu->addAction(a);


    a = actionCollection()->addAction(  "disable_for_this_page");
    a->setText(i18n("Disable for this page"));
    connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisPage()));
    m_menu->addAction(a);

    a = actionCollection()->addAction(  "disable_for_this_site");
    a->setText(i18n("Disable for this site"));
    connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisSite()));
    m_menu->addAction(a);

    connect(m_part, SIGNAL(completed()), this, SLOT(initLabel()));
}

AdBlock::~AdBlock()
{
    KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part);

    if (statusBarEx)
    {
        statusBarEx->removeStatusBarItem(m_label);
        delete m_label;
    }
    m_label = 0;
    delete m_menu;
    m_menu=0;
}

void AdBlock::initLabel()
{
    if (m_label) return;

    KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part);

    if (!statusBarEx) {
        kDebug() << "couldn't get KParts::StatusBarExtension";
        return;
    }

    m_label = new KUrlLabel(statusBarEx->statusBar());

    KIconLoader *loader = KIconLoader::global();

    m_label->setFixedHeight(loader->currentSize(KIconLoader::Small));
    m_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
    m_label->setUseCursor(false);
    m_label->setPixmap(loader->loadIcon("preferences-web-browser-adblock", KIconLoader::Small));

    statusBarEx->addStatusBarItem(m_label, 0, false);
    connect(m_label, SIGNAL(leftClickedUrl()), this, SLOT(slotConfigure()));
    connect(m_label, SIGNAL(rightClickedUrl()), this, SLOT(contextMenu()));
}


void AdBlock::slotDisableForThisPage()
{
    KHTMLSettings *settings = const_cast<KHTMLSettings *>(m_part->settings());
    settings->addAdFilter("@@"+m_part->toplevelURL().url());
}

void AdBlock::slotDisableForThisSite()
{
    KHTMLSettings *settings = const_cast<KHTMLSettings *>(m_part->settings());
    QString url = m_part->toplevelURL().url();
    url = url.remove( "http://" );
    url = url.section( '/', 0,0 );
    settings->addAdFilter("@@http://"+url);
}

void AdBlock::slotConfigure()
{
    if (!m_part->settings()->isAdFilterEnabled())
    {
	KMessageBox::error(0,
                           i18n("Please enable Konqueror's Adblock"),
                           i18n("Adblock disabled"));

	return;
    }

    AdElementList elements;
    fillBlockableElements(elements);

    AdBlockDlg *dlg = new AdBlockDlg(m_part->widget(), elements, m_part);
    connect(dlg, SIGNAL( notEmptyFilter(const QString&) ), this, SLOT( addAdFilter(const QString&) ));
    dlg->exec();
    delete dlg;
}

void AdBlock::showKCModule()
{
    KCMultiDialog* dialogue = new KCMultiDialog(m_part->widget());
    dialogue->addModule("khtml_filter");
    connect(dialogue, SIGNAL( cancelClicked() ), dialogue, SLOT( delayedDestruct() ));
    connect(dialogue, SIGNAL( closeClicked() ), dialogue, SLOT( delayedDestruct() ));
    dialogue->show();
}

void AdBlock::contextMenu()
{
    m_menu->menu()->exec(QCursor::pos());
}

void AdBlock::fillBlockableElements(AdElementList &elements)
{
    fillWithHtmlTag(elements, "script", "src", i18n( "script" ));
    fillWithHtmlTag(elements, "embed" , "src", i18n( "object" ));
    fillWithHtmlTag(elements, "object", "src", i18n( "object" ));
    fillWithImages(elements);

    const KHTMLSettings *settings = m_part->settings();

    AdElementList::iterator it;
    for ( it = elements.begin(); it != elements.end(); ++it )
    {
	AdElement &element = (*it);
        if (settings->isAdFiltered( element.url() ))
	{
	    element.setBlocked(true);
	}
    }
}

void AdBlock::fillWithImages(AdElementList &elements)
{
    HTMLDocument htmlDoc = m_part->htmlDocument();

    HTMLCollection images = htmlDoc.images();

    for (unsigned int i = 0; i < images.length(); i++)
    {
	HTMLImageElement image = static_cast<HTMLImageElement>( images.item(i) );

	DOMString src = image.src();

	QString url = htmlDoc.completeURL(src).string();
	if (!url.isEmpty() && url != m_part->baseURL().url())
	{
	    AdElement element(url, i18n( "image" ), "IMG", false, image);
	    if (!elements.contains( element ))
		elements.append( element);
	}
    }
}

void AdBlock::fillWithHtmlTag(AdElementList &elements,
			      const DOMString &tagName,
			      const DOMString &attrName,
			      const QString &category)
{
    Document doc = m_part->document();

    NodeList nodes = doc.getElementsByTagName(tagName);

    for (unsigned int i = 0; i < nodes.length(); i++)
    {
	Node node = nodes.item(i);
	Node attr = node.attributes().getNamedItem(attrName);

	DOMString src = attr.nodeValue();
	if (src.isNull()) continue;

	QString url = doc.completeURL(src).string();
	if (!url.isEmpty() && url != m_part->baseURL().url())
	{
	    AdElement element(url, tagName.string(), category, false, attr);
	    if (!elements.contains( element ))
		elements.append( element);
	}
    }
}

void AdBlock::addAdFilter(const QString &url)
{
    //FIXME hackish
    KHTMLSettings *settings = const_cast<KHTMLSettings *>(m_part->settings());
    settings->addAdFilter(url);
}

// ----------------------------------------------------------------------------

AdElement::AdElement() :
  m_url(0), m_category(0), m_type(0), m_blocked(false) {}

AdElement::AdElement(const QString &url, const QString &category,
		     const QString &type, bool blocked, const DOM::Node&node) :
  m_url(url), m_category(category), m_type(type), m_blocked(blocked),m_node( node ) {}

AdElement &AdElement::operator=(const AdElement &obj)
{
  m_blocked = obj.m_blocked;
  m_url = obj.m_url;
  m_category = obj.m_category;
  m_type = obj.m_type;
  m_node = obj.m_node;

  return *this;
}

bool AdElement::operator==(const AdElement &obj)
{
    return m_url == obj.m_url;
}

bool AdElement::isBlocked() const
{
  return m_blocked;
}

void AdElement::setBlocked(bool blocked)
{
    m_blocked = blocked;
}

QString AdElement::url() const
{
  return m_url;
}

QString AdElement::category() const
{
  return m_category;
}

QString AdElement::type() const
{
  return m_type;
}

DOM::Node AdElement::node() const
{
    return m_node;
}

#include "adblock.moc"
