/* Agent Tcl
   Bob Gray
   27 July 1995
  
   tclToolCmd.cc

   This file implements the Tcl command that forcibly terminates 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 "mesgMachId.h"        // MachineId class
#include "genConversion.h"
#include "suppDString.h"
#include "genMessage.h"
#include "tclMakeLists.h"	// Agent_IdToTclList

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

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

/* Agent_InfoCmd

   Tcl syntax: agent_info
	       {-ids <machine> | -names <machine> | -id <identification>} 
	       [-time <seconds>]

      Purpose: Get information about the agents registered with a particular
               server

        Input: dummy  = client data (unused)
               interp = the current interpreter
               argc   = number of command arguments
               argv   = the command arguments

       Output: The procedure returns TCL_ERROR and sets the interpreter result
               to an appropriate error message on error.  Otherwise the
               procedure returns TCL_OK and sets the interpreter result to
               a string that describes the agents registered with the server.
*/

int Agent_InfoCmd (ClientData, Tcl_Interp *interp, int argc, char **argv)
{
    int i = 1;
    UINT_8 flag;
    char *string;
    char temp[48];
    double seconds;
    ID_LIST *idList;  
    AGENT_INFO *info; 
    NAME_LIST *nameList;
    AgentId *id = NULL;
    int code = TCL_ERROR;
    MachineId *machine = NULL;

	/* check the number of command arguments */

    if (argc < 2) {
	Tcl_AppendResult (interp, "wrong # of args: should be \"", argv[0], " {-ids <machine> | -names <machine> | -id <identification>} [-time <seconds>]\"", (char *) NULL);
	return TCL_ERROR;
    }

	/* parse the type of information */

    if (!strcmp(argv[i], "-ids")) {

	flag = INFO_ALL_IDS;

	if (++i >= argc) {
	    Tcl_AppendResult (interp, "\"-ids\" must be followed by a machine name", (char *) NULL);
	    return TCL_ERROR;
	}

	machine = new MachineId (argv[i], UNKNOWN_IP);

    } else if (!strcmp(argv[i], "-names")) {

	flag = INFO_ALL_NAMES;

	if (++i >= argc) {
	    Tcl_AppendResult (interp, "\"-names\" must be followed by a machine name", (char *) NULL);
	    return TCL_ERROR;
	}

	machine = new MachineId (argv[i], UNKNOWN_IP);

    } else if (!strcmp(argv[i], "-id")) {

	flag = INFO_ID;

	if (++i >= argc) {
	    Tcl_AppendResult (interp, "\"-id\" must be followed by an agent identification", (char *) NULL);
	    return TCL_ERROR;
	}
	
	if ((id = Agent_SplitAgentId (interp, argv[i])) == NULL) {
	    return TCL_ERROR;
	}

    } else {

	Tcl_AppendResult (interp, "unknown option \"", argv[i], "\": should be -ids, -names or -id", (char *) NULL);
	return TCL_ERROR;
    }
	
	/* parse the timeout */

    if (++i >= argc) {
	seconds = DEFAULT_CONNECT_SECONDS;
    } else if ((i = parseShortTimeout (interp, seconds, argc, argv, i)) < 0) {
	goto done;
    } else if (i < argc) {
	Tcl_AppendResult (interp, "extraneous arguments", (char *) NULL);
	goto done;
    }     

	/* call the appropriate procedure */

    if (flag == INFO_ALL_IDS) {

	if ((idList = Agent_InfoIds (interp, seconds, machine)) == NULL) {
	    goto done;
	}

	Tcl_ResetResult (interp);

	for (i = 0; i < idList -> count; i++) {
	    sprintf (temp, "%u", idList -> ids[i]);
	    Tcl_AppendElement (interp, temp);
	}

	delete (idList);
	code = TCL_OK;

    } else if (flag == INFO_ALL_NAMES) {

	if ((nameList = Agent_InfoNames (interp, seconds, machine)) == NULL) {
	    goto done;
	}

	Tcl_ResetResult (interp);

	for (i = 0; i < nameList -> count; i++) {
	    Tcl_AppendElement (interp, nameList -> names[i]);
	}

	delete (nameList);
	code = TCL_OK;
  
    } else {

	if ((info = Agent_Info (interp, seconds, id)) == NULL) {
	    goto done;
	}

        Tcl_ResetResult (interp);
    
	    /* append the agent identification */

	Tcl_DString dstring;
	Tcl_DStringInit (&dstring);
	Agent_IdToTclList (&(info -> id), &dstring);
	Tcl_AppendElement (interp, Tcl_DStringValue(&dstring));
	Tcl_DStringFree (&dstring);

	    /* append the security information */

	Tcl_DStringInit (&dstring);
	Agent_SecurityToTclList (&(info -> security), &dstring);
	Tcl_AppendElement (interp, Tcl_DStringValue(&dstring));
	Tcl_DStringFree (&dstring);

	    /* append the actual location */

	string = Agent_MachineToString (&info -> actual);
	Tcl_AppendElement (interp, string);
	delete (string);

	    /* append the agent and blocker pid's */

        sprintf (temp, "%d %d", info -> agentPid, info -> blockerPid);
	Tcl_AppendElement (interp, temp);

	    /* append the message, meeting and event counts */

	sprintf (temp, "%d %d %d", info -> messages, info -> meetings, info -> events);
	Tcl_AppendElement (interp, temp);
	delete (info);
	code = TCL_OK;
    }
      
done:

    delete (machine);
    delete (id);
    return (code);
}

/* Agent_ForceCmd

   Tcl syntax: agent_force identification [-time seconds]

      Purpose: Forcibly terminate an agent

        Input: dummy  = client data (unused)
		    	(ClientData)

               interp = the current interpreter
			(struct Tcl_Interp *)

               argc   = number of command arguments
			(int)

               argv   = the command arguments
			(char **)

       Output: The procedure returns TCL_ERROR and sets the interpreter
               result to an appropriate error message on error.  Otherwise
               the procedure returns TCL_OK and sets the interpreter result
               to the full identification of the forced agent.
*/

int Agent_ForceCmd (ClientData, Tcl_Interp *interp, int argc, char **argv)
{
    double seconds;
    AgentId *id = NULL;
    int code = TCL_ERROR;
    AgentId *forcedId = NULL;

	/* check the number of arguments */

    if ((argc != 2) && (argc != 4)) {
	Tcl_AppendResult (interp, "wrong # of arguments: should be \"", argv[0], " identification ?-time seconds?\"", (char *) NULL);
	goto cleanup;
    }

	/* get the arguments */

    if ((id = Agent_SplitAgentId (interp, argv[1])) == NULL) {
	goto cleanup;
    } else if (argc == 2) {
	seconds = DEFAULT_CONNECT_SECONDS;
    } else if (parseShortTimeout (interp, seconds, argc, argv, 2) < 0) {
	goto cleanup;
    } 

	/* forcibly terminate the agent */

    if (Agent_Force (interp, seconds, id, &forcedId) != TCL_OK) {
	goto cleanup;
    } else if (forcedId == NULL) {
	Tcl_SetResult (interp, "-1", TCL_STATIC);
	code = TCL_OK;
    } else {
	Tcl_DString dstring;
	Tcl_DStringInit (&dstring);
	Agent_IdToTclList (forcedId, &dstring);
	Tcl_DStringResult (interp, &dstring);
	Tcl_DStringFree (&dstring);
	code = TCL_OK;
    }

cleanup:

    delete (forcedId);
    delete (id);
    return (code);
}

/* Agent_StatusCmd

   Tcl syntax: agent_status exists <id> [-event] [-time seconds]

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

        Input: dummy  = client data (unused)
		        (ClientData)
	
	       interp = the Tcl interpreter
		        (struct Tcl_Interp *)

	       argc   = the number of command arguments
			(int)

	       argv   = the command arguments
 		 	(char **)

       Output: The procedure returns TCL_ERROR and sets the interpreter
	       result to an appropriate error message on error.  Otherwise
	       the procedure returns TCL_OK and sets the interpreter result
	       to one of the following.

	       1. 0 if the agent does not exist

	       2. 1 if the agent does exist

	       3. An event tag if the agent does not exist but the server will
		  send a notification event when the agent comes into
		  existence. 

	       The server will only provide a notification event if the 
	       "-event" parameter is specified and the agent is specified 
	       by symbolic name.
*/

int Agent_StatusCmd (ClientData, Tcl_Interp *interp, int argc, char **argv)
{
    int i;
    int type;
    AgentId *id = NULL;
    int code = TCL_ERROR;
    int provideEvent = FALSE;
    double seconds = DEFAULT_CONNECT_SECONDS;

	/* assertions on the parameters */

    assert (interp != NULL);
    assert (argv != NULL);

	/* check the number of parameters */

    if (argc <= 2) {
	Tcl_AppendResult (interp, "wrong # of args: should be \"", argv[0], " exists <id> [-event] [-time seconds]", (char *) NULL);
	goto cleanup;
    } 

	/* check the we have the "exists" option */

    argc -= 1;
    argv += 1;

    if (!strcmp(argv[0], "exists")) {
	type = STATUS_EXISTS;
    } else {
	Tcl_AppendResult (interp, "unknown option \"", argv[0], "\": should be one of exists", (char *) NULL);
	goto cleanup;
    }

	/* parse the agent identification */

    argc -= 1;
    argv += 1;

    if ((id = Agent_SplitAgentId (interp, argv[0])) == NULL) {
	goto cleanup;	
    }

	/* see if we are requesting an event notification */

    argc -= 1;
    argv += 1;

    if (argc != 0) {
	if (!strcmp(argv[0], "-event")) {
	    provideEvent = TRUE;
	    argc -= 1;
	    argv += 1;
   	} else if (strcmp(argv[0], "-time")) {
	    Tcl_AppendResult (interp, "unknown option \"", argv[0], "\": should be \"-event\" or \"-time\"", (char *) NULL);
	    goto cleanup;
	}
    }

	/* see if we have a timeout */
 
    if (argc != 0) {
	if ((i = parseShortTimeout (interp, seconds, argc, argv, 0)) < 0) {
	    goto cleanup;
	} else {
	    argc -= i;
	    argv += i;
	}
    }

	/* check for extraneous arguments */

    if (argc != 0) {
	Tcl_AppendResult (interp, "extraneous arguments", (char *) NULL);
	goto cleanup;
    }

	/* check */

    code = Agent_Status (interp, seconds, type, provideEvent, id);

	/* set the interpreter result on success */

    if (code == TCL_CONTINUE) {

	char *temp = new char [(id -> getName().length()) + 8];
	sprintf (temp, "exists:%s", id -> getName().value());	
	Tcl_SetResult (interp, temp, TCL_DYNAMIC);
 
    } else if (code == TCL_BREAK) {

	Tcl_SetResult (interp, "0", TCL_STATIC);

    } else if (code == TCL_OK) {

	Tcl_SetResult (interp, "1", TCL_STATIC);
    }

cleanup:

	/* done */

    code = (code == TCL_ERROR) ? TCL_ERROR : TCL_OK;
    delete (id);
    return (code);
}
