#include "dcipfiltercontrol.h"

#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QHash>
#include <QHashIterator>
#include <QMenu>
#include <QAction>
#include <QIcon>

#include <QFileDialog>

#include "dcmenuhandler.h"
#include "dciconloader.h"

DCIPFilterControl::DCIPFilterControl(QWidget *parent): QDialog(parent)
{
    setupUi(this);

    InitDocument();
}

DCIPFilterControl::~DCIPFilterControl()
{
    clearTree();
}

void DCIPFilterControl::InitDocument()
{
    if (g_pConfig->GetIPFilterEnabled()){
	checkBox_ENABLE->setChecked(true);
    }
    else
	checkBox_ENABLE->setChecked(false);


    slotCheckBoxClick();

    connect(checkBox_ENABLE, SIGNAL(clicked()), this, SLOT(slotCheckBoxClick()));
    connect(radioButton_ALLOW, SIGNAL(toggled(bool)), this, SLOT(slotPolicyChanged_ALLOW(bool)));
    connect(radioButton_BLOCK, SIGNAL(toggled(bool)), this, SLOT(slotPolicyChanged_BLOCK(bool)));

    connect(pushButton_ALLOWIIP, SIGNAL(clicked()), this, SLOT(slotFastRulesAllowIIP()));
    connect(pushButton_DENYIIP, SIGNAL(clicked()), this, SLOT(slotFastRulesDenyIIP()));
    connect(pushButton_DENYLIP, SIGNAL(clicked()), this, SLOT(slotFastRulesDenyLIP()));

    connect(pushButton_EXPORT, SIGNAL(clicked()), this, SLOT(slotExport()));
    connect(pushButton_IMPORT, SIGNAL(clicked()), this, SLOT(slotImport()));

    connect(pushButton_ADDRULE, SIGNAL(clicked()), this, SLOT(slotAddRule()));

    connect(treeWidget_RULES, SIGNAL(itemClicked(QTreeWidgetItem*,int)),
	    this, SLOT(slotTreeClicked(QTreeWidgetItem*,int)));
    connect(treeWidget_RULES, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
	    this, SLOT(slotTreeItemChanged(QTreeWidgetItem*,int)));

    rfc1918 << "^10.(\\d{1,3}.){2,2}.\\d{1,3}:\\d{1,5}$"  << "^172.16.\\d{1,3}.\\d{1,3}:\\d{1,5}$";
    rfc1918 << "^192.168.\\d{1,3}.\\d{1,3}:\\d{1,5}$";
    rfc1918 << "^127.(\\d{1,3}.){2,2}.\\d{1,3}:\\d{1,5}$"; // not rfc1918 but local
}

void DCIPFilterControl::slotCheckBoxClick()
{
    bool b = checkBox_ENABLE->isChecked();
    bool loaded = false;

    frame_POLICY->setEnabled(b);
    groupBox->setEnabled(b);
    treeWidget_RULES->setEnabled(b);
    lineEdit_RULE->setEnabled(b);
    pushButton_ADDRULE->setEnabled(b);
    groupBox_FASTRULES->setEnabled(b);
    pushButton_EXPORT->setEnabled(b);
    pushButton_IMPORT->setEnabled(b);

    if (b){
	if (!g_pIPFilter)
	    new DCIPFilter();
	else{
	    loadItems();
	    loaded = true;
	}

	connect(g_pIPFilter, SIGNAL(ruleAdded(QString,eDIRECTION)),
		this, SLOT(slotRuleAdded(QString,eDIRECTION)));
	connect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
		this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	connect(g_pIPFilter, SIGNAL(ruleRemoved(QString,eDIRECTION)),
		this, SLOT(slotRuleRemoved(QString,eDIRECTION)));

	if (!loaded)
	    g_pIPFilter->loadList();

	if (g_pIPFilter->getRole() == eMATCH){
	    radioButton_ALLOW->setChecked(true);;
	}
	else{
	    radioButton_BLOCK->setChecked(true);
	}
    }
    else{
	if (g_pIPFilter){
	    g_pIPFilter->saveList();

	    disconnect(g_pIPFilter, SIGNAL(ruleAdded(QString,eDIRECTION)),
			this, SLOT(slotRuleAdded(QString,eDIRECTION)));
	    disconnect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
		       this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	    disconnect(g_pIPFilter, SIGNAL(ruleRemoved(QString,eDIRECTION)),
		       this, SLOT(slotRuleRemoved(QString,eDIRECTION)));

	    delete g_pIPFilter;

	    clearTree();
	}
    }

    g_pConfig->SetIPFilterEnabled(b);

}

void DCIPFilterControl::slotRuleAdded(QString exp, eDIRECTION direction)
{
    QTreeWidgetItem *it = new QTreeWidgetItem(treeWidget_RULES);

    it->setText(0, exp);
    it->setFlags(Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsEnabled);

    QString type = "OUT";

    switch (direction){
	case eDIRECTION_BOTH:
	    type = "BOTH";

	    break;
	case eDIRECTION_IN:
	    type = "IN";

	    break;
	default:
	    break;
    }

    it->setText(1, type);
}

void DCIPFilterControl::clearTree()
{
    QTreeWidgetItemIterator it(treeWidget_RULES, QTreeWidgetItemIterator::NotHidden);

    while(*it){
	if (*it)
	    delete *it;
	++it;
    }

    treeWidget_RULES->clear();
}

void DCIPFilterControl::loadItems()
{
    if (!g_pIPFilter)
	return;

    clearTree();

    QHash<QString, eDIRECTION> list;

    g_pIPFilter->getRules(list);

    if (list.empty())
	return;

    QHashIterator<QString, eDIRECTION> i(list);

    while (i.hasNext()){
	i.next();

	QTreeWidgetItem *it = new QTreeWidgetItem(treeWidget_RULES);

	it->setText(0, i.key());
	it->setFlags(Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsEnabled);

	QString type = "OUT";

	switch (i.value()){
	    case eDIRECTION_BOTH:
		type = "BOTH";

		break;
	    case eDIRECTION_IN:
		type = "IN";

		break;
	    default:
		break;
	}

	it->setText(1, type);
    }
}

void DCIPFilterControl::slotPolicyChanged_ALLOW(bool checked)
{
    if (!g_pIPFilter)
	return;

    changePolicy(radioButton_ALLOW, checked);
}

void DCIPFilterControl::slotPolicyChanged_BLOCK(bool checked)
{
    if (!g_pIPFilter)
	return;

    changePolicy(radioButton_BLOCK, checked);
}

void DCIPFilterControl::changePolicy(QRadioButton *btn, bool checked)
{
    if (btn == radioButton_ALLOW){
	if (checked)
	    g_pIPFilter->setRole(eMATCH);

	radioButton_BLOCK->setChecked(!checked);
    }

    else if (btn == radioButton_BLOCK){
	if (checked)
	    g_pIPFilter->setRole(eNOMATCH);

	radioButton_ALLOW->setChecked(!checked);
    }	
}

void DCIPFilterControl::slotTreeClicked(QTreeWidgetItem *item, int column)
{
    if (!item || column < 0 || !g_pIPFilter)
	return;

    old_exp = "";
    old_dir = "";

    QMenu *m = new QMenu(this);
    QPoint p = m->mapFromGlobal(QCursor::pos());

    QAction *remove = DCMenuHandler::addAction(m, emiREMOVE, true, tr("Remove item"));
    QAction *change = DCMenuHandler::addAction(m, emiEDIT, true, tr("Edit item"));
    QMenu   *ch_direction = new QMenu(tr("Change direction"), this);
    ch_direction->setIcon(QIcon(g_pIconLoader->GetPixmap(eiRELOAD)));

    QAction *in	    = ch_direction->addAction(QIcon(), tr("IN"));
    QAction *out    = ch_direction->addAction(QIcon(), tr("OUT"));
    QAction *both   = ch_direction->addAction(QIcon(), tr("BOTH"));

    m->addMenu(ch_direction);

    QAction *result = NULL;

    m->move(p);

    result = m->exec();

    if (!result)
	return;

    if (result == remove){
	treeWidget_RULES->removeItemWidget(item, 0);
	treeWidget_RULES->removeItemWidget(item, 1);

	treeWidget_RULES->repaint();

	QString exp = item->text(0);

	//disconnect(g_pIPFilter, SIGNAL(ruleRemoved(QString,eDIRECTION)),
	//	   this, SLOT(slotRuleRemoved(QString,eDIRECTION)));
	g_pIPFilter->remFromRules(exp);
	//connect(g_pIPFilter, SIGNAL(ruleRemoved(QString,eDIRECTION)),
	//	this, SLOT(slotRuleRemoved(QString,eDIRECTION)));

	//delete item;
    }
    else if (result == change){
	old_exp = item->text(0);
	old_dir = item->text(1);

	treeWidget_RULES->setEditTriggers(QAbstractItemView::SelectedClicked);
	treeWidget_RULES->editItem(item, column);
	treeWidget_RULES->setEditTriggers(QAbstractItemView::NoEditTriggers);
    }
    else if (result == in){
	//disconnect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	       this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	g_pIPFilter->changeRuleDirection(item->text(0), eDIRECTION_IN);
	//connect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	//item->setText(1, "IN");
    }
    else if (result == out){
	//disconnect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	       this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	g_pIPFilter->changeRuleDirection(item->text(0), eDIRECTION_OUT);
	//connect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	//item->setText(1, "OUT");
    }
    else if (result == both){
	//disconnect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	       this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	g_pIPFilter->changeRuleDirection(item->text(0), eDIRECTION_BOTH);
	//connect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
	//	this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
	//item->setText(1, "BOTH");
    }

    delete m;
}

void DCIPFilterControl::slotTreeItemChanged(QTreeWidgetItem *item, int column)
{
    if (!item || column < 0 || old_exp == "" || !g_pIPFilter)
	return;

    if (item->text(0).trimmed() == "" && item->text(1).trimmed() == ""){
	item->setText(0, old_exp);
	item->setText(1, old_dir);

	old_dir = "";
	old_exp = "";

	return;
    }

    eDIRECTION direction = eDIRECTION_OUT;

    if (item->text(1) == "IN")
	direction = eDIRECTION_IN;
    else if (item->text(1) == "BOTH")
	direction = eDIRECTION_BOTH;

    if (column == 0){
	g_pIPFilter->remFromRules(old_exp);

	//Just add new rule to IPFilter, item already exists in treewidget
	disconnect(g_pIPFilter, SIGNAL(ruleAdded(QString,eDIRECTION)),
		    this, SLOT(slotRuleAdded(QString,eDIRECTION)));

	g_pIPFilter->addToRules(item->text(0), direction);

	connect(g_pIPFilter, SIGNAL(ruleAdded(QString,eDIRECTION)),
		this, SLOT(slotRuleAdded(QString,eDIRECTION)));

    }
    else if (column == 1){
	//Just change direction in IPFilter, text in treeWidget already updated
	disconnect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
		       this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));

	g_pIPFilter->changeRuleDirection(old_exp, direction);

	connect(g_pIPFilter, SIGNAL(ruleChanged(QString,eDIRECTION,eDIRECTION)),
		this, SLOT(slotRuleChanged(QString,eDIRECTION,eDIRECTION)));
    }

    old_exp = "";
    old_dir = "";
}

void DCIPFilterControl::closeEvent(QCloseEvent *e)
{
    if (g_pIPFilter){
	g_pIPFilter->saveList();
    }

    e->accept();
}

void DCIPFilterControl::slotAddRule()
{
    if (!g_pIPFilter)
	return;

    if (lineEdit_RULE->text().length() > 2){
	eDIRECTION direction = eDIRECTION_OUT;

	if (radioButton_BOTH->isChecked())
	    direction = eDIRECTION_BOTH;
	else if (radioButton_IN->isChecked())
	    direction = eDIRECTION_IN;

	g_pIPFilter->addToRules(lineEdit_RULE->text(), direction);
    }

    lineEdit_RULE->setText("");
    lineEdit_RULE->setFocus();
}

void DCIPFilterControl::fastLocalRule(eREGEXP_ROLE role){
    if (!g_pIPFilter)
	return;

    if (role != g_pIPFilter->getRole()){
	g_pIPFilter->clearRules();
    }

    for (int i = 0; i < rfc1918.size(); i++)
	g_pIPFilter->addToRules(rfc1918.at(i), eDIRECTION_BOTH);

    if (role == eMATCH)
	radioButton_ALLOW->setChecked(true);
    else
	radioButton_BLOCK->setChecked(true);
}

void DCIPFilterControl::fastInternetRule(eREGEXP_ROLE role)
{
    if (!g_pIPFilter)
	return;

    if (role == eMATCH){
	g_pIPFilter->clearRules();

	g_pIPFilter->addToRules("^(\\d{1,3}.){3,3}\\d{1,3}:\\d{1,5}$", eDIRECTION_BOTH);

	radioButton_ALLOW->setChecked(true);
    }
    else
	fastLocalRule(eMATCH);
}

void DCIPFilterControl::slotFastRulesAllowIIP()
{
    fastInternetRule(eMATCH);
}

void DCIPFilterControl::slotFastRulesDenyIIP()
{
    fastLocalRule(eMATCH);
}

void DCIPFilterControl::slotFastRulesDenyLIP()
{
    fastLocalRule(eNOMATCH);
}

void DCIPFilterControl::slotRuleChanged(QString rule, eDIRECTION from, eDIRECTION to)
{
    if (!g_pIPFilter)
	return;

    QString type = "OUT";

    switch (to){
	case eDIRECTION_BOTH:
	    type = "BOTH";

	    break;
	case eDIRECTION_IN:
	    type = "IN";

	    break;
	default:
	    break;
    }

    QTreeWidgetItemIterator it(treeWidget_RULES, QTreeWidgetItemIterator::NotHidden);

    while(*it){
	if ((*it) && (*it)->text(0) == rule){
	    (*it)->setText(1, type);

	    break;
	}
    }

}

void DCIPFilterControl::slotRuleRemoved(QString rule, eDIRECTION)
{
    QTreeWidgetItemIterator it(treeWidget_RULES, QTreeWidgetItemIterator::NotHidden);

    while(*it){
	if ((*it) && (*it)->text(0) == rule){
	    treeWidget_RULES->removeItemWidget((*it), 0);
	    treeWidget_RULES->removeItemWidget((*it), 1);

	    treeWidget_RULES->repaint();

	    delete (*it);

	    break;
	}

	++it;
    }
}

void DCIPFilterControl::slotImport()
{
    if (!g_pIPFilter)
	return;

    QString fname = QFileDialog::getOpenFileName(this, tr("Import list"), QDir::homePath(),
						 tr("All Files (*)"));

    if (fname != "")
	g_pIPFilter->importFrom(fname);

    if (g_pIPFilter->getRole() == eMATCH){
	radioButton_ALLOW->setChecked(true);;
    }
    else{
	radioButton_BLOCK->setChecked(true);
    }
}

void DCIPFilterControl::slotExport()
{
    if (!g_pIPFilter)
	return;

    QString fname = QFileDialog::getSaveFileName(this, tr("Export list"), QDir::homePath(),
						 tr("All Files (*)"));

    if (fname != "")
	g_pIPFilter->exportTo(fname);
}

