/* 
    Fam++ main header. Fampp, FamRequest, Fam_gdk are here

    Copyright (C) 2000 Ben Martin

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifndef INCLUDED_FAM_HH
#define INCLUDED_FAM_HH

#include <fam.h>

#include <stdexcept>
#include <map>

//#include <hash_map.h>	// If this is included before this header, we use it.

#ifndef __FAM_NAMESPACE
#define __FAM_NAMESPACE famppNS
#endif


#define __PRIVATE_FAM_NAMESPACE fampp_PRIVATE_NS

#include "handle.hh"
#include "FamppExceptions.hh"
#include "private/FamppEventModel.hh"
#include "FamppEvents.hh"
#include "private/FamppModel.hh"

namespace __FAM_NAMESPACE {

	// Forward decl
	class Fampp;
	void Fampp_removeRequest(Fampp* fam, FamppRequest *req);

	#ifdef SIGCXX_SIGNAL_SYSTEM_H

		typedef SigC::Signal4< void, FAMCodes, string, 
			FamppRequest*, FamppEvent* > FamppSig_t; 

	#endif

	// TODO Copy & Assignment operators.
	class FamppRequest {

		#ifdef SIGCXX_SIGNAL_SYSTEM_H
		public:
			FamppSig_t Sig;
		private:
		#endif


			friend Fampp; 
			friend void Fampp_removeRequest(Fampp* fam, FamppRequest *req);;

			Fampp* theFampp;

			FAMRequest theFAMRequest;
			FamppHandle<__PRIVATE_FAM_NAMESPACE::FamppModel> theFamppModel;

			// Stop copy by value.
			FamppRequest( const FamppRequest& ref):
				theFamppModel(ref.theFamppModel)
			{
				throw FamppUnsupportedCopyByValueException();
			}
			FamppRequest& operator=(const FamppRequest& ref) 
			{
				throw FamppUnsupportedCopyByValueException();
			}

			FamppRequest(
				Fampp* _theFampp,
				FamppHandle<__PRIVATE_FAM_NAMESPACE::FamppModel> _theFamppModel,
				string filename, 
				void* userData=NULL,
				bool isFile=false
			):
				theFampp(_theFampp),
				theFamppModel(_theFamppModel)
			{
				if(isFile) {
					if( 0 != FAMMonitorFile( 
						theFamppModel->getFAMConnection(),
						filename.c_str(),
						&theFAMRequest,
						userData)) 
					{
						throw FamppFileMonitorInitFailedException(
							filename);
					}
				}
				else {
					if( 0 != FAMMonitorDirectory( 
						theFamppModel->getFAMConnection(),
						filename.c_str(),
						&theFAMRequest,
						userData)) 
					{
						throw FamppDirMonitorInitFailedException(
							filename);
					}
				}
			}

			FAMRequest& getRequest() {
				return theFAMRequest;
			}

			/*
			*/
			void dispatch(FamppEvent& ev) {

				#ifdef SIGCXX_SIGNAL_SYSTEM_H

cerr << " emitting for name :" << ev.getFileName() << endl;

					Sig.emit( ev.getFAMEvent()->code, 
						ev.getFileName(), this, &ev );
					return;
				#endif

				if(Handlers[ev.getFAMEvent()->code]) {
					Handlers[ev.getFAMEvent()->code]->handle();
				}
			}


		protected:
			#ifdef __SGI_STL_HASH_MAP_H
				hash_map<
					FamppEvent::FamppFAMCodeType,
					abstractFamppEventHandler *> Handlers;
			#else
				map<
					FamppEvent::FamppFAMCodeType,
					abstractFamppEventHandler *> Handlers;
			#endif


		public:

			~FamppRequest() {
				if( 0 != FAMCancelMonitor(
					theFamppModel->getFAMConnection(), 
					&theFAMRequest)) 
				{
					throw FamppRequestCancelFailedException(this);
				}
				Fampp_removeRequest(theFampp,this);
			}

			template <class _Function, class _Event>
			void setHandler(_Event e, _Function f ) {
				Handlers[e.getFAMCode()] = new
					FamppEventHandlerAdaptor<_Function,
					FamppEventHandler<_Event>,
					_Event>
					(f,e);
			}

			void suspend() {

				if( 0 != FAMSuspendMonitor(
					theFamppModel->getFAMConnection(), 
					&getRequest())) 
				{
					throw FamppSuspendFailedException(this);
				}
			}

			void resume() {

				if( 0 != FAMResumeMonitor(
					theFamppModel->getFAMConnection(), 
					&getRequest())) 
				{
					throw FamppResumeFailedException(this);
				}
			}



	};






	class Fampp {

			friend class FamppRequest;	// getFAMConnection
			friend void Fampp_removeRequest(Fampp* fam, FamppRequest *req);;

			// This is where the Event's data is stored. All events share this one class.
			FamppEvent theFamppEvent;

			FamppHandle<__PRIVATE_FAM_NAMESPACE::FamppModel> theFamppModel;

			#ifdef __SGI_STL_HASH_MAP_H
				hash_map<int, FamppRequest*> FAMppRequestsByID;
			#else
				map<int, FamppRequest*> FAMppRequestsByID;
			#endif

			FamppRequest* getNewFamppRequest(
				string filename, 
				void* userData=NULL,
				bool isFile=false) 
			{
				return new FamppRequest(this,theFamppModel,filename,userData,isFile);
			}

			FAMConnection* getFAMConnection() {
				return theFamppModel->getFAMConnection();
			}



		public:
			Fampp():
				theFamppModel(new __PRIVATE_FAM_NAMESPACE::FamppModel()) 
			{
			}

			Fampp(const string appName): 
				theFamppModel(new __PRIVATE_FAM_NAMESPACE::FamppModel(appName)) 
			{
			}

			~Fampp() {
			}

			int getFD() {
				return theFamppModel->getFD();
			}


////////

/*			
			#ifdef SIGCXX_SIGNAL_SYSTEM_H
			private:
				SigChanged_t SigChanged;
				SigDeleted_t SigDeleted;

				typedef vector<FAMRequest*> FAMRequests_t;
				FAMRequests_t FAMRequests;

				#ifdef __SGI_STL_HASH_MAP_H
					typedef hash_map<int, FAMRequest*> SigRequestsByID_t;
				#else
					typedef map<int, FAMRequest*> SigRequestsByID_t;
				#endif
				SigRequestsByID_t SigRequestsByID;

			public:
				FamppRequest* SigMonitorDirectory( 
					const string& fn,
					const void* udata = 0
					)
					throw 
						(FamppDirMonitorInitFailedException)
				{
					FAMRequest* req = new FAMRequest();
					FAMRequests.push_back(req);

					if( 0 != FAMMonitorDirectory( 
						theFamppModel->getFAMConnection(),
						fn.c_str(), req, udata)) 
					{
						throw FamppDirMonitorInitFailedException(fn);
					}

					SigRequestsByID[
						FAMREQUEST_GETREQNUM( req )] = req;
				}


			#endif
*/


 			FamppRequest* MonitorDirectory(
				string filename, 
				void* userData=NULL) 
				throw 
					(FamppDirMonitorInitFailedException)
			{
				FamppRequest* ret = getNewFamppRequest(
					filename,
					userData,
					false
					);
				FAMppRequestsByID[
					FAMREQUEST_GETREQNUM(
						(&ret->getRequest()))] = ret;

				return ret;
			}

			FamppRequest* MonitorFile(
				string filename, 
				void* userData=NULL) 
				throw 
					(FamppFileMonitorInitFailedException)
			{
				FamppRequest* ret = getNewFamppRequest(
					filename,
					userData,
					true
					);
				FAMppRequestsByID[
					FAMREQUEST_GETREQNUM(
						(&ret->getRequest()))] = ret;
				return ret;
			}

////////
			void NextEvent() {
	
				if( 0 > FAMNextEvent(
					getFAMConnection(), 
					theFamppEvent.getFAMEvent())) 
				{
					throw FamppNextEventFailedException();
				}

/*
				#ifdef SIGCXX_SIGNAL_SYSTEM_H
					FAMRequest* req = SigRequestsByID[
						FAMREQUEST_GETREQNUM(
							&theFamppEvent.getFAMEvent()->fr)];


				#endif
*/

				FamppRequest *req = FAMppRequestsByID[
					FAMREQUEST_GETREQNUM(
						&theFamppEvent.getFAMEvent()->fr)];
	
				if(req) {
					req->dispatch(theFamppEvent);
				}

			}

			int Pending() {
				return FAMPending(getFAMConnection());
			}
	};
	

/////
// Version for gdk automatic callbacks.
/////
	#ifdef __GDK_H__
	static void Fampp_gdk_callback (gpointer data,
                                  	gint source_fd,
                                  	GdkInputCondition condition) 
	{
		Fampp* theFampp = (Fampp*)data;
		while(theFampp->Pending()) {
			theFampp->NextEvent();
		}
	}

	class Fampp_gdk : public Fampp {

			bool alreadyRegistered;

		public:
			Fampp_gdk( bool register_gdk = false ):
				Fampp(),
				alreadyRegistered(false)
			{
				if(register_gdk) {
					registerCallback();
				}
			}

			Fampp_gdk(const string appName): 
				Fampp(appName)
			{}

			~Fampp_gdk() {
			}
			
			void registerCallback() {
				if( !alreadyRegistered ) {
					gdk_input_add( getFD(), 
						GdkInputCondition(GDK_INPUT_READ|GDK_INPUT_EXCEPTION),
//						GdkInputCondition(GDK_INPUT_READ|GDK_INPUT_WRITE|GDK_INPUT_EXCEPTION),
						Fampp_gdk_callback, 
						this
					);
					alreadyRegistered = true;
				}
			}

	};
	extern 	FamppHandle<Fampp_gdk> getInstance_gnome_and_wait();

	
	#endif

/*
	//
	// gcc 2.96 doesn't like this function!
	//
	//
	// The catch 22 is thus: 
	//	The returned FamppHandle<> must be a copy of a static handle so that the created object
	//	is only destroyed once, and only when it should happen.
	//
	//	We can not have a NULL handle. 
	//
	//	So we create the object first and then if there is no error doing the creation then we create the handle.
	//
	//	The can_never_happen exception is only needed to stop the compiler from worrying.
	//
	template <class FAM_T = Fampp>
	FamppHandle<FAM_T> getInstance(FAM_T* ft = 0) {
		static FAM_T* s = 0;
		FAM_T* ts = 0;
		
		if( !s ) {
			FAM_T* ts = new FAM_T();
			s = ts;
		}

		// Only create the handle if the new operation worked ok.
		static FamppHandle<FAM_T> hs(s);
		return hs;
	}
*/
	

	
	
};

#endif /* INCLUDED_FAM_HH */
