//
// C++ Implementation: kmfnetzone
//
// Description:
//
//
// Author: Christian Hubinger <chubinger@gmail.com>, (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 "kmfnetzone.h"

// QT includes
#include <qdom.h>

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

// Project includes
#include "kmfcheckinput.h"
#include "kmfprotocol.h"
#include "kmfnethost.h"
#include "kmfgenericdoc.h"
#include "kmferror.h"
#include "ipaddress.h"

KMFNetZone::KMFNetZone( QObject *parent, const QString& name ) : NetfilterObject( parent ) {
	m_doc = 0;
	m_zone = 0;
	m_guiName = i18n( "New Zone" );
// 	m_deadEnd = false;
	m_err = new KMFError();
	m_address = new IPAddress( 0, 0, 0, 0 );
	m_object_type = NETZONE;
	m_protocols.setAutoDelete( true );
	m_zones.setAutoDelete( false );
	m_hosts.setAutoDelete( false );
	if ( name.isNull() )
		setName( i18n( "New Zone" ) );
	else
		setName( name );
	if ( KMFNetZone * zone = dynamic_cast<KMFNetZone*> ( parent ) ) {
		m_zoneType = NODE;
		m_zone = zone;
		setZone( *m_zone->address(), m_zone->maskLength() /* , *m_zone->mask() */ );
	} else if ( KMFGenericDoc * doc = dynamic_cast<KMFGenericDoc*> ( parent ) ) {
		m_zoneType = ROOT;
		m_doc = doc;
	}
}


KMFNetZone::~KMFNetZone() {
	kdDebug() << "KMFNetZone: Deleting Children" << endl;
	m_address->setAddress( "0.0.0.0" );
	m_zones.setAutoDelete( true );
	m_hosts.setAutoDelete( true );
	m_zones.clear();
	m_hosts.clear();
	m_protocols.clear();
	m_zones.setAutoDelete( false );
	m_hosts.setAutoDelete( false );
	delete m_address;
	delete m_err;
}

void KMFNetZone::clear() {
	m_address->setAddress( "0.0.0.0" );
	m_zones.setAutoDelete( true );
	m_hosts.setAutoDelete( true );
	m_zones.clear();
	m_hosts.clear();
	m_protocols.clear();
	m_zones.setAutoDelete( false );
	m_hosts.setAutoDelete( false );
}

KMFGenericDoc* KMFNetZone::doc() const {
	if ( m_zoneType == ROOT ) {
		return m_doc;
	} else if ( m_zoneType == NODE ) {
		return m_zone->doc();
	}
	return 0;
}

KMFNetZone* KMFNetZone::zone() const {
	if ( m_zoneType == NODE ) {
		return m_zone;
	}
	return 0;
}

void KMFNetZone::setGuiName( const QString& name ) {
	m_guiName = name;
}

// void KMFNetZone::setDeadEnd( bool onoff ) {
// 	m_deadEnd = onoff;
// }

void KMFNetZone::setMaskLength( int len ){
	kdDebug() << "void KMFNetZone::setMaskLength( int len )" << endl;
	if ( len < 0 || len > 32 ) {
		kdDebug() << "ERROR: Mask Lengh invalid: " << len << endl;
		m_maskLen = -1;
		return;
	}
	m_maskLen = len;
}

QPtrList<KMFProtocol>& KMFNetZone::protocols() const {
	QPtrList<KMFProtocol>* ret_val = new QPtrList<KMFProtocol>;
	*ret_val = m_protocols;
	return *ret_val;
}

QPtrList<KMFNetZone>& KMFNetZone::zones () const {
	QPtrList<KMFNetZone>* ret_val = new QPtrList<KMFNetZone>;
	*ret_val = m_zones;
	return *ret_val;
}
QPtrList<KMFNetHost>& KMFNetZone::hosts () const {
	QPtrList<KMFNetHost>* ret_val = new QPtrList<KMFNetHost>;
	*ret_val = m_hosts;
	return *ret_val;
}
KMFProtocol* KMFNetZone::findProtocol( const QString& name ) const {
// 	kdDebug() << "KMFProtocol* KMFNetZone::findProtocol( const QString& name ) const" << endl;
	QPtrListIterator<KMFProtocol> it( m_protocols );
	while ( it.current() ) {
		KMFProtocol * p = it.current();
		++it;
		if ( p->name() == name ) {
// 			kdDebug() << "Found Protocol: " << name << endl;
			return p;
		}
	}
	return 0;
}

bool KMFNetZone::protocolInherited( const QString& name ) const {
	// 	kdDebug() << "bool KMFNetZone::protocolInherited() const" << endl;
	if ( m_zoneType != NODE ) {
		// 		kdDebug() << "At ROOT node stopping search" << endl;
		return false;
	}

// 	if ( m_deadEnd ) {
// // 		kdDebug() << "Found Dead End Zone: " << endl;
// 		return false;
// 	}

	QPtrListIterator<KMFProtocol> it( m_zone->protocols() );
	while ( it.current() ) {
		KMFProtocol * p = it.current();
		++it;
		if ( p->name() == name ) {
// 			kdDebug() << "Found Inherited Protocol: " << name << endl;
			return true;
		}
	}
	return m_zone->protocolInherited( name );
}

bool KMFNetZone::setZone( const IPAddress& from, int maskLen  ) {
	m_address->setAddress( from.toString() );
	setMaskLength( maskLen );
	return true;
}


KMFNetZone* KMFNetZone::addZone( const QString& name, KMFError* err ) {
// 	kdDebug() << "KMFProtocol* KMFNetZone::addZone( const QString& name, KMFError* err )" << endl;
	QPtrListIterator<KMFNetZone> it( m_zones );
	while ( it.current() ) {
		KMFNetZone * z = it.current();
		++it;
		if ( z->name() == name ) {
			err->setErrType( KMFError::NORMAL );
			err->setErrMsg( i18n( "Zone %1 already exists, please try again with another name" ).arg( name ) );
			return 0;
		}
	}
	KMFNetZone* new_zone = new KMFNetZone( this, name );
	m_zones.append( new_zone );
	err->setErrType( KMFError::OK );
	return new_zone;
}

void KMFNetZone::delZone( KMFNetZone* zone, bool destructive ) {
// 	kdDebug() << "void KMFNetZone::delZone( KMFNetZone* z )" << endl;
	m_zones.remove( zone );
	if ( destructive )
		delete zone;
}

KMFProtocol* KMFNetZone::addProtocol( const QString& name, const QDomDocument& xml ) {
// 	kdDebug() << "KMFProtocol* KMFNetZone::addProtocol( const QString& name, const QDomDocument& xml )" << endl;
	QPtrListIterator<KMFProtocol> it( m_protocols );
	while ( it.current() ) {
		KMFProtocol * p = it.current();
		++it;
		if ( p->name() == name ) {
			kdDebug() << "WARNING: Ignoreing duplicate protocol entry in zone" << endl;
			return 0;
		}
	}
	KMFProtocol* new_protocol = new KMFProtocol( this );
	new_protocol->loadXML( xml );
	m_protocols.append( new_protocol );
	return new_protocol;
}

void KMFNetZone::delProtocol( KMFProtocol* prot ) {
	QPtrListIterator<KMFProtocol> it( m_protocols );
	bool deleted = false;
	while ( it.current() ) {
		KMFProtocol * p = it.current();
		++it;
		if ( p->name() == prot->name() ) {
			kdDebug() << "Deleting Protocol: " << p->name() << " from zone: " << name() << endl;
			m_protocols.remove( p );
			deleted = true;
		}
	}
	if ( ! deleted ) {
		kdDebug() << "WARNING: Couldn't delete protocol: " << prot->name() << " from zone:  " << m_name << endl;
	}
}

KMFNetHost* KMFNetZone::placeHostInZone( KMFNetHost* host ) {
	kdDebug() << "KMFNetZone::placeHostInZone..." << " Zone name: " << this->name() << endl;
	KMFNetHost* addedHost = 0;
	QPtrListIterator<KMFNetZone> it ( m_zones );
	while ( it.current() ) {
		KMFNetZone *z = *it;
		++it;
		addedHost = z->placeHostInZone( host );
		if ( addedHost )
			return addedHost;
	}

	if ( /*! addedHost &&*/ IPAddress::hostsOnSameNetwork( *address(), *host->address(), maskLength() ) ) {
		if ( host->zone() ) {
			host->zone()->delHost( host, false );
		}
		kdDebug() << "Placing host: " << host->name() << " in zone: " << name() << endl;
		host->setParentZone( this );
		QString hostnum;
		hostnum = hostnum.setNum( m_hosts.count() + 1 );
		QString host_name = "host_" + this->name() + "_" + hostnum;
		host->setName( host_name );
		m_hosts.append( host );
		return host;
	}
	return 0;
}

KMFNetHost* KMFNetZone::addHost( const QString& name, const QDomDocument& xml ) {
// 	kdDebug() << "KMFProtocol* KMFNetHost::addProtocol( const QString& name, const QDomDocument& xml )" << endl;
	QString hostnum;
	hostnum = hostnum.setNum( m_hosts.count() + 1 );
	QString host_name = "host_" + this->name() + "_" + hostnum;
// 	kdDebug() << "Host Nr:"  << hostnum << endl;
// 	kdDebug() << "New internal Host name: " << host_name << endl;
	KMFNetHost* new_host = new KMFNetHost( this , host_name );
	if ( ! new_host ) {
		kdDebug() << "ERROR couldn't create Host" << endl;
		return 0;
	}
	new_host->loadXML( xml );
	new_host->setName( host_name );
	new_host->setGuiName( name );
	KMFNetHost *retHost = placeHostInZone( new_host );
	if ( ! retHost ) {
		kdDebug() << "ERROR: couldn't place host!" << endl;
	}
	return retHost;
}

void KMFNetZone::delHost( KMFNetHost* host, bool destructive ) {
	QPtrListIterator<KMFNetHost> it( m_hosts );
	bool deleted = false;
	while ( it.current() ) {
		KMFNetHost * p = it.current();
		++it;
		if ( p == host ) {
			m_hosts.remove( p );
			if ( destructive )
				delete host;
			deleted = true;

		}
	}
	if ( !deleted ) {
		kdDebug() << "WARNING: Couldn't delete host: " << host->name() << " from zone:  " << m_name << endl;
	}
}

const QDomDocument& KMFNetZone::getDOMTree() {
// 	kdDebug() << "const QDomDocument& KMFNetZone::getDOMTree() " << endl;
	QDomDocument doc;
	QDomElement root = doc.createElement( "netzone" );
	root.setAttribute( "id", m_object_id );
	root.setAttribute( "name", m_name );
	root.setAttribute( "guiName", m_guiName );
	root.setAttribute( "description", m_desc );
// // 	if ( m_deadEnd )
// // 		root.setAttribute( "deadEnd", "bool:on" );
// // 	else
// // 		root.setAttribute( "deadEnd", "bool:off" );

	QDomElement from = doc.createElement( "fromIP" );
	root.appendChild( from );
	QString num;
	from.setAttribute( "address", m_address->toString() );

	QDomElement mask = doc.createElement( "netMask" );
	root.appendChild( mask );
	mask.setAttribute( "address", m_maskLen );

	QPtrListIterator<KMFNetZone> it ( m_zones );
	while ( it.current() ) {
		root.appendChild( it.current() ->getDOMTree( ) );
		++it;
	}

	QPtrListIterator<KMFProtocol> it2 ( m_protocols );
	while ( it2.current() ) {
		root.appendChild( it2.current() ->getDOMTree( ) );
		++it2;
	}

	QPtrListIterator<KMFNetHost> it3 ( m_hosts );
	while ( it3.current() ) {
		root.appendChild( it3.current() ->getDOMTree( ) );
		++it3;
	}

	doc.appendChild( root );
	return *( new QDomDocument( doc ) );
}

void KMFNetZone::loadXML( const QDomDocument& doc ) {
// 	kdDebug() << "void KMFNetZone::loadXML( const QDomDocument& )" << endl;
	QDomElement root = doc.documentElement();
	QDomNode curr = root.firstChild();
	QString name = "";
	QString guiName = "";
	QString desc = "";
// 	QString deadEnd = "";
	name = root.toElement().attribute( "name" );
	guiName = root.toElement().attribute( "guiName" );
	desc = root.toElement().attribute( "description" );
// 	deadEnd = root.toElement().attribute( "deadEnd" );

// 	if ( deadEnd == "bool:on" )
// 		m_deadEnd = true;
// 	else
// 		m_deadEnd = false;

	setDescription( *( new QString( desc ) ) );
	setName( *( new QString( name ) ) );
	setGuiName( *( new QString( guiName ) ) );

	while ( !curr.isNull() ) {
		if ( curr.isElement() && ( curr.nodeName() == "fromIP" || curr.nodeName() == "toIP" || curr.nodeName() == "netMask" ) ) {
			QString addr = curr.toElement().attribute( "address" );
			if ( curr.nodeName() == "fromIP" )
				m_address->setAddress( addr );
/*			if ( curr.nodeName() == "toIP" )
				m_toIP->setAddress( addr );*/
			if ( curr.nodeName() == "netMask" ) {
				bool ok;
				int len = addr.toInt( &ok );
				if ( ok ) {
					setMaskLength( len );
				} else {
					kdDebug() << "Parsing mask failed" << endl;
				}
				//m_netMask->setAddress( addr );
			}
		}
		if ( curr.isElement() && ( curr.nodeName() == "netzone" ) ) {
			QString name = "";
			curr.toElement().attribute( "name" );
			QDomDocument zone_doc;
			zone_doc.appendChild( curr.cloneNode( true ) );
			KMFNetZone *z = addZone( name, m_err );
			if ( z ) {
				z->loadXML( zone_doc );
			}
		}
		if ( curr.isElement() && ( curr.nodeName() == "protocol" ) ) {
			QString name = "";
			curr.toElement().attribute( "name" );
			QDomDocument protocol_doc;
			protocol_doc.appendChild( curr.cloneNode( true ) );
			addProtocol( name, protocol_doc );
		}
		if ( curr.isElement() && ( curr.nodeName() == "nethost" ) ) {
			QString name = "";
			curr.toElement().attribute( "name" );
			QDomDocument host_doc;
			host_doc.appendChild( curr.cloneNode( true ) );
			KMFNetHost *h = addHost( name, host_doc );
			if ( h ) {
				h->loadXML( host_doc );
			}
		}
		curr = curr.nextSibling();
	}
// 	kdDebug() << "KMFNetZone - finished parsing XML" << endl;
}

