/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
/*
Author: Christian Hubinger <chubinger@irrsinnig.org>, (C) 2001-2004
*/
#include "kmfprotocollibrary.h"

// QT includes
#include <tqfile.h>
#include <tqdir.h>
#include <tqdom.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tqvaluelist.h>

// KDE includes
#include <kapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <klocale.h>
#include <kio/netaccess.h>
#include <kio/job.h>
#include <ktrader.h>
#include <klibloader.h>
#include <ktempfile.h>
#include <kfileitem.h>

// project includes
#include "../version.h"
#include "xmlnames.h"
#include "kmfundoengine.h"
#include "kmferror.h"
#include "kmferrorhandler.h"
#include "kmfgenericdoc.h"
#include "kmfnetzone.h"
#include "kmfnethost.h"
#include "kmfprotocolcategory.h"



namespace KMF {


/** Static Stuff **/
KMFProtocolLibrary* KMFProtocolLibrary::m_instance = 0;
KMFProtocolLibrary* KMFProtocolLibrary::instance() {
	if ( ! m_instance ) {
		m_instance = new KMFProtocolLibrary( 0 , "KMFProtocolLibrary");
	}
	return m_instance;
}
/** End Static Stuff **/ 
 
KMFProtocolLibrary::KMFProtocolLibrary( TQObject* parent, const char* name ) : TQObject( parent, name ) {
	m_libraryLoaded = false;
	forceProtocolLibraryReload();
}
KMFProtocolLibrary::~KMFProtocolLibrary() {}

TQValueList< KMFProtocolCategory* >& KMFProtocolLibrary::protocolCategories() {
	return m_protocolCategories;
}
TQValueList<KMFProtocol*>& KMFProtocolLibrary::allProtocols() {
	m_protocols.clear();
	
	TQValueList<KMFProtocolCategory* >::iterator it;
	for( it = protocolCategories().begin(); it != protocolCategories().end(); ++it ) {
		KMFProtocolCategory* cat = *it;
		TQValueList<KMFProtocol*>& currProtocols = cat->protocols();
		TQValueList<KMFProtocol*>::iterator itProt;
		for( itProt = currProtocols.begin(); itProt != currProtocols.end(); ++itProt ) {
			m_protocols.append( *itProt );
		}
	}
	return m_protocols;
}


void KMFProtocolLibrary::forceProtocolLibraryReload() {
	if ( m_libraryLoaded ) {
		kdDebug() << "\nWARNING: KMFProtocolLibrary allready loaded, soing nothing!\n\n" << endl;
		return;
	}
	kdDebug() << "########### START LOADING PROTOCOL LIBRARY #############" << endl;
	
	m_protocolCategories.clear();
	m_protocols.clear();
	
	KMFError *err = new KMFError();
	KMFErrorHandler *errH = new KMFErrorHandler( "KMFErrorHandler" );
			
	KStandardDirs std_dir;
	TQString file = std_dir.findResource( "data", "kmyfirewall/protocols/kmfprotocollibrary.xml" );
	KURL url;
	url.setPath( file );
	kdDebug() << "STD lib at: " << file << endl;
	
	err->setErrType( KMFError::OK );
	loadProtocolDefinitionsFromURL( url, err );
	
	if ( ! errH->showError( err ) ) {
		return;
	}
	
	TQValueList<KMFProtocolCategory* >::iterator it;
	for( it = protocolCategories().begin(); it != protocolCategories().end(); ++it ) {
		KMFProtocolCategory* cat = *it;
		TQValueList<KMFProtocol*>& currProtocols = cat->protocols();
		TQValueList<KMFProtocol*>::iterator itProt;
		for( itProt = currProtocols.begin(); itProt != currProtocols.end(); ++itProt ) {
			KMFProtocol *p = *itProt;
			p->setCustomProtocol( false );
		}
	}	
	kdDebug() << "########### FINISHED LOADING PROTOCOL LIBRARY #############" << endl;			
	
	kdDebug() << "########### START LOADING CUSTOM PROTOCOL LIBRARY #############" << endl;	
	file = std_dir.findResource( "data", "kmyfirewall/protocols/kmfcustomprotocollibrary.xml"	 );
	
	url.setPath( file );
	kdDebug() << "CUSTOM lib at: " << file << endl;
	// KURL tmpurl =  KURL( dir + "protocols/kmfcustomprotocollibrary.xml" );
	if ( ! KIO::NetAccess::exists( url, false, KApplication::kApplication()->mainWidget() ) ) {	
		kdDebug() << "KMFGenericDoc::loadProtocolLibrary() - no custom protocols found." << endl;
		return;
	}
		
		
	err->setErrType( KMFError::OK );
	loadProtocolDefinitionsFromURL( url, err );
	if (  err->errType() != KMFError::OK ) {
		return;
	}	
	
	kdDebug() << "########### FINISHED LOADING CUSTOM PROTOCOL LIBRARY #############" << endl;
	m_libraryLoaded = true;		
}

void KMFProtocolLibrary::loadProtocolDefinitionsFromURL( const KURL& url,  KMFError* err ) {
	kdDebug() << "KMFProtocolLibrary::loadProtocolDefinitionsFromURL( const KURL& " << url.fileName() << " )" << endl;
// 	TQPtrList<KMFProtocol> *prots = new TQPtrList<KMFProtocol>();

	KIO::UDSEntry f_props;
	
	if ( ! KIO::NetAccess::stat( url , f_props, KApplication::kApplication()->mainWidget() ) ) {
		
		const TQString msg = i18n( "<qt><p>The file <b>%1</b> could not be loaded.</p>"
		                          "<p>If you are working with files stored on remote computers "
		                          "make sure that the network is up and the fileserver running.</qt>" ).arg( url.url() );
		err->setErrType( KMFError::NORMAL );
		err->setErrMsg( msg );
		return;
	}
	
	KFileItem *props = new KFileItem( f_props, url );
	kdDebug() << "Found file permissions: " << props->permissionsString() << endl;
	if ( !props->isReadable() ) {
		const TQString msg = i18n( "<qt><p>You don't have the permissions to read file: <b>%1</b></p></qt>" ).arg( url.url() );
		err->setErrType( KMFError::NORMAL );
		err->setErrMsg( msg );
		return;
	}

	TQString xmlfile;
	if ( ! KIO::NetAccess::download( url, xmlfile, KApplication::kApplication()->mainWidget() ) ) {
		const TQString msg = i18n( "<qt><p>Could not download file: <b>%1</b></p></qt>" ).arg( url.url() );
		err->setErrType( KMFError::NORMAL );
		err->setErrMsg( msg );
		return;
	}

	if ( xmlfile.isEmpty() ) {
		const TQString msg = i18n( "<qt><p>No File found at URL: <b>%1</b></p></qt>" ).arg( url.url() );
		err->setErrType( KMFError::NORMAL );
		err->setErrMsg( msg );
		return;
	}
	
	TQFile kmfrsFile( xmlfile );

	TQDomDocument domTree;
	if ( !kmfrsFile.open( IO_ReadOnly ) ) {
		kdDebug() << "Couldn't open file" << endl;
		return;
	}
	if ( !domTree.setContent( &kmfrsFile ) ) {
		kdDebug() << "Couldn't set DomDocument content" << endl;
		kmfrsFile.close();
		return;
	}
	kmfrsFile.close();

	TQDomElement root = domTree.documentElement();
	TQDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
	//	kdDebug() << "Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == XML::ProtocolCategory_Element ) {
		//	kdDebug() << "Parsing XML::ProtocolCategory_Element " << endl;
			
			TQString uuid = curr.toElement().attribute( XML::Uuid_Attribute );
			KMFProtocolCategory *protCat = findCategory( uuid );
			// kdDebug() << "asghasgh" << endl;
			if ( ! protCat ) {
				TQString name = curr.toElement().attribute(  XML::Name_Attribute );
				kdDebug() << " + Creating KMFProtocolCategory: " << name << endl;
				protCat = KMFProtocolCategory::createCategory( name );
				m_protocolCategories.append( protCat );
			}
			TQDomDocument protocolCat;
			protocolCat.appendChild( curr.cloneNode( true ) );
			TQStringList *errors = new TQStringList();
			protCat->loadXML( protocolCat, *errors );
			// FIXME: Check Errors
		} 
		curr = curr.nextSibling();
	}
 	
 	kdDebug() << "Finished Parsing Protocol Library: " << url.url() << endl;
	return;
} 

KMFError* KMFProtocolLibrary::saveProtocolLibrary() {
	kdDebug() << "KMFProtocolLibrary::saveProtocolLibrary()" << endl;
	KMFError *err = new KMFError();
	KStandardDirs std_dir;
	
	TQString saveFile = std_dir.saveLocation( "data", "kmyfirewall"	 );
	
	KURL url;
	url.setPath( saveFile + "protocols/kmfcustomprotocollibrary.xml" );
	
	TQString sDir = saveFile;
	TQDir d0( sDir );
	if ( ! d0.exists() ) {
		d0.mkdir( sDir, true );
	}
	
	sDir = saveFile + "protocols/";
	TQDir d1( sDir );
	if ( ! d1.exists() ) {
		d1.mkdir( sDir, true );
	}
	
	KTempFile file;
	
	
	TQDomDocument doc( "kmyfirewall-protocollibrary" );
	TQDomElement root = doc.createElement( "protocollibrary" );
	root.setAttribute( XML::Version_Attribute, KMYFIREWALL_VERSION );
	root.setAttribute( XML::MinVersion_Attribute, "1.0.0" );
	root.setAttribute( XML::MaxVersion_Attribute, "~" );
	
	TQValueList<KMFProtocolCategory* >::iterator it;
	for( it = protocolCategories().begin(); it != protocolCategories().end(); ++it ) {
		KMFProtocolCategory* cat = *it;
		root.appendChild( cat->getDOMTree() );
	}
	doc.appendChild( root );
	// return  *( new TQString( doc.toString() ) );
	const TQString& xml = doc.toString();
	
	kdDebug() << "XML: " << xml << endl;
	
	if ( file.name() != TQString() ) {
		TQFile f( file.name() );
		f.remove();
		bool isWriteable = f.open( IO_ReadWrite );
		if ( isWriteable ) {
			TQTextStream ts( &f );
			ts << xml << endl;
			f.flush();
			f.close();
			
			
			if ( ! KIO::NetAccess::upload( file.name(), url, KApplication::kApplication()->mainWidget() ) ) {
				kdDebug() << "Coudn't save File: " << url.url() << endl;
				err->setErrType( KMFError::NORMAL );
				err->setErrMsg( i18n( "<qt><p><b>Saving <i>%1</i> Failed.</b></p>"
				                        "<p>Please make sure that you have the permissions to write to this Directory.<br>"
				                        "If you are working with remotely stored files "
				                        "make sure that the target host and the directory is reachable."
				                        "</p></qt>" ).arg( url.url() ) );
				file.unlink();
				// errH->showError( err );
				return err;
				
			}
			file.unlink();
			kdDebug() << "\n\nSaved ProtocolLibrary to: " << url.url() << "\n"<< endl;
			err -> setErrType( KMFError::OK );
			err -> setErrMsg( "" );
			// errH->showError( err );

//			forceProtocolLibraryReload();
			
			return err;
		} else {
			// generate retrun error object
			err -> setErrType( KMFError::NORMAL );
			const TQString& msg = i18n( "Opening file %1 for writing failed.\n"
			                           "Please make sure that you are logged in as root" ).arg( file.name() );
			err -> setErrMsg( msg );
			file.unlink();
			// errH->showError( err );
			return err;
		}

	} else {
		// generate retrun error object
		err -> setErrType( KMFError::NORMAL );
		const TQString& msg = i18n( "Opening file %1 for writing failed.\n"
		                           "Please make sure that you are logged in as root" ).arg( file.name() );
		err -> setErrMsg( msg );
		file.unlink();
		// errH->showError( err );
		return err;
		
	}
	
	// generate retrun error object
	err -> setErrType( KMFError::FATAL );
	const TQString& msg = i18n( "File to save = TQString(). This is a bug." );
	err -> setErrMsg( msg );
	file.unlink();
	// errH->showError( err );
	return err;
}

KMFProtocol* KMFProtocolLibrary::findProtocolByName( const TQString& name ) {
//	kdDebug() << "TQPtrList<KMFProtocol>* KMFProtocol::findProtocol( " << name << " )" << endl;
	TQValueList< KMFProtocol* >& allProtos =  allProtocols();
	TQValueList< KMFProtocol* >::iterator it;
	for( it = allProtos.begin(); it != allProtos.end(); ++it ) {
		KMFProtocol *p = *it;
		if ( p->name() == name ) {
			// kdDebug() << "Found Protocol: " << p->name() << " in KMFProtocol::protocolLIbrary()." << endl;
			return p;
		}
	}
	kdDebug() << "WARNING: Could not find protocol: " << name << " in KMFProtocol::protocolLIbrary()." << endl;
	return 0;
}

KMFProtocol* KMFProtocolLibrary::findProtocolByUuid( const TQUuid& uuid ) {
//	kdDebug() << "TQPtrList<KMFProtocol>* KMFProtocol::findProtocolByUuid( " <<  uuid.toString() << " )" << endl;
	TQValueList< KMFProtocol* >& allProtos =  allProtocols();
	TQValueList< KMFProtocol* >::iterator it;
	for( it = allProtos.begin(); it != allProtos.end(); ++it ) {
		KMFProtocol *p = *it;
		if ( p->uuid() == uuid ) {
		//	kdDebug() << "Found Protocol: name=" << p->name() << " uuid=" <<  uuid.toString()  << " in KMFProtocol::protocolLIbrary()." << endl;
			return p;
		}
	}
	kdDebug() << "WARNING: Could not find protocol: " << uuid.toString() << " in KMFProtocol::protocolLIbrary()." << endl;
	return 0;
}

KMFProtocol* KMFProtocolLibrary::findEquivalentProtocol( KMFProtocol* comp ) {
	kdDebug() << "TQPtrList<KMFProtocol>* KMFProtocol::findEquivalentProtocol( " << comp->name() << " )" << endl;
	TQValueList< KMFProtocol* >& allProtos =  allProtocols();
	TQValueList< KMFProtocol* >::iterator it;
	for( it = allProtos.begin(); it != allProtos.end(); ++it ) {
		KMFProtocol *p = *it;
		if ( p->isEquivalent( comp ) ) {
			kdDebug() << "Found Equivalent Protocol: " << p->name() << endl;
			return p;
		}
	}
	
	kdDebug() << "WARNING: No Equivalent Protocol found!" << endl;
	return 0;
}

KMFProtocolCategory* KMFProtocolLibrary::findCategory( const TQUuid& uuid ) {
	// kdDebug() << "KMFProtocolCategory* KMFProtocolLibrary::findCategory( const TQUuid& " << uuid << " )" << endl;
	TQValueList< KMFProtocolCategory* >::iterator it;
	for( it = m_protocolCategories.begin(); it != m_protocolCategories.end(); ++it ) {
		// kdDebug() << "At cat: " << (*it)->name() << " "  << (*it)->uuid()  << endl;
		if ( (*it)->uuid() == uuid  ) {
			// kdDebug() << "Returning Found Categroy" << endl;
			return (*it);
		}
	}
//	kdDebug() << "No Categroy Found" << endl;
	return 0;
}

}
