/***************************************************************************
    begin                : Mon Jan 28 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.                                   *
 *                                                                         *
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// StdLib includes
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>

// QT includes
#include "qtextstream.h"
#include "qfile.h"
#include "qxml.h"
#include "qregexp.h"
#include "qptrlist.h"
#include "qmessagebox.h"
#include <qstringlist.h>
#include <qvbox.h>
#include <qlabel.h>

// KDE includes
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kfiledialog.h>
#include <kurl.h>
#include <qfiledialog.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <klocale.h>
#include <kprocess.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <ktempfile.h>

// My includes
#include "../version.h"
#include "kmfdoc.h"
#include "iptchain.h"
#include "iptrule.h"
#include "iptable.h"
#include "netfilterobject.h"
#include "kmferror.h"
#include "kmfconfig.h"
#include "kmferrorhandler.h"

//##########  KMFDoc the baseclass ##############
// static stuff
uint KMFDoc::MAX_UNDO = 10;
void KMFDoc::setMaxUndo( uint num ) {
	MAX_UNDO = num;
}
// end static stuff

KMFDoc::KMFDoc( QObject *parent, const char *name ) : QObject( parent, name ) {
	kdDebug() << "KMFDoc::KMFDoc( QObject *parent, const char *name ) : QObject( parent, name )" << endl;
	m_description = i18n("No Description Available");
	m_name = i18n("Unnamed Document");
}
KMFDoc::~KMFDoc() {}


void KMFDoc::setUrl( const KURL& url ) {
	m_url = url;
}

void KMFDoc::setFileName( const QString& fileName ) {
	m_url.setFileName( fileName );
}

void KMFDoc::setDescription( const QString& desc ) {
	if ( desc.isNull() )
		return ;
	m_description = desc;
	changed();
}

void KMFDoc::setName( const QString& name ) {
	if ( name.isNull() )
		return ;
	m_name = name;
	changed();
}

void KMFDoc::saved() {
	is_saved = true;
}

bool KMFDoc::isSaved() {
	return is_saved;
}

void KMFDoc::changed() {
	is_saved = false;
}

void KMFDoc::updateView() {
	kdDebug() << "void KMFDoc::upateView()" << endl;
}


void KMFDoc::startTransaction() {
	kdDebug() << "void KMFIPTDoc::startTransaction()" << endl;
	m_changed_objects.clear();
	m_in_transaction = true;
}


void KMFDoc::endTransaction() {
	kdDebug() << "void KMFDoc::endTransaction()" << endl;
	m_in_transaction = false;
	if ( ! m_changed_objects.empty() ) {
		QValueList<int> del_ids;
// 		kdDebug() << "Starting to remove obsolete undo ids..." << endl;
		QValueList<int>::iterator it;
		for ( it = m_changed_objects.begin(); it != m_changed_objects.end(); ++it ) {
			NetfilterObject *obj = NetfilterObject::getObjectsDict() ->find( *it );
			QValueList<int>::iterator it2;
			bool remove = false;
			for ( it2 = m_changed_objects.begin(); it2 != m_changed_objects.end(); ++it2 ) {
				if ( obj->isChildOf( *it2 ) ) {
					obj->undoSaveState();
					remove = true;
				}
			}
			if ( remove )
				del_ids.append( *it );
		}

		for ( it = del_ids.begin(); it != del_ids.end(); ++it ) {
			QValueList<int>::Iterator rem_it = m_changed_objects.find( *it );
			if ( rem_it != m_changed_objects.end() )
				m_changed_objects.remove( rem_it );
		}
// 		kdDebug() << "Finished remove of obsolete undo ids." << endl;
		m_undo_transactions.append( m_changed_objects );
	}
	QString nums = "";

	QValueList<int>::iterator it;
	for ( it = m_changed_objects.begin(); it != m_changed_objects.end(); ++it ) {
		QString num = "";
		nums.append( num.setNum( *it ) + "," );
	}
	while ( m_undo_transactions.count() > KMFDoc::maxUndo() )
		m_undo_transactions.pop_front();

	if ( m_undo_transactions.empty() ) {
		emit sigEnableUndo ( false );
		emit sigEnableRedo ( false );
	} else {
		emit sigEnableUndo ( true );
	}

// 	kdDebug() << "\nKMFDoc: Storing " << m_undo_transactions.count() << " undo transactions." << "\n" << endl;
	QValueList< QValueList<int> >::iterator it2;
	for ( it2 = m_undo_transactions.begin(); it2 != m_undo_transactions.end(); ++it2 ) {
		QValueList<int> undos = *it2;
		QValueList<int>::iterator it3;
		QString ids = "";
		for ( it3 = undos.begin(); it3 != undos.end(); ++it3 ) {
			QString num = "";
			ids.append( num.setNum( *it3 ) );
			ids.append( ", " );
		}
// 		kdDebug() << "Sored Undo Transaction: " << ids << endl;
	}
}

void KMFDoc::changed( int id ) {
	// 	kdDebug() << "void KMFIPTDoc::changed( int id )" << endl;
	if ( ! m_in_transaction ) {
		// 		kdDebug() << "WARNING: Not recording Changes. NO UNDO FOR THIS CHANGES" << endl;
		return ;
	}

	is_saved = false;
	int index = m_changed_objects.findIndex( id );
	if ( index == -1 ) {
// 		kdDebug() << "Adding ID: " << id << " to m_changed_objects" << endl;
		m_changed_objects.append( id );
	}
}

void KMFDoc::undo() {
	kdDebug() << "void KMFIPTDoc::undo()" << endl;
	if ( m_undo_transactions.empty() ) {
// 		kdDebug() << "No undo transactions available" << endl;
		emit sigEnableUndo ( false );
		return ;
	}

	QValueList<int>& id_list = m_undo_transactions.back();
	if ( ! id_list.empty() ) {
// 		kdDebug() << "Undo ID List Not Empty" << endl;
		m_redo_transactions.append( id_list );
		QString ids = "";
		QValueList<int>::iterator it;
		for ( it = id_list.begin(); it != id_list.end(); ++it ) {
			int obj_id = *it;
			ids.append( ids.setNum( obj_id ) );
			ids.append( ", " );
			NetfilterObject *obj = 0;
			obj = NetfilterObject::getObjectsDict() ->find( obj_id );
			if ( obj ) {
				obj->undo();
			}
		}
// 		kdDebug() << "Called undo() for Objects: " << ids << endl;
		emit sigEnableUndo ( true );
	}
	m_undo_transactions.pop_back();

	if ( m_redo_transactions.empty() ) {
		emit sigEnableRedo ( false );
	} else {
		emit sigEnableRedo ( true );
	}

	if ( m_undo_transactions.empty() ) {
// 		kdDebug() << "No More undo transactions available" << endl;
		emit sigEnableUndo ( false );
	}

}

void KMFDoc::redo() {
// 	kdDebug() << "void KMFIPTDoc::redo()" << endl;
	if ( m_redo_transactions.empty() ) {
// 		kdDebug() << "No undo transactions available" << endl;
		emit sigEnableRedo ( false );
		return ;
	}

	QValueList<int>& id_list = m_redo_transactions.back();
	if ( ! id_list.empty() ) {
// 		kdDebug() << "Redo ID List Not Empty" << endl;
		m_undo_transactions.append( id_list );
		QString ids = "";
		QValueList<int>::iterator it;
		for ( it = id_list.begin(); it != id_list.end(); ++it ) {
			int obj_id = *it;
			ids.append( ids.setNum( obj_id ) );
			ids.append( ", " );
			NetfilterObject *obj = 0;
			obj = NetfilterObject::getObjectsDict() ->find( obj_id );
			if ( obj ) {
				obj->redo();
			}
		}
// 		kdDebug() << "Called redo() for Objects: " << ids << endl;
		emit sigEnableRedo ( true );
	}
	m_redo_transactions.pop_back();

	if ( m_undo_transactions.empty() ) {
		emit sigEnableUndo ( false );
	} else {
		emit sigEnableUndo ( true );
	}

	if ( m_redo_transactions.empty() ) {
// 		kdDebug() << "No More redo transactions available" << endl;
		emit sigEnableRedo ( false );
	}
}

KMFError* KMFDoc::exportXMLRuleset( const KURL& url ) {
// 	kdDebug() << "bool KMFIPTDoc::exportXMLRuleset(const QString& filename)" << endl;
	KTempFile file;
	const QString& xml = getXMLSniplet();
// 	kdDebug() << "Writing XML:\n " << xml << endl;
	if ( file.name() != QString::null ) {
		QFile f( file.name() );
		f.remove();
		bool isWriteable = f.open( IO_ReadWrite );
		if ( isWriteable ) {
			QTextStream ts( &f );
			ts << xml << endl;
			f.flush();
			f.close();
			
			
			if ( ! KIO::NetAccess::upload( file.name(), url, 0 ) ) {
				kdDebug() << "Coudn't save File: " << url.url() << endl;
				m_err->setErrType( KMFError::NORMAL );
				m_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();
				return m_err;
			}
			file.unlink();

			if (  KMFConfig::useGenericInterface() ) {
				KMFConfig::setCurrentGenericConfiguration( url.url() );
			}	 else {
				KMFConfig::setCurrentIPTConfiguration( url.url() );
			}

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

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

const QString& KMFDoc::getXMLSniplet() {
	return  *( new QString( getDOMTree().toString() ) );
}

const QString& KMFDoc::getXMLRuleset() {
	return getXMLSniplet();
}

#include "kmfdoc.moc"
