/***************************************************************************
 *                                                                         *
 *   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-2008
*/
#include "kmfundoengine.h"


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

// Project includes

#include "kmftransaction.h"
#include "kmferror.h"
#include "kmyfirewallinterface.h"
#include "kmfappstate.h"

namespace KMF {

//##########  KMFUndoEngine  ##############
//#### static stuff
KMFUndoEngine* KMFUndoEngine::m_instance = 0;
KMFUndoEngine* KMFUndoEngine::instance() {
	if ( ! m_instance ) {
		m_instance = new KMFUndoEngine( 0 , "KMFUndoEngine" );
	}
	
	return m_instance;
}
//##### end static stuff

KMFUndoEngine::KMFUndoEngine( TQObject* parent,  const char* name ) : TQObject( parent, name ) {
	MAX_UNDO = 10;
	m_in_transaction = false;
	is_saved = true;
// 	m_transactionName = "";
	m_app = 0;
}

KMFUndoEngine::~KMFUndoEngine() {}

void KMFUndoEngine::setMaxUndo ( uint num ) {
	MAX_UNDO = num;
}

// void KMFUndoEngine::setPreserveObjectUuid( bool onOff ) {
// 	// m_preserveObjectUuid = onOff;
// }

bool KMFUndoEngine::inTransaction() {
	return m_in_transaction;
};

void KMFUndoEngine::setKMFInterface( KMyFirewallInterface *app ) {
	m_app = app;
}

TQValueList< KMFTransaction* >&  KMFUndoEngine::undoTransactions() {
	return m_undo_transactionObjects;
}
	
TQValueList< KMFTransaction* >&  KMFUndoEngine::redoTransactions()  {
	return m_redo_transactionObjects;
}

KMFTransaction * KMFUndoEngine::findTransction( const TQUuid& uuid ) {
	TQValueList< KMFTransaction* >::iterator it;
	for ( it = m_undo_transactionObjects.begin(); it != m_undo_transactionObjects.end(); ++it ) {
		if ( (*it)->uuid() == uuid ) {
			return *it;
		}
	}
	for ( it = m_redo_transactionObjects.begin(); it != m_redo_transactionObjects.end(); ++it ) {
		if ( (*it)->uuid() == uuid ) {
			return *it;
		}
	}
	kdDebug() << "Not TRansaction found by uuid: " << uuid.toString() << endl;
	return 0;
}

void KMFUndoEngine::saved() {
	kdDebug() << "void KMFUndoEngine::startTransaction()" << endl;
	is_saved = true;
}

bool KMFUndoEngine::isSaved() {
	// kdDebug() << "void KMFUndoEngine::isSaved() -> " << is_saved << endl;
	return is_saved;
}

void KMFUndoEngine::log( const TQString& msg, int kmfErrorType = KMFError::NORMAL, NetfilterObject* obj = 0 ) {
	TQString m = msg;
	TQString s = "";
	if ( obj ) {
		m.prepend( i18n("<i>%1:</i> ").arg( obj->name() ) );
	}
	s.append( KMFError::getAsString( kmfErrorType, m ) );
	emit sigLog( *( new TQString(s) ) );
}

void KMFUndoEngine::changed ( const TQUuid& id ) {
	NetfilterObject* obj = NetfilterObject::findObject ( id );
	if ( ! obj ) {
		log( i18n("Change non exiting object id: %1.").arg( id ), KMFError::FATAL );
		return;
	}
	is_saved = false;
	if ( m_app && KMFAppState::upAndRunning() ) {
		m_app->updateCaption();
	}
	if ( ! m_in_transaction ) {
		// log( i18n("Change object: %1 without transaction.").arg( obj->name() ), KMFError::WARNING  );
		return;
	}
	
	NetfilterObject *highestAffected = NetfilterObject::findObject( m_currentTransaction->objectUuid() );
	if ( ! highestAffected || obj != highestAffected && ! obj->isChildOf( highestAffected->uuid() ) ) {
		log( i18n("Change object: %1 outside of declared highestAffectedObject: %2 in transaction: %3.").arg( obj->name() ).arg( highestAffected->name() ).arg( m_currentTransaction->name() ), KMFError::WARNING  );
	}
}

void KMFUndoEngine::clearStacks() {
	m_undo_transactionObjects.clear();
	m_redo_transactionObjects.clear();
	if ( m_app && KMFAppState::upAndRunning() ) {
		m_app->enableUndo ( false );
		m_app->enableRedo ( false );
	}
	emit sigStackChanged();
}

void KMFUndoEngine::startTransaction ( NetfilterObject* highestAffectedObject, const TQString& name ) {
	kdDebug() << "void KMFUndoEngine::startTransaction( const TQString& " <<  name << " )" << endl;
	m_in_transaction = true;
	m_currentTransaction = new KMFTransaction( name, highestAffectedObject );
}

void KMFUndoEngine::abortTransaction() {
	kdDebug() << "void KMFUndoEngine::abortTransaction()" << endl;
	if ( ! m_in_transaction ) {
		log( "KMFUndoEngine::abortTransaction() - No active Transaction!", KMFError::NORMAL, 0 ); 
		return;
	}
	m_in_transaction = false;
	delete m_currentTransaction;
	m_currentTransaction = 0;
}

void KMFUndoEngine::endTransaction() {
	kdDebug() << "void KMFUndoEngine::endTransaction()" << endl;
	if ( ! m_in_transaction || ! m_currentTransaction ) {
		log( "KMFUndoEngine::endTransaction() - No active Transaction!", KMFError::NORMAL, 0 );
		return;
	}
	m_currentTransaction->commit();
	m_undo_transactionObjects.append( m_currentTransaction );
	
	while ( m_undo_transactionObjects.count() > KMFUndoEngine::maxUndo() ) {
		m_undo_transactionObjects.pop_front();
	}

	if ( m_undo_transactionObjects.empty() ) {
		m_app->enableUndo ( false );
		m_app->enableRedo ( false );
	} else {
		m_app->enableUndo ( true );
	}
	
	m_in_transaction = false;
	m_currentTransaction = 0;
	emit sigStackChanged();
	return;
}


TQValueList< NetfilterObject* >&  KMFUndoEngine::undo() {
	TQValueList< NetfilterObject* > *affected = new TQValueList< NetfilterObject* >;
	
	kdDebug() << "void KMFUndoEngine::undo()" << endl;
	if ( m_undo_transactionObjects.empty() ) {
		kdDebug() << "No undo transactions available" << endl;
		m_app->enableUndo ( false );
		return *affected;
	}

	KMFTransaction* transaction = m_undo_transactionObjects.back();
	NetfilterObject *obj = transaction->undo();
	if ( obj ) {
		affected->append( obj );
	}
	m_undo_transactionObjects.pop_back();
	m_redo_transactionObjects.append ( transaction );
	
	if ( m_redo_transactionObjects.empty() ) {
		m_app->enableRedo ( false );
	} else {
		m_app->enableRedo ( true );
	}

	if ( m_undo_transactionObjects.empty() ) {
		kdDebug() << "No More undo transactions available" << endl;
		m_app->enableUndo ( false );
	}
	emit sigStackChanged();
	return *affected;
}



TQValueList< NetfilterObject* >&  KMFUndoEngine::redo() {
	kdDebug() << "void KMFIPTDoc::redo()" << endl;
	TQValueList< NetfilterObject* > *affected = new TQValueList< NetfilterObject* >;
	if ( m_redo_transactionObjects.empty() ) {
		kdDebug() << "No undo transactions available" << endl;
		m_app->enableRedo ( false );
		return *affected;
	}

	KMFTransaction* transaction = m_redo_transactionObjects.back();
	NetfilterObject *obj = transaction->redo();
	if ( obj ) {
		affected->append( obj );
	}
	
	m_redo_transactionObjects.pop_back();
	m_undo_transactionObjects.append ( transaction );
	if ( m_undo_transactionObjects.empty() ) {
		m_app->enableUndo ( false );
	} else {
		m_app->enableUndo ( true );
	}

	if ( m_redo_transactionObjects.empty() ) {
		kdDebug() << "No More redo transactions available" << endl;
		m_app->enableRedo ( false );
	}
	emit sigStackChanged();
	return *affected;
}

}
