/***************************************************************************
begin                : Mon Feb 4 2002
copyright            : (C) 2002 by Christian Hubinger
email                : chubinger@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.                                   *
 *                                                                         *
 ***************************************************************************/
#include "iptruleoption.h"
// KDE
#include <kdebug.h>
#include <klocale.h>


// QT
#include <qdom.h>

// PROJECT
#include "kmfdoc.h"
#include "kmfiptdoc.h"
#include "iptchain.h"
#include "iptable.h"

//############# Initialise static members
QDict<QStringList>* IPTRuleOption::m_dict_option_strings = new QDict<QStringList>;
QDict<QStringList>* IPTRuleOption::m_dict_gui_strings = new QDict<QStringList>;
QDict<QString>* IPTRuleOption::m_dict_option_names = new QDict<QString>;
QPtrList<QString>* IPTRuleOption::m_known_types = new QPtrList<QString>;
bool IPTRuleOption::m_created_dict = false;

IPTRuleOption::IPTRuleOption( IPTRule* rule ) : NetfilterObject( rule ) {
	if ( rule != 0 ) {
		// 		kdDebug() << "IPTRuleOption::IPTRuleOption(..): Creating new IPTRuleOption" << endl;
		m_object_type = NetfilterObject::RULEOPTION;
		m_rule = rule;
		m_parent = rule;
		m_option_type = "UNDEFINED";
		m_target_option = false;
		m_dict_option_strings->setAutoDelete( true );
		m_known_types->setAutoDelete( true );
		for ( int i = 0; i < MAXOPTNUM;i++ )
			m_values[ i ] = "UNDEFINED";

		if ( ! m_created_dict ) {
			// Initialise Dict on first use
			m_rule->chain()->table()->kmfDoc()->registerRuleOptions();
			m_created_dict = true;
		}

	}
}

IPTRuleOption::~IPTRuleOption() {}

void IPTRuleOption::setOptionType( const QString& type ) {
	if ( type.isNull() )
		return ;
	m_option_type = type;
}

const QString& IPTRuleOption::guiName() const {
	return *m_dict_option_names->find( m_option_type );
}

void IPTRuleOption::setTargetOption( bool is_tg_opt ) {
	m_target_option = is_tg_opt;
}

void IPTRuleOption::reset() {
	for ( int i = 0; i < MAXOPTNUM;i++ )
		m_values[ i ] = "bool:off";
	changed();
}

bool IPTRuleOption::isEmpty() {
	for ( int i = 0; i < MAXOPTNUM ;i++ ) {
		// 		kdDebug() << "Curr opt: " << m_values[ i ] << endl;
		if ( ! m_values[ i ].isEmpty() && m_values[ i ] != "UNDEFINED" && m_values[ i ] != "bool:off" && m_values[ i ] != " " ) {
			// 			kdDebug() << "Curr opt: " << getOptionType() << " is not empty found Value: "<< m_values[ i ]  << endl;
			return false;
		}
	}
	// 	kdDebug() << "Curr opt: " << getOptionType() << " is empty!" << endl;
	return true;
}

void IPTRuleOption::loadXML( const QDomDocument& doc ) {
	//	kdDebug() << "const QString& IPTRuleOption::restoreState( )\nXML INPUT: "<< xml << endl;
	QDomElement root = doc.documentElement();
	QString new_opt_type = root.toElement().attribute( "type" );
	if ( m_option_type == "bool:off" ) {
		// 		kdDebug() << "IPTRuleOption::restoreState: Setting Option Type: " << new_opt_type << endl;
		m_option_type = new_opt_type;
	}
	if ( m_option_type == new_opt_type )
		kdDebug() << "Found Option Type: " << m_option_type << endl;

	QString tmp = root.toElement().attribute( "targetoption" );
	if ( tmp == "yes" )
		setTargetOption( true );
	else
		setTargetOption( false );

	QDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		if ( curr.isElement() ) {
			if ( curr.nodeName() == "ruleoptionvalue" ) {
				for ( int i = 0; i < MAXOPTNUM;i++ ) {
					QDomText textChild = curr.firstChild().toText();
					QString attrib = QString( "value%1" ).arg( i );
					QString val = curr.toElement().attribute( attrib );

					if ( !val.isEmpty() && val != "UNDEFINED" ) {
						m_values[ i ] = val;
						//            kdDebug() << "Found Option Value: " << m_values[i] << endl;
					}
				}
			}
		}
		curr = curr.nextSibling();
	}
	changed();
}

void IPTRuleOption::loadValues( QStringList args ) {
	for ( uint i = 0; i < MAXOPTNUM; i++ )
		m_values[ i ] = "bool:off";

	int i = 0;
	for ( QStringList::Iterator it = args.begin(); it != args.end(); ++it ) {
		m_values[ i ] = *it;
		kdDebug() << "+ Inserted Option Argument Nr: " << i << " value: " << m_values[ i ] << endl;
		i++;
	}
	changed();
}

const QStringList& IPTRuleOption::getValues() {
	// 	kdDebug() << "const QStringList& IPTRuleOption::getValues()" << endl;
	QStringList vals;
	for ( uint i = 0; i < MAXOPTNUM; i++ ) {
		QString val = m_values[ i ];
		// 		kdDebug() << "Append Value: "  <<  val << endl;
		vals << val;
	}
	return *( new QStringList( vals ) );
}


const QDomDocument& IPTRuleOption::getDOMTree() {
	// 	kdDebug() << "const QString& IPTRuleOption::getDOMTree( )" << endl;
	QDomDocument doc;
	if ( isEmpty() )
		return * ( new QDomDocument( doc ) );

	bool found = false;
	for ( uint i = 0; i < MAXOPTNUM; i++ )
		if ( !m_values[ i ].isEmpty() && m_values[ i ] != "UNDEFINED" )
			found = true;
	if ( true ) {
		QDomElement root = doc.createElement( "ruleoption" );
		root.setAttribute( "type", m_option_type );
		if ( m_target_option )
			root.setAttribute( "targetoption", "yes" );
		else
			root.setAttribute( "targetoption", "no" );

		root.setAttribute( "id", m_object_id );
		// 		kdDebug() << "Root Element Should be: ruleoption" << endl;
		// 		kdDebug() << "Root Element: " << root.tagName() << endl;
		doc.appendChild( root );
		//  kdDebug() << "Storing: " << m_values->length() << " values" << endl;
		for ( uint i = 0; i < MAXOPTNUM; i++ ) {
			QString val = m_values[ i ];
			if ( !val.isEmpty() && val != "UNDEFINED" ) {
				QDomElement tag = doc.createElement( "ruleoptionvalue" );
				tag.setTagName( "ruleoptionvalue" );
				//			kdDebug() << "Writing Attribute: value" << i << " val: " << val << endl;
				QString attrib = QString( "value%1" ).arg( i );
				tag.setAttribute( attrib, val );
				root.appendChild( tag );
			}
		}
	}
	// 	kdDebug() << "XML:\n " << doc.toString() << endl;
	return *( new QDomDocument( doc ) );
}


const QString& IPTRuleOption::toString() {
	// 	kdDebug() << "const QString& IPTRuleOption::toString()" << endl;

	QStringList * commandStrings;
	commandStrings = m_dict_option_strings->find( m_option_type );
	QString s = "";
	QTextStream str( &s, IO_WriteOnly );
	QString ws = " ";
	// 	kdDebug() << "Option: " << m_option_type << " defined " << used << endl;
	if ( ! isEmpty() && commandStrings && ! commandStrings->isEmpty() ) {
		// 		str << *commandStrings->at( 0 );
		str << ws;
		// 		bool was_modifier = false;
		for ( uint i = 0; i < commandStrings->count(); i++ ) {
			QString command = *commandStrings->at( i );
			QString val = m_values[ i ];
			QStringList* guiStrings = m_dict_gui_strings->find( m_option_type );
			QString guiName = *guiStrings->at( i );

			if (! val.isNull() &&
			        ! val.isEmpty() &&
			        val != "UNDEFINED" &&
			        val != "bool:off" ) {
				if ( val == "bool:on" ) {
					val = "";
				}
//				kdDebug() << "Create option Name: " << guiName << endl;
//				kdDebug() << "Create option string: " << command <<  " " << val << endl;
				str << command;
				str << ws;
				str << val;
				str << ws;
			}
		}
	}
	// 	kdDebug() << "Returning String: " << s << endl;

	return *( new QString( s.simplifyWhiteSpace() ) );
}

//############ static members ####################//
QDict<QStringList>* IPTRuleOption::getOptionStringDict() {
	return m_dict_option_strings;
}

QDict<QStringList>* IPTRuleOption::getGUIStringDict() {
	return m_dict_gui_strings;
}

QPtrList<QString>* IPTRuleOption::getAvailableOptionTypes() {
	return m_known_types;
}

void IPTRuleOption::readRuleOptionDefinition( const QDomDocument& doc ) {
	QDomElement root = doc.documentElement();
	QDomNode curr = root.firstChild();
	bool inOption = false;
	QString name = "";
	QString guiName = "";
	QStringList *vals = 0;
	QStringList *gui = 0;
	while ( !curr.isNull() ) {
		//		kdDebug() << "IPTRuleOption: Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == "ruleoptiondefinition" ) {
			name = curr.toElement().attribute( "name" );
			guiName = curr.toElement().attribute( "guiName" );
			//	version = root.toElement().attribute( "version" );
			vals = new QStringList();
			gui = new QStringList();
			kdDebug() << "+ Register Rule Option: " << name << " " <<  guiName << endl;
			inOption = true;
			readRuleOptionDefinitionNode( curr, vals, gui );
			m_dict_option_strings->insert( name, vals );
			m_dict_gui_strings->insert( name, gui );
			m_dict_option_names->insert( name, new QString( guiName ) );
			m_known_types->append( ( new QString( name ) ) );

			kdDebug() << "+ Testing Key: " << name << endl;
			QStringList* list;
			list = m_dict_option_strings->find( name );
			if ( list ) {
				for ( uint i = 0; i < list->count(); i ++ ) {
					QString s = *list->at( i );
					kdDebug() << "---+ Found Option String:  " << s << endl;
					s = "";
				}
			} else {
				kdDebug() << "!!!!Couldn't fetch QStringList with key: " << name << endl;
			}
		}
		curr = curr.nextSibling();
	}
}


void IPTRuleOption::readRuleOptionDefinitionNode( const QDomNode& currNode, QStringList* vals, QStringList* gui ) {
	QDomNode curr = currNode;
	curr = curr.firstChild();
	while ( !curr.isNull() ) {
		kdDebug() << "IPTRuleOption: Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == "option" ) {
			QString cmd = curr.toElement().attribute( "command" );
			QString guiName = curr.toElement().attribute( "guiName" );

			cmd.simplifyWhiteSpace();
			vals->append( cmd );
			kdDebug() << "---+ Registering Option String:  " << cmd << endl;

			guiName.simplifyWhiteSpace();
			gui->append( guiName );
			kdDebug() << "---+ Registering GUI String:  " << guiName << endl;

		}
		curr = curr.nextSibling();
	}
}

