//
// C++ Implementation: kmfnethost
//
// Description:
//
//
// Author: Christian Hubinger <chubinger@irrsinnig.org>, (C) 2003
//
// Copyright: See COPYING file that comes with this distribution
//
//

/***************************************************************************
 *                                                                         *
 *   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 "kmfnethost.h"
#include "kmftarget.h"

// QT includes
#include <tqdom.h>

// KDE includes
#include <kdebug.h>
#include <klocale.h>

// Project includes
#include "kmfundoengine.h"
#include "kmfcheckinput.h"
#include "kmfprotocol.h"
#include "kmfprotocollibrary.h"
#include "kmfprotocolusage.h"
#include "kmfnetzone.h"
#include "kmfgenericdoc.h"
#include "kmferror.h"
#include "ipaddress.h"
#include "xmlnames.h"

#include <stdlib.h>

namespace KMF {

KMFNetHost::KMFNetHost( NetfilterObject *parent, const char* name, const TQString& hostName, KMFNetwork* net ) : KMFTarget( parent, name, hostName, net ) {
	m_logIncoming = false;
	m_logOutgoing = false;
	m_guiName = i18n("New Host");
	m_address = new IPAddress( 0,0,0,0 );
	m_limitScale = "minute";
	m_limitNum = -1;
	m_protocols.setAutoDelete( false );
	setName( hostName );
//	m_object_type = NETHOST;
	if ( KMFNetZone* zone = dynamic_cast<KMFNetZone*> ( parent ) ) {
		m_zone = zone;
		m_address->setAddress( m_zone->address()->toString() );
	} else {
		kdDebug() << "ERROR: KMFNetHost called with wrong Parent class." << endl;
	}
}
// 
KMFNetHost::~KMFNetHost() {
	kdDebug() << "KMFNetHost::~KMFNetHost()" << endl;
	clear();
}

int KMFNetHost::type() {
	kdDebug() << "KMFNetHost::type()" << endl;
	return NetfilterObject::NETHOST;
}
void KMFNetHost::clear() {
	m_protocols.setAutoDelete( true );
	m_protocols.clear();
	m_protocols.setAutoDelete( false );
}

void KMFNetHost::setLogIncoming( bool onoff ) {
	if ( onoff == m_logIncoming ) {
		return;
	}
	m_logIncoming = onoff;
	changed();
}

void KMFNetHost::setLogOutgoing( bool onoff ) {
	if ( onoff == m_logOutgoing ) {
		return;
	}
	m_logOutgoing = onoff;
	changed();
}

void KMFNetHost::setLimit( int num, const TQString& scale ) {
	if ( num < 1 ) {
		m_limitNum = -1;
		m_limitScale = "minute";
		return;
	}
	m_limitNum = num;
	m_limitScale = scale;
	changed();
}

bool KMFNetHost::limit() const {
	if ( m_limitNum > 0 )
		return true;
	
	return false;
}

int KMFNetHost::limitRate() const {
	return m_limitNum;
}

const TQString& KMFNetHost::limitScale() const {
	return m_limitScale;
}

TQPtrList<KMFProtocolUsage>& KMFNetHost::protocols() const {
	TQPtrList<KMFProtocolUsage>* ret_val = new TQPtrList<KMFProtocolUsage>;
	*ret_val = m_protocols;
	return *ret_val;
}

KMFProtocolUsage* KMFNetHost::findProtocolUsageByProtocolUuid( const TQUuid& uuid ) const {
	kdDebug() << "KMFProtocol* KMFNetHost::findProtocolUsageByProtocolUuid( const TQUuid& " << uuid.toString() << " ) const" << endl;
	if ( (new TQUuid(uuid))->isNull() ) {
		exit(1);
	}
	
	TQPtrListIterator<KMFProtocolUsage> it(m_protocols);
	while ( it.current() ) {
		KMFProtocolUsage *p = it.current();
		++it;
		if ( p->protocol()->uuid() == uuid ) {
// 			kdDebug() << "Found Protocol: "  << name << endl;
			return p;
		}
	}
	return 0;
}

KMFProtocolUsage* KMFNetHost::addProtocolUsage( const TQUuid& protocolUuid, const TQDomDocument& xml ) {
	// kdDebug() << "KMFNetHost* KMFNetZone::addProtocolUsage( const TQUuid& " << protocolUuid.toString() << ", const TQDomDocument& xml )" << endl;
	if ( protocolUuid.isNull() ) {
		kdDebug() << "ERROR: protocolUuid.isNull()" << endl;
		exit(1);
	}
	
	
	KMFProtocolUsage* old =  findProtocolUsageByProtocolUuid( protocolUuid );
	if ( old ) {
 		kdDebug() << "WARNING: Ignoreing duplicate protocol entry in zone" << endl;
		return old;
	}
	KMFProtocol* prot = KMFProtocolLibrary::instance()->findProtocolByUuid( protocolUuid );
	if ( ! prot ) {
		kdDebug() << "ERROR: No Protocol Found By uuid: " << protocolUuid << endl;
		return 0;
	}
	
	KMFProtocolUsage* new_protocol = prot->createUsage();
	
	// FIXME: Check For Error
	TQStringList *errors = new TQStringList();
	new_protocol->loadXML( xml, *errors );
	if ( ! new_protocol->validUsage() ) {
		kdDebug() << "WARNING: ProtocolUsage parsed from: " << xml.toString() << " is not Valid! Skippin Usage." << endl;
		return 0;
	}
	new_protocol->setProtocol( prot );
	m_protocols.append( new_protocol );
	disconnect( new_protocol, TQT_SIGNAL( destroyed( TQObject* ) ),
		this, TQT_SLOT( slotOnProtocolUsageDeleted( TQObject* ) ) );
	connect( new_protocol, TQT_SIGNAL( destroyed( TQObject* ) ),
		this, TQT_SLOT( slotOnProtocolUsageDeleted( TQObject* ) ) );
	changed();
	return new_protocol;
}


void KMFNetHost::delProtocolUsage( KMFProtocolUsage* prot, bool destructive ){
// 	kdDebug() << "void KMFNetHost::delProtocol( KMFProtocol* prot )" << endl;
	TQPtrListIterator<KMFProtocolUsage> it( m_protocols );
	bool deleted = false;
	while ( it.current() ) {
		KMFProtocolUsage *p =  it.current();
		kdDebug() << "Comparing protocol: " << prot->uuid() << " with protocol: " << p->uuid() << endl;
		if ( p->name() == prot->name() ) {
			kdDebug() << "Delete protocol: " << prot->uuid() << " from host:  " << name() << endl;
			m_protocols.remove( p );
			if ( destructive ) {
				p->deleteLater();
			}
			deleted = true;
		}
		++it;
	}
	
	if ( ! deleted ) {
		kdDebug() << "WARNING: Couldn't delete protocol: " << prot->name() << " from host:  " << name() << endl;
	}
	changed();
}

void KMFNetHost::slotOnProtocolUsageDeleted( TQObject* prot ) {
	kdDebug() << "KMFNetHost::slotOnProtocolUsageDeleted... Host name: " << this->name() << endl;
	TQPtrListIterator<KMFProtocolUsage> it( m_protocols );
	while ( it.current() ) {
		KMFProtocolUsage * p = it.current();
		++it;
		if ( p == prot ) {
			kdDebug() << "Deleting Protocol" << endl;
			m_protocols.remove( p );
			p->deleteLater();
			changed();
			return;
		}
	}
}



bool KMFNetHost::protocolInherited( const TQUuid& uuid ) const {
//  	kdDebug() << "bool KMFNetHost::protocolInherited() const" << endl;
	if ( ! m_zone )
		return false;
	
	if ( uuid.isNull() ) {
		kdDebug() << "ERROR: KMFNetHost::protocolInherited(): uuid.isNull() == true" << endl;
		exit(1);
	}
	
	TQPtrListIterator<KMFProtocolUsage> it( m_zone->protocols() );
	while ( it.current() ) {
		KMFProtocolUsage *p = it.current();
		++it;
		if ( p->protocol()->uuid() == uuid ) {
			kdDebug() << "Found Inherited Protocol: "  << p->protocol()->name() << endl;
			return true;
		}
	}
	return m_zone->protocolInherited( uuid );
}

const TQDomDocument& KMFNetHost::getDOMTree() {
	kdDebug() << "const TQDomDocument& KMFNetHost::getDOMTree() " << endl;
	TQDomDocument doc;
	TQDomElement root = doc.createElement( XML::NetHost_Element );
	NetfilterObject::saveUuid( root );

	root.setAttribute( XML::Name_Attribute, name() );
	root.setAttribute( XML::GUIName_Attribute, m_guiName );
	root.setAttribute( XML::Description_Attribute, description() );
	root.setAttribute( XML::Address_Attribute, m_address->toString() );
	if ( logIncoming() ) {
		root.setAttribute( XML::LogIncoming_Attribute, XML::BoolOn_Value );
	} else {
		root.setAttribute( XML::LogIncoming_Attribute, XML::BoolOff_Value );
	}
	if ( logOutgoing() ) {
		root.setAttribute( XML::LogOutgoing_Attribute, XML::BoolOn_Value );
	} else {
		root.setAttribute( XML::LogOutgoing_Attribute, XML::BoolOff_Value );
	}
	
	root.setAttribute( XML::LimitRate_Attribute, limitRate() );
	root.setAttribute( XML::LimitScale_Attribute, limitScale() );

	TQPtrListIterator<KMFProtocolUsage> it2 ( m_protocols );
	while (  it2.current() ) {
		root.appendChild(  it2.current()->getDOMTree( ) );
		++it2;
	}
	doc.appendChild( root );
	return *( new TQDomDocument( doc ) );
}
void KMFNetHost::loadXML( const TQDomDocument& doc, TQStringList& errors ) {
	// kdDebug() << "void KMFNetHost::loadXML( const TQDomDocument& )" << endl;
 	TQDomElement root = doc.documentElement();
 	loadXML( root, errors );
}
void KMFNetHost::loadXML( TQDomNode root, TQStringList& errors ) {
 	kdDebug() << "void KMFNetHost::loadXML( TQDomNode root )" << endl;
	//TQDomElement root = doc.documentElement();
	
	NetfilterObject::loadUuid( root, errors );
	TQString name = "";
	TQString guiName = "";
	TQString desc = "";
	TQString address = "";
	TQString logIn = "";
	TQString logOut = "";
	TQString limitRate = "";
	TQString limitScale = "";	
	
	name = root.toElement().attribute(  XML::Name_Attribute );
	guiName = root.toElement().attribute( XML::GUIName_Attribute );
	desc = root.toElement().attribute( XML::Description_Attribute );
	address = root.toElement().attribute( XML::Address_Attribute );
	logIn = root.toElement().attribute( XML::LogIncoming_Attribute );
	logOut = root.toElement().attribute( XML::LogOutgoing_Attribute );
	limitRate = root.toElement().attribute( XML::LimitRate_Attribute );
	limitScale = root.toElement().attribute( XML::LimitScale_Attribute );
	
	if ( logIn == XML::BoolOn_Value ) {
		m_logIncoming = true;
	} else {
		m_logIncoming = false;
	}

	if ( logOut == XML::BoolOn_Value ) {
		m_logOutgoing = true;
	} else {
		m_logOutgoing = false;
	}
	bool ok;
	int lRate = limitRate.toInt( &ok );
	if ( ok ) {
		setLimit( lRate, *( new TQString( limitScale ) ) );
	}
	
	setDescription( *( new TQString( desc ) ) );
	// setName( *( new TQString( name ) ) );
	setGuiName( *( new TQString( guiName ) ) );
	this->address()->setAddress( address );
	
	TQValueList< KMFProtocolUsage* > xmlDefinedProtocols;
	TQDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		if ( curr.isElement() && ( curr.nodeName() == XML::Protocol_Element ) ) {
			// FIXME: Remove later
			// keep for compatibility
			TQString name = curr.toElement().attribute(  XML::Name_Attribute );
			TQDomDocument protocol_doc;
			protocol_doc.appendChild( curr.cloneNode(true) );
			KMFProtocol *protocol = KMFProtocolLibrary::instance()->findProtocolByName( name );
			if ( ! protocol ) {
				KMFUndoEngine::instance()->log( i18n("No Protocol Found by name: %1").arg( name ), KMFError::OK, this );
				continue;
			}
			
			KMFProtocolUsage* pu = addProtocolUsage( protocol->uuid(), protocol_doc );
			if ( pu ) {
				
				xmlDefinedProtocols.append( pu );
			}
		}
		if ( curr.isElement() && ( curr.nodeName() == XML::ProtocolUsage_Element ) ) {
			TQString protocolUuid = curr.toElement().attribute( XML::ProtocolUuid_Attribute );
			TQDomDocument protocol_doc;
			protocol_doc.appendChild( curr.cloneNode(true) );
			KMFProtocolUsage* pu = addProtocolUsage( protocolUuid , protocol_doc );
			if ( pu ) {
				xmlDefinedProtocols.append( pu );
			}
		}
		curr = curr.nextSibling();
	}
	
	{
		TQPtrList< KMFProtocolUsage >& allprotocols = protocols();
		TQPtrListIterator< KMFProtocolUsage > itAllProtocols( allprotocols ); 
		while( itAllProtocols.current() ) {
			KMFProtocolUsage *oldProtocolUsage = itAllProtocols.current();
			++itAllProtocols;
			
			bool found = false;
			TQValueList< KMFProtocolUsage* >::iterator itProtocols;
			for( itProtocols = xmlDefinedProtocols.begin(); itProtocols != xmlDefinedProtocols.end() && ! found; ++itProtocols ) {
				KMFProtocolUsage* pu = *itProtocols;
				if ( pu == oldProtocolUsage ) {
					found = true;
				}
			}
			
			if ( ! found ) {
				KMFUndoEngine::instance()->log( i18n("Removing unused oldProtocolUsage: %1").arg( oldProtocolUsage->name() ), KMFError::OK, this );
				delProtocolUsage( oldProtocolUsage, true );
			}
		}
	}
	changed();
}

}



