#include "CFireFlierClientApp.hh"
#include "CDlgIPTablesRules.hh"
#include "fireflierIO.h"
#include <glibmm/arrayhandle.h>
#include <algorithm>
#if FF_DEBUG
#include <iostream>
#endif
using namespace std;


// definition of static member
const CDlgIPTablesRules::CIPTablesRulesColumnRecord CDlgIPTablesRules::m_colRecord;


CDlgIPTablesRules::CDlgIPTablesRules()
	: Gtk::Window()
	, m_butDel(Gtk::Stock::DELETE)
{
	initGUI();

	ffIO.networkThread.signal_IPTablesRules_avail().connect(m_signalIPTablesRulesAvail.slot(true));
	ffIO.networkThread.signal_Authentication().connect(m_signalAuthentication.slot());
	ffIO.networkThread.signal_error().connect(m_signalNetworkThreadError.slot());

	m_signalIPTablesRulesAvail.connect(SigC::slot(*this, &CDlgIPTablesRules::on_IPTablesRules_avail));
	m_signalAuthentication.connect(SigC::slot(*this, &CDlgIPTablesRules::on_Authentication));
	m_signalNetworkThreadError.connect(SigC::slot(*this, &CDlgIPTablesRules::on_networkthread_error));
}


CDlgIPTablesRules::~CDlgIPTablesRules()
{
#ifdef WITHGCONF
	if (m_gconfUI[strGConfUIIPTablesRulesDlg_remember].get_bool())
	{
		if (is_visible())
			save_pos_and_size();
		m_gconfUI.write();
	}
#endif
}

void CDlgIPTablesRules::initGUI()
{
	Gtk::VBox* vboxWhole = manage(new Gtk::VBox);
	Gtk::HBox* hboxDlgAction = manage(new Gtk::HBox(true, 5));
	hboxDlgAction->set_border_width(5);

	Gtk::ScrolledWindow* scrwnd = manage(new Gtk::ScrolledWindow);
	scrwnd->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrwnd->add(m_tvIPTablesRules);
	
	Gtk::Button* butCancel = manage(new Gtk::Button(Gtk::Stock::CLOSE));
	
	m_butDel.set_sensitive(false);
	hboxDlgAction->pack_start(m_butDel, Gtk::PACK_EXPAND_WIDGET, 2);
	hboxDlgAction->pack_start(*butCancel, Gtk::PACK_EXPAND_WIDGET, 2);
	vboxWhole->pack_start(*scrwnd, Gtk::PACK_EXPAND_WIDGET, 0);
	vboxWhole->pack_start(*hboxDlgAction, Gtk::PACK_SHRINK);

	add(*vboxWhole);
	set_modal(false);
	set_title(strAppTitle+"::IPTables Rules");
	
	m_pTreeStoreIPTablesRules = Gtk::TreeStore::create(m_colRecord);
	m_tvIPTablesRules.set_model(m_pTreeStoreIPTablesRules);
	m_tvIPTablesRules.append_column("IPTables Rules", m_colRecord.m_colIPTablesRule);
	m_pTreeSel = m_tvIPTablesRules.get_selection();
	m_pTreeSel->set_mode(Gtk::SELECTION_MULTIPLE);
	m_pTreeSel->set_select_function(SigC::slot(*this, &CDlgIPTablesRules::on_Selection_changing));
	m_pTreeSel->signal_changed().connect(SigC::slot(*this, &CDlgIPTablesRules::on_selection_changed));

	show_all_children();

	m_butDel.signal_clicked().connect(SigC::slot(*this, &CDlgIPTablesRules::on_butDel_clicked));
#ifdef WITHGCONF
	m_gconfUI.add_dir(gb_pGConfClient, strGConfUIIPTablesRulesDlg, SigC::slot(*this, &CDlgIPTablesRules::on_value_changed));
	on_value_changed("", Gnome::Conf::Value());
	signal_delete_event().connect(SigC::slot(*this, &CDlgIPTablesRules::on_delete));
	butCancel->signal_clicked().connect(SigC::slot(*this, &CDlgIPTablesRules::on_butCancel_clicked));
#else
	m_tvIPTablesRules.modify_font(Pango::FontDescription("Courier 10 Pitch"));
	resize(600, 600);
	butCancel->signal_clicked().connect(SigC::slot(*this, &CDlgIPTablesRules::hide));
#endif
}

#ifdef WITHGCONF
bool CDlgIPTablesRules::on_delete(GdkEventAny*)
{
	save_pos_and_size();
	return false;
}

void CDlgIPTablesRules::on_butCancel_clicked()
{
	save_pos_and_size();
	hide();
}

void CDlgIPTablesRules::save_pos_and_size()
{
	int nX, nY;
	get_position(nX, nY);
	m_gconfUI[strGConfUIIPTablesRulesDlg_posX].set(nX);
	m_gconfUI[strGConfUIIPTablesRulesDlg_posY].set(nY);
	m_gconfUI[strGConfUIIPTablesRulesDlg_sizeX].set(get_width());
	m_gconfUI[strGConfUIIPTablesRulesDlg_sizeY].set(get_height());
}

void CDlgIPTablesRules::on_value_changed(const ustring& strKey, const Gnome::Conf::Value& value)
{
	bool bFFConcerned = (strKey.find(strGConfUIIPTablesRulesDlg) != ustring::npos);
#if FF_DEBUG
	cerr << "CDlgIPTablesRules::on_value_changed(): strKey=" << strKey << endl;
	cerr << "CDlgIPTablesRules::on_value_changed()::bFFConcerned=" << bFFConcerned << endl;
#endif
	if (bFFConcerned && strKey.length())
		m_gconfUI[strKey] = value;
	if (bFFConcerned || !strKey.length())
	{
#if FF_DEBUG
		cerr << "CDlgIPTablesRules::on_value_changed(): moving window" << endl;
#endif
		move(m_gconfUI[strGConfUIIPTablesRulesDlg_posX].get_int(), m_gconfUI[strGConfUIIPTablesRulesDlg_posY].get_int());
		resize(m_gconfUI[strGConfUIIPTablesRulesDlg_sizeX].get_int(), m_gconfUI[strGConfUIIPTablesRulesDlg_sizeY].get_int());
		Pango::FontDescription font(m_gconfUI[strGConfUIIPTablesRulesDlg_font].get_string());
		m_tvIPTablesRules.modify_font(font);
	}
}
#endif // WITHGCONF


void CDlgIPTablesRules::on_IPTablesRules_avail(const char* szMsg, int nLen)
{
#if FF_DEBUG
	cout << "CDlgIPTablesRules::onIPTablesRules_avail()" << endl;
#endif
	m_pTreeStoreIPTablesRules->clear();
	const char* itLineBegin = szMsg;
	const char* itLineEnd = NULL;
	const char* itEnd = itLineBegin+nLen+1;
	Gtk::TreeIter treenode;
	for (; (itLineEnd = find(itLineBegin, itEnd, '\n')) != itEnd; itLineBegin = itLineEnd+1)
	{
		if (*itLineBegin == 'C')
		{	// make lines beginning with "Chain" to the main nodes
			treenode = m_pTreeStoreIPTablesRules->append();
			treenode->set_value(m_colRecord.m_colIPTablesRule, ustring(itLineBegin, itLineEnd));
		}
		else if (itLineEnd > itLineBegin)
			// strip empty lines
			// append the rules to their main node
			m_pTreeStoreIPTablesRules->append(treenode->children())->set_value(m_colRecord.m_colIPTablesRule, ustring(itLineBegin, itLineEnd));
	}
	m_tvIPTablesRules.expand_all();
}


void CDlgIPTablesRules::deleteRule(const Gtk::TreePath& path, int* pnCount, int* pnPath)
{
	Glib::ArrayHandle <int>::const_iterator pathIt = path.get_indices().begin();
#if FF_DEBUG
	cout << "deleting iptables rule: chain=" << (*pathIt)+ff::CHAIN_BEGIN << ", nr= " << (*(pathIt+1))-*pnCount << endl;
#endif
	
	if (*pathIt != *pnPath)
	{	// count for each chain separately
		*pnCount = 0;
		*pnPath = *pathIt;
	}
	try
	{
		// server sends us the new rules, therefore deleting a treerow is not needed
		ffIO << CCO_IPTablesRuleDelete((ff::Chains) ((*pathIt)+ff::CHAIN_BEGIN), (*(pathIt+1))-*pnCount);
		++*pnCount;
	}
	catch (...)
	{
	}
}


void CDlgIPTablesRules::on_butDel_clicked()
{
	lock();
	int nCount = 0;
	int nPath = 0;
	m_pTreeSel->selected_foreach(SigC::bind(SigC::slot(&CDlgIPTablesRules::deleteRule), &nCount, &nPath));
	unlock();
}


bool CDlgIPTablesRules::on_Selection_changing(	const Glib::RefPtr <Gtk::TreeModel>& ptm, 
												const Gtk::TreePath& path, 
												bool bSelLost)
{
	// losing selection is always allowed
	if (bSelLost)
		return true;
	
	// if the path has only 1 element, we are on a main node
	if (path.get_indices().size() == 1)
		// prevent selection of a main node
		return false;
	
	// else a rule or description is to be selected
	Glib::ArrayHandle <int>::const_iterator idxIt = path.get_indices().begin();
	if (*++idxIt == 0)
		// but prevent selection of the description
		return false;
	
/*	Gtk::TreeIter it = m_pTreeStoreIPTablesRules->get_iter(path)->parent();
	const Gtk::TreeNodeChildren& cont = it->children();
	if (*idxIt == (int) cont.size()-1)
		// also prevent selection of the last rule (``queue all")
		return false;
*/	
	return true;
}


void CDlgIPTablesRules::on_selection_changed()
{
	lock();
	bool bAnySelected = false;
	m_pTreeSel->selected_foreach(SigC::bind(SigC::slot(isAnySelected), &bAnySelected));
	// disable delete button if no node is selected
	m_butDel.set_sensitive(bAnySelected);
	unlock();
}


void CDlgIPTablesRules::isAnySelected(const Gtk::TreePath& path, bool* bWasHere)
{
	*bWasHere = true;
}


void CDlgIPTablesRules::on_Authentication(int nAuthenticated)
{
	lock();
	if (nAuthenticated == 1)
		m_butDel.set_sensitive();
	unlock();
}


void CDlgIPTablesRules::on_networkthread_error(CffIO::pCffIOError pErr, bool bDisconnected)
{
	lock();
	if (bDisconnected)
		m_butDel.set_sensitive(false);
	unlock();
}
