/* Agent Tcl
   Bob Gray
   27 July 1995

   tclTool.cc

   This file implements the library routines that forcibly terminate an agent
   and that gets information about an agent.

   Copyright (c) 1995, Robert S. Gray Dartmouth College

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

#include "platPorting.h"
#include "platTimeval.h"
#include "agentAgentId.h"	// AgentId 
#include "mesgMachId.h"	// MachineId 
#include "suppDString.h"		// DynamicString
#include "genMessage.h"

/* --------------------------------------------------------- */

#include "tcl.h"
#include "agentTcl.h"
#include "agentTclInt.h"

/* Agent_InfoNames

   Purpose: Get all of the symbolic names registered with a server

     Input: interp  = the current interpreter
            seconds = the timeout interval
            machine = the server

    Output: The procedure returns NULL and sets the interpreter result to an
            appropriate error message on error.  Otherwise the procedure
            returns a dynamically allocated NAME_LIST structure that contains
            all of the symbolic names registered with the server.
*/

NAME_LIST *Agent_InfoNames (Tcl_Interp *interp, double seconds, MachineId *machine)
{
    int code;
    NAME_LIST *list;
    AGENT_TCL *agent;
    struct timeval delay;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (seconds >= 0.0);
    assert (machine != NULL); 

	/* AGENT structure */

    agent = AGENT_TCL::get_agent (interp);
    assert (agent != NULL);

	/* timeout */

    delay = TimevalUtil::secondsToTimeval (seconds);

	/* get the list of names */

    if ((code = agent -> agentGetNames (delay, *machine, list)) == AGENT_OK) {
	return (list);
    }

	/* error so construct error code and error message */

    Tcl_SetErrorCode (interp, (agent -> agentCodeToShortString(code)).value(), (char *) NULL);
    Tcl_SetResult (interp, agent -> agentCodeToLongString (code, machine -> getName()).value(), TCL_VOLATILE);
    return ((NAME_LIST *) NULL);
}

/* Agent_InfoIds

   Purpose: Get the numeric id of every agent registered with a server

     Input: interp  = the current interpreter
            seconds = the timeout interval
            machine = the server

    Output: The procedure returns NULL and sets the interpreter result to an
            appropriate error message on error.  Otherwise the procedure
            returns a dynamically allocated ID_LIST structure that contains
            the numeric id of every agent registered with the server.
*/

ID_LIST *Agent_InfoIds (Tcl_Interp *interp, double seconds, MachineId *machine)
{
    int code;
    ID_LIST *list;
    AGENT_TCL *agent;
    struct timeval delay;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (seconds >= 0.0);
    assert (machine != NULL); 

	/* AGENT structure */

    agent = AGENT_TCL::get_agent (interp);
    assert (agent != NULL);

	/* timeout */

    delay = TimevalUtil::secondsToTimeval (seconds);

	/* get the list of names */

    if ((code = agent -> agentGetIds (delay, *machine, list)) == AGENT_OK) {
	return (list);
    }

	/* error so construct error code and error message */

    Tcl_SetErrorCode (interp, (agent -> agentCodeToShortString(code)).value(), (char *) NULL);
    Tcl_SetResult (interp, agent -> agentCodeToLongString (code, machine -> getName()).value(), TCL_VOLATILE);
    return ((ID_LIST *) NULL);
} 

/* Agent_Info

   Purpose: Get information about a particular agent

     Input: interp  = the current interpreter
		      (struct Tcl_Interp *)

            seconds = the timeout interval
		      (double)

	    id      = the agent identification
		      (struct AgentId *)

    Output: The procedure returns NULL and sets the interpreter result to an
            appropriate error message on error.  Otherwise the procedure
            returns a dynamically allocated AGENT_INFO structure that contains
            the agent information.
*/

AGENT_INFO *Agent_Info (Tcl_Interp *interp, double seconds, AgentId *id)
{
    int code;
    AGENT_TCL *agent;
    AGENT_INFO *info;
    struct timeval delay;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (seconds >= 0.0);
    assert (id != NULL);

	/* AGENT structure */

    agent = AGENT_TCL::get_agent (interp); 
    assert (agent != NULL);

	/* timeout */

    delay = TimevalUtil::secondsToTimeval (seconds);

	/* get the information */

    if ((code = agent -> agentGetInfo (delay, id, info)) == AGENT_OK) {
	return (info);
    }

	/* error so construct error code and error message */

    Tcl_SetErrorCode (interp, (agent -> agentCodeToShortString(code)).value(), (char *) NULL);
    Tcl_SetResult (interp, agent -> agentCodeToLongString (code, id -> getServerName()).value(), TCL_VOLATILE);
    return ((AGENT_INFO *) NULL);
}

/* Agent_Force

   Purpose: Forcibly terminate an agent

     Input: interp  = the Tcl interpreter
		      (struct Tcl_Interp *)

            seconds = number of seconds until timeout
		      (double)

            id      = agent identification
		      (struct AgentId *)
 
    Output: The procedure returns TCL_ERROR and sets the intrepreter result to 
            an appropriate error message on error.  Otherwise the procedure
            returns TCL_OK and sets forcedId to (1) NULL if there was no agent
            with the given id or (2) a dynamically allocated AgentId
            structure that contains the id of the forced agent.
*/

int Agent_Force (Tcl_Interp *interp, double seconds, AgentId *id, struct AgentId **forcedId)
{
    int code;
    AGENT_TCL *agent;
    struct timeval delay;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (seconds >= 0.0);
    assert (id != NULL);
    assert (forcedId != NULL);

	/* AGENT structure */

    agent = AGENT_TCL::get_agent (interp);
    assert (agent != NULL);
 
	/* timeout */

    delay = TimevalUtil::secondsToTimeval (seconds);

	/* force the agent */

    if ((code = agent -> agentForce (delay, id, *forcedId)) == AGENT_OK) {
	return TCL_OK;
    } 

    	/* error so construct an error code and error message */

   Tcl_SetErrorCode (interp, (agent -> agentCodeToShortString(code)).value(), (char *) NULL);
   Tcl_SetResult (interp, agent -> agentCodeToLongString (code, id -> getServerName()).value(), TCL_VOLATILE);
   return TCL_ERROR;
}

/* Agent_Status

   Purpose: Check for various conditions such as whether an agent exists

     Input: interp       = the Tcl interpreter
		           (struct Tcl_Interp *)

	    seconds      = maximum time to wait for server contact
			   (double)

	    type         = STATUS_EXISTS
			   (int)

	    provideEvent = 1 if we want a notification event
			   (int)

	    id           = agent identification
			   (class AgentId *)

    Output: The procedure returns TCL_ERROR and sets the interpreter result
	    to an appropriate error message on error.  Otherwise the 
	    procedure returns TCL_CONTINUE if the agent does not exist but
	    we will get a notification event, TCL_BREAK if the agent does
	    not exist and we will *not* get a notification event, and TCL_OK
	    if the agent does exist.
*/

int Agent_Status (Tcl_Interp *interp, double seconds, int type, int provideEvent, AgentId *id)
{
    int rc;
    AGENT_TCL *agent;
    struct timeval delay;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (seconds >= 0.0);
    assert (type == STATUS_EXISTS);
    assert (id != NULL);

	/* AGENT */

    agent = AGENT_TCL::get_agent (interp);
    assert (agent != NULL);

	/* timeout */

    delay = TimevalUtil::secondsToTimeval (seconds);

	/* check */

    if ((rc = agent -> agentStatus (delay, type, provideEvent, id)) == AGENT_OK) {
	return TCL_OK;
    } else if (rc == AGENT_PENDING_EVENT) {
	return TCL_CONTINUE;
    } else if (rc == AGENT_NONEXISTENT) {
	return TCL_BREAK;
    }

	/* error so construct error code and error message */

    Tcl_SetErrorCode (interp, (agent -> agentCodeToShortString(rc)).value(), (char *) NULL);
    Tcl_SetResult (interp, agent -> agentCodeToLongString (rc, id -> getServerName()).value(), TCL_VOLATILE); 
    return TCL_ERROR;
}
