/* Agent Tcl
   Bob Gray
   10 January 1996

   genAgent.h

   This file defines the agent routines and data structures.

   Copyright (c) 1995-2000, Bob Gray, Dartmouth College

   See the file "agent.terms" for information on usage and redistribution
   of this file and for a DISCLAIMER OF ALL WARRANTIES.
*/

#ifndef _GEN_AGENT_H
#define _GEN_AGENT_H

#ifndef NO_PRAGMAS
#pragma interface
#endif

#ifndef _PLAT_PORTING_H
#include "platPorting.h"	  // assert
#endif
#ifndef _AGENT_ID_H
#include "agentId.h"		  // NUM_ITEMS
#endif
#ifndef _PLAT_EXCLUSION_H
#include "platExclusion.h"	  // MonitorLock
#endif
#ifndef _PLAT_SIGIO_H
#include "platSigio.h"		  // SigioTrap
#endif
#ifndef _PLAT_SIZES_H
#include "platSizes.h"		  // UINT_32
#endif
#ifndef _PLAT_SIGTERM_H
#include "platSigterm.h"	  // SigtermTrap
#endif
#ifndef _SUPP_DSTRING_H
#include "suppDString.h"	  // DynamicString
#endif
#ifndef _GEN_MEETING_H
#include "genMeeting.h"		  // MEET_NO_CHANGE
#endif
#ifndef _GEN_SERVER_FLAGS_H
#include "genServerInterface.h"	  // SubmissionTypes and ServerErrorcodes
#endif
#ifndef _GEN_VERSION_H
#include "genVersion.h"		  // Version
#endif
#ifndef _TRUEFALSE_H
#include "truefalse.h"		  // BOOLEAN
#endif

    /* forward declarations */

class AgentId;
class AgentIO;
class AgentConnectionProxy;
class AgentFactory;
class AGENT_INFO;
class DynamicBuffer;
class FileCallbacks;
class ID_LIST;
class INCOMING_MESSAGE;
class INCOMING_MEETING;
class INCOMING_EVENT;
class LOCATION;
class MachineId;
class NAME_LIST;
class AgentMasks;
class AgentMeetings;
class RESTRICT;
class SECURITY;
struct timeval;

    /* return codes from the agent routines */

#ifdef FIX_LATER 
    /* these error codes should be an enumerated type */
#endif

const int AGENT_BEG_GENERAL		= 0;
const int AGENT_OK			= 0;
const int AGENT_ALREADY_REG		= 1;
const int AGENT_NO_SERVER		= 2;
const int AGENT_BAD_RESPONSE	 	= 3;
const int AGENT_SERVER_ERROR	 	= 4;
const int AGENT_NOT_REG		 	= 5;
const int AGENT_PROGRAM_ERROR		= 6;
const int AGENT_TIMEOUT		 	= 7;
const int AGENT_LOST_BACKGROUND	 	= 8;
const int AGENT_NONEXISTENT		= 9;
const int AGENT_SELF		 	= 10;
const int AGENT_UNKNOWN		 	= 11;
const int AGENT_BAD_STATE		= 12;
const int AGENT_SAME		 	= 13;
const int AGENT_PENDING_EVENT	 	= 14;
const int AGENT_NO_EVENT	 	= 15;
const int AGENT_FAILED_MESSAGE	 	= 16;
const int AGENT_ENCRYPT		 	= 17;
const int AGENT_DECRYPT		 	= 18;
const int AGENT_SERVER_SOURCE	 	= 19;
const int AGENT_SERVER_INTERP	 	= 20;
const int AGENT_SERVER_SYSTEM	 	= 21;
const int AGENT_SERVER_INVALID	 	= 22;
const int AGENT_TOO_MANY_MEETINGS	= 23;
const int AGENT_SERVER_LOST		= 24;
const int AGENT_NAME_USED	 	= 25;
const int AGENT_NAME_RESERVED		= 26;
const int AGENT_BAD_MEETING		= 27;
const int AGENT_CANT_ACCEPT		= 28;
const int AGENT_UNKNOWN_MEETING		= 29;
const int AGENT_CANT_REJECT		= 30;
const int AGENT_CANT_SEND		= 31;
const int AGENT_MEET_BROKEN		= 32;
const int AGENT_CANT_RECEIVE 		= 33;
const int AGENT_NEXT_FILE		= 34;
const int AGENT_CANT_READ		= 35;
const int AGENT_NEXT_MESSAGE		= 36;
const int AGENT_CANT_WRITE		= 37;
const int AGENT_MEET_SELF		= 38;
const int AGENT_NO_JUMPS		= 39;
const int AGENT_SERVER_MAX_AGENTS	= 40;
const int AGENT_SERVER_MAX_ANON         = 41;
const int AGENT_SERVER_LIAR		= 42;
const int AGENT_SERVER_RBEGIN		= 43;
const int AGENT_SERVER_ANON_RBEGIN	= 44;
const int AGENT_SERVER_RCOMM		= 45;
const int AGENT_SERVER_RINFO		= 46;
const int AGENT_SERVER_ANON_RINFO	= 47;
const int AGENT_NO_CHILDREN		= 48;
const int AGENT_SERVER_LOAD		= 49;
const int AGENT_SERVER_TOO_MANY_EVENTS	= 50;
const int AGENT_CANT_FORCE		= 51;
const int AGENT_MEET_REJECTED		= 52;
const int AGENT_EXIT			= 53;
const int AGENT_END_GENERAL		= 53;

    /* callback class for incoming communication */

class IncomingCallback: public SigioTrap
{
private:

	/* callback */

    void sigioAlarm (int /*fd*/, int /*flags*/) {
	incomingAlarm ();
    }

protected:

	/* constructor and destructor */

    IncomingCallback (void);
    virtual ~IncomingCallback ();

	/* callback when there might be an incoming item */

    virtual void incomingAlarm (void) = 0;

public:

	/* handle incoming items */

    void handleIncomingItems (void) {
	SigioHandler::checkAndCallTraps ();
    }
};

    /* parameters to the select method */

class AgentSelectParameters {

public:

	// constructors and destructor

    AgentSelectParameters (void);
    AgentSelectParameters (const AgentSelectParameters &);
   ~AgentSelectParameters ();

	// assignment operator

    AgentSelectParameters& operator= (const AgentSelectParameters &);

	// set the maximum number of handles or fds

    void setMaximumHandles (UINT_32 slots);
    void setMaximumFds (UINT_32 slots);

	// get the current number of handles or fds

    UINT_32 getNumHandles (void) {
	return (m_numHandles);
    }

    UINT_32 getNumFds (void) {
	return (m_numFds);
    }

	// add an item, handle or fd to the monitored set

    void watchItem (UINT_8 item) {
	m_itemsIn [item] = e_TRUE;
	m_itemsOut[item] = e_FALSE;
    }

    void watchHandle (UINT_32 handle) {
	assert (m_numHandles < m_numHandleSlots);
	m_handlesIn[m_numHandles] = handle;
	m_handlesOut[m_numHandles] = MEET_NO_CHANGE;
	m_numHandles += 1;
    }
 
    void watchFd (int fd) {
	assert (m_numFds < m_numFdSlots);
	m_fdsIn[m_numFds] = fd;
	m_fdsOut[m_numFds] = e_FALSE;
	m_numFds += 1;
    }

	// see if an item, handle or fd is ready -- Note that for handles
	// and fds, you do not specify the actual handle or fd, but instead
	// its position within the list of handles.  The handle or fd is
	// returned as an output parameter.  Using the position reflects the
	// fact that the client code will almost always be iterating through
	// the entire set of handles or fds.  Note also that isHandleReady
	// returns the status of the meeting (so a return value of 
	// MEET_NO_CHANGE indicates that the meeting has not changed its
	// state).

    BOOLEAN isItemReady (UINT_8 item) {
	return (m_itemsOut[item]);
    }

    UINT_8 isHandleReady (UINT_32 i, UINT_32 &handle) {
	assert (i < m_numHandles);
	handle = m_handlesIn[i];
	return (m_handlesOut[i]);
    }

    UINT_8 isFdReady (UINT_32 i, int &fd) {
	assert (i < m_numFds);
	fd = m_fdsIn[i];
	return (m_fdsOut[i]);
    }

	// get a handle or fd (by position) or see if we are watching a
	// particular item

    UINT_32 getHandle (UINT_32 i) {
	assert (i < m_numHandles);
	return (m_handlesIn[i]);
    }

    BOOLEAN areWatchingItem (UINT_8 item) {
	return (m_itemsIn[item]);
    }

	// set the status of a handle or mark an item or fd as ready
	// (again by position)

    void setHandleStatus (UINT_32 i, UINT_8 status) {
	assert (i < m_numHandles);
	m_handlesOut[i] = status;
    }

    void markItemReady (UINT_8 item) {
	m_itemsOut[item] = e_TRUE;
    }

	// reset the output flags to all FALSE

    void resetOutputFlags (void);

private:

	// meeting handles

    UINT_32  m_numHandles;
    UINT_32  m_numHandleSlots;
    UINT_32 *m_handlesIn;
    UINT_8  *m_handlesOut;
    
	// incoming message, event, or meeting request

    BOOLEAN m_itemsIn [NUM_ITEMS];
    BOOLEAN m_itemsOut[NUM_ITEMS];
 
	// arbitrary file descriptors (most likely socket descriptors)

    UINT_32 m_numFds;
    UINT_32 m_numFdSlots;
    int *m_fdsIn;
    BOOLEAN *m_fdsOut;

	// copy the arrays from another AgentSelectParameters instance

    void copyArrays (const AgentSelectParameters&);
};

    /* an agent */

class AGENT: public SigtermTrap
{
public:

	/* 
	 * types of AGENT_END calls
	 */

    enum EndType {
	e_END_NORMAL			= 0,
	e_END_CLEANUP			= 1,
	e_END_NORMAL_BUT_NO_EXIT	= 2
    };

	/*
	 * types of AGENT_JUMP calls
	 */

    enum JumpType {
	e_JUMPING	= 0,
	e_FORKING	= 1
    };

	/* 
	 * source of the agent 
	 */

    enum AgentSource {
	e_LOCAL_AGENT	 = 0,	// agent started on the local machine
	e_INCOMING_AGENT = 1	// agent arrived from another machine 
    };

	/* 
	 * location of the agent 
	 *
	 * The meaning of the two constants is slightly different than their
	 * names indicate.  e_AT_HOME means that the agent has *never* left
	 * its home machine, i.e., the home and local identifications of the
	 * agent are the same.  e_AWAY_FROM_HOME means that the agent has
	 * left home at some point in its past.  Thus, e_AWAY_FROM_HOME
	 * does not actually indicate whether the agent is currently on or
	 * away from its home machine.
	 *
#ifdef FIX_LATER
         * Once make_root is eliminated (in favor of an optional argument to
         * submit and fork that specifies which agent should receive the child
         * agent's result), AgentSource and AgentLocation will provide the same
         * information and AgentLocation will be removed.
#endif
         */

    enum AgentLocation {
	e_AT_HOME	 = 0,	// agent has never left home 
	e_AWAY_FROM_HOME = 1	// agent has left home
    };

	/*
	 * position of the agent within the parent-child hierarchy
	 */

    enum AgentRelation {
	e_CHILD_AGENT	= 0,	// agent is a child agent
	e_ROOT_AGENT	= 1	// agent is a root agent
    };

	/*
	 * whether agent is registed with a server
	 */

    enum AgentRegister {
	e_REGISTERED	 = 0,	// agent is registered with a server
	e_NOT_REGISTERED = 1	// agent is not registered with a server 
    };

private:

	/* 
	 * lock to provide mutual exclusion 
	 */

    MonitorLock monitor;

private:

	/* convert a MESSAGE code into an AGENT code */

    int messageCodeToAgentCode 
	(int code, AgentConnectionProxy *connection);

	/* convert a SERVER code into an AGENT code */

    int serverCodeToAgentCode
	(ServerInterface::ServerErrorcodes code);

	/* send an information request */

    int agentGenericInfo 
	(UINT_8 flag, struct timeval delay, AgentId *id, void *&list);

	/* send a meeting acceptance */

    int sendAcceptanceMessage 
	(struct timeval stop, const AgentId &id, UINT_32 handle, 
	 UINT_32 remoteHandle, UINT_32 port);

	/* handle a SIGTERM */

    SigtermHandler *sigtermHandler;
    virtual void sigtermAlarm (void);	// inherited from SigtermTrap

protected:

        /* constructor and virtual destructor */

    AGENT (const Version &version, const AgentFactory &factory, AGENT::AgentSource agentSource);
    virtual ~AGENT();

	/* I/O */      

    AgentIO *io;

	/* network location of the agent */

    LOCATION *location; 

	/* "filters" for incoming messages, events and meeting requests */

    AgentMasks *masks;	

	/* pending and active meetings */

    AgentMeetings *meetings;

	/* permits */

    RESTRICT *restrict;

	/* security characteristics */

    SECURITY *security;

	/* core and interpreter versions */

    Version coreVersion;
    Version interpreterVersion;

	/* capture the current state of the agent */

    virtual int captureState 
	(JumpType jumpType, DynamicBuffer& buffer) = 0;

	/* restore the state of an agent */

    virtual int restoreState
	(ServerInterface::SubmissionTypes type, const DynamicBuffer& buffer) = 0;

public:

	/* get the MonitorLock */

    MonitorLock &getMonitorLock (void) {
	return (monitor);
    }

	/* get the I/O structure */

    AgentIO *get_io (void) const {
	return (io);
    }

	/* get network location structure */

    LOCATION *get_location (void) const { 
	return (location); 
    } 

	/* get masks structure */

    AgentMasks *get_masks (void) const { 
	return (masks); 
    } 

	/* get AgentMeetings structure */

    AgentMeetings *get_meetings (void) const {
	return (meetings);
    } 

	/* get the permit structure */

    RESTRICT *get_restrict (void) const {
	return (restrict);
    } 

	/* get the security structure */

    SECURITY *get_security (void) const {
	return (security);
    } 

	/* get the core version */

    Version getCoreVersion (void) const {
	return (coreVersion);
    }

	/* get and set the interpreter version */ 

#ifdef FIX_LATER
	/* should these be protected? */
#endif

    void setInterpVersion (Version &version) {
	interpreterVersion = version;
    }

    Version getInterpVersion (void) const {
	return (interpreterVersion);
    }

	/* get the IP address of the host */

    int agentIp (struct timeval delay, MachineId &machine); 

	/* restore an incoming agent */

    int agentRestore (void);
    int agentRestore (int fd);

	/* register an agent with the server */

    int agentBegin (struct timeval delay, const MachineId& server);

	/* remove an agent from the server tables */

    int agentEnd (struct timeval delay, enum EndType endType);

	/* change the name of the agent */

    int agentName (struct timeval delay, char *name);

	/* turn the agent into a root agent */

    int agentRoot (void);

        /* send a message to another agent or receive a message */

    int agentSendMessage 
	(struct timeval delay, const AgentId &id, UINT_32 code, char *string);

    int agentReceiveMessage 
	(struct timeval delay, INCOMING_MESSAGE *&message);

	/* request, receive and accept meetings */

    int agentRequestMeeting 
	(struct timeval delay, const AgentId &id, UINT_32 &handle);

    int agentReceiveMeeting
	(struct timeval delay, INCOMING_MEETING *& meeting);

    int agentAcceptMeeting
	(struct timeval delay, UINT_32 handle, AgentId &id);

    int agentRejectMeeting
	(struct timeval delay, UINT_32 handle, AgentId &id);

    int agentMeetingReceive
	(struct timeval delay, UINT_32 handle, UINT_32 &code, char *&string);

    int agentMeetingGetfile
	(struct timeval delay, UINT_32 handle, FileCallbacks &callbacks,
	 UINT_32 &bytes);

    int agentMeetingSend
	(UINT_32 handle, UINT_32 code, char *string);

    int agentMeetingSendfile
	(UINT_32 handle, FileCallbacks &callbacks, UINT_32 bytes);

    int agentCloseMeeting
	(UINT_32 handle);

    int agentMeet
	(struct timeval delay, const AgentId &id, UINT_32 &handle);

    int agentAccept
	(struct timeval delay, UINT_32 handle, AgentId &id);

	/* send an event to another agent or receive an event */ 

    int agentSendEvent 
	(struct timeval delay, const AgentId &id, char *tag, char *string);

    int agentReceiveEvent 
	(struct timeval delay, INCOMING_EVENT *&event);

	/* select on events, messages, meeting requests and connected */
	/* meetings                                                   */

    int agentSelect
	(struct timeval delay, AgentSelectParameters &selectParams);

#ifdef FIX_LATER
	/* There is no need to have agentSubmit, agentAgletJump and */
	/* agentAgletSubmit as separate routines ... we should be   */
	/* able to combine them into a single routine (perhaps with */
	/* the addition of one additional parameter)                */
#endif

	/* submit and jump/fork */

    int agentSubmit
	(struct timeval delay, const MachineId &machine, Version *version, 
	 const DynamicBuffer &script, AgentId *&id);

    int agentJump
	(struct timeval delay, const MachineId &machine, AgentId *&id, 
	 JumpType jumpType);

    int agentDisk
	(FileCallbacks &callbacks);

    int agentTransfer
	(struct timeval delay, const MachineId &machine, FileCallbacks &callbacks,
	 AgentId *&id); 

        /* agentAgletJump -- currently used in Agent Java only */
	/* (but could be used with other languages)            */
 
     int agentAgletJump
         (struct timeval delay, const MachineId &machine, 
	  const DynamicBuffer &state, AgentId *&id);
 
        /* agentAgletSubmit -- currently used in Agent Java only */
 	/* (but could be used with other languages)              */

     int agentAgletSubmit
         (struct timeval delay, const MachineId &machine, Version *version,
 	  const DynamicBuffer &state, AgentId *&id);

	/* forcibly terminate an agent */

    int agentForce
	(struct timeval delay, AgentId *id, AgentId *&forcedId);

	/* sleep */      

    int agentSleep 
	(struct timeval delay);

	/* agent information */

    int agentGetInfo
	(struct timeval delay, AgentId *id, AGENT_INFO *&info);

    int agentGetNames
	(struct timeval delay, const MachineId &id, NAME_LIST *&list);

    int agentGetIds 
	(struct timeval delay, const MachineId &id, ID_LIST *&list);

    int agentStatus
	(struct timeval delay, int type, int provideEvent, AgentId *id);

	/* random numbers -- cryptographically secure with help from server */

    void agentSeed
	(UINT_32 seed);

    UINT_32 agentRandom
	(void);

	/* convert an agent error code into a *short* or *long* string */

    DynamicString agentCodeToShortString 
	(int code);

    DynamicString agentCodeToLongString
	(int code, const DynamicString& name);
};

#endif    /* _GEN_AGENT_H */
