/* Agent Tcl
   Bob Gray
   10 January 1996

   tclConversion.cc

   This file implements various conversion routines.

   Copyright (c) 1995-1996, 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 NO_PRAGMAS
#pragma implementation
#endif

#include "platPorting.h"
#include "mesgTcpip.h"
#include "agentAgentId.h"	// AgentId
#include "genConversion.h"
#include "tclConversion.h"
#include "tclMasks.h"
#include "tcpipTcl.h"

#ifdef FIX_LATER

/*
 * value of DEFAULT_MASK_SECONDS should be under the control of the agent
 * programmer (i.e., it should not be a fixed constant)
 */

#endif

#define DEFAULT_MASK_SECONDS	20

/* Agent_SplitAgentId

   Purpose: Split apart a string that contains an agent identification

     Input: interp = Tcl interpreter
            string = the list

    Output: The procedure returns NULL on error.  Otherwise the procedure
            returns a pointer to a dynamically allocated AgentId structure
            that contains the agent identification.
*/

AgentId *Agent_SplitAgentId (Tcl_Interp *interp, char *string)
{
  UINT_32 ip;               /* IP number                         */
  AgentId *id = NULL;       /* agent identification              */
  int listArgc;             /* number of components in the id    */
  char **listArgv = NULL;   /* components of the id              */
  int number;               /* scratch area for numer conversion */

  if (Tcl_SplitList (interp, string, &listArgc, &listArgv) == TCL_OK) {

    if (listArgc == 2) {

      if (tcpip_stringToIP (listArgv[0], ip) != e_TCPIP_OK) {
        ip = UNKNOWN_IP;
      }

      if (Tcl_GetInt (interp, listArgv[1], &number) != TCL_OK) {
        id = new AgentId (listArgv[0], ip, listArgv[1], 0); 
      } else if (number >= 0) {
        id = new AgentId (listArgv[0], ip, NULL, number);
      }

      Tcl_ResetResult (interp);

    } else if (listArgc == 4) {

      if (tcpip_stringToIP (listArgv[1], ip) == e_TCPIP_OK) {
        if ((Tcl_GetInt (interp, listArgv[3], &number) == TCL_OK) && (number >= 0)) {
          id = new AgentId (listArgv[0], ip, listArgv[2], number);
        }
      }
    }
  }

  if (id == NULL) {
    Tcl_AppendResult
      (interp, ": agent identification must be {server name}, {server id} or {server IP name id}", (char *) NULL);
  }

  if (listArgv != NULL) {
    free (listArgv);
  }

  return id;  
}

#ifdef TCL8

AgentId *Agent_SplitAgentIdObj (Tcl_Interp *interp, Tcl_Obj *agentIdObject)
{
    UINT_32 ip;
    int length;
    int listObjc;
    int numericId;
    char *ipString;
    char *nameString;
    char *serverString;
    Tcl_Obj **listObjv;
    AgentId *agentId = (AgentId *) NULL;

	// get the elements of the AgentId list

    if (Tcl_ListObjGetElements ((Tcl_Interp *) NULL, agentIdObject,
	    &listObjc, &listObjv) == TCL_OK) {

	    // we should have either two elements, e.g., {server agent-id},
	    // or four elements, e.g., {server server-ip agent-name agent-id}

	serverString = Tcl_GetStringFromObj (listObjv[0], &length);

	if (listObjc == 2) {

	    if (tcpip_stringToIP (serverString, ip) != e_TCPIP_OK) {
		ip = UNKNOWN_IP;
	    }

	    if (Tcl_GetIntFromObj ((Tcl_Interp *) NULL, listObjv[1], &numericId)
		    != TCL_OK) {

		nameString = Tcl_GetStringFromObj (listObjv[1], &length);
		agentId    = new AgentId (serverString, ip, nameString, 0);

	    } else if (numericId >= 0) {
		    
		agentId = new AgentId (serverString, ip, (char *) NULL, numericId);
	    }

	} else if (listObjc == 4) {

	    ipString = Tcl_GetStringFromObj (listObjv[1], &length);

	    if (tcpip_stringToIP (ipString, ip) == e_TCPIP_OK) {
	        if ((Tcl_GetIntFromObj ((Tcl_Interp *) NULL, listObjv[3],
		  	&numericId) == TCL_OK) && (numericId >=0)) {
		    nameString = Tcl_GetStringFromObj (listObjv[2], &length);
		    agentId = new AgentId 
			(serverString, ip, nameString, numericId);
		}
	    }
	}
    }


	// record an error message if we were not able to construct the
	// AgentId instance

    if (agentId == (AgentId *) NULL) {
	Tcl_AppendToObj (Tcl_GetObjResult(interp),
	    "agent identification must be {server name}, {server id} or {server IP name id}", -1);
    }

    return (agentId);
}

#endif

/* Agent_SplitAgentMaskEntry

   Purpose: Split apart a string that contains a mask entry

     Input: interp = Tcl interpreter
		     (struct Tcl_Interp *)

            string = the string
		     (char *)

    Output: The procedure returns NULL on error.  Otherwise the procedure
            returns a pointer to a dynamically allocated AgentMaskEntry structure
            that contains the mask entry.
*/

enum flagTypes {TAG = 0, EVENT = 1, EVENT_ALT = 2, INTERRUPT = 3, DUMMY = 4};

static char *flags[] = {
	"-tag",
	"-event",
	"-handler",
	"-interrupt"
};

static BOOLEAN isAnyString (char *string)
{
    if (!strcmp(string, "ANY") || (*string == '\0')) {
	return (e_TRUE);
    }

    return (e_FALSE);
}

static int parseId (Tcl_Interp *interp, UINT_32 &id, char *idString)
{
    int number;   /* scratch area for number conversion */

    if (isAnyString (idString)) {

	id = ANY_ID;

    } else {

	if ((Tcl_GetInt(interp, idString, &number) != TCL_OK) || (number < 0)) {
	    Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
	    Tcl_AppendResult (interp, ": invalid numeric id \"", idString, "\"", (char *) NULL);
	    return TCL_ERROR; 
	} 

	id = (UINT_32) number;
    }

    return TCL_OK;
}

static int findIp (Tcl_Interp *interp, UINT_32 &ip, char *server)
{
    if (isAnyString (server)) {

	ip = ANY_IP;

    } else {

	if (Tcpip_Getip (interp, DEFAULT_MASK_SECONDS, server, &ip) != TCL_OK) {
	    return TCL_ERROR; 
	}
    }

    return TCL_OK;
}


AgentMaskEntry *Agent_SplitAgentMaskEntry (Tcl_Interp *interp, char *string)
{
    int i, j;                    /* loop counters                      */
    flagTypes k;   		 /* loop counter                       */
    int listArgc = 0;            /* number of components in the entry  */
    char **listArgv = NULL;      /* components of the entry            */
    AgentId *maskId = NULL;
    MASK_HANDLER_TCL *maskHandler = NULL;
    AgentMaskEntry *mask = NULL;     /* a mask entry                       */
    char *server = ANY_SERVER;   /* name of the server                 */
    UINT_32 ip = ANY_IP;         /* ip of the server                   */
    char *name = ANY_NAME;       /* name of the agent                  */
    UINT_32 id = ANY_ID;         /* id of the agent                    */
    AgentMaskHandler::HandlerTypes type = AgentMaskHandler::EVENT_HANDLER;    /* EVENT_HANDLER or INTERRUPT_HANDLER */
    char *tag = NULL;		 /* event tag			       */
    char *handler = NULL;        /* handler name                       */
    int tagFound = FALSE;        /* TRUE if -tag flag is present       */
    int handlerFound = FALSE;    /* TRUE if -event or -interrupt flag  */
				 /* is present                         */

	/* split the string into its components */

    if ((Tcl_SplitList (interp, string, &listArgc, &listArgv) != TCL_OK)) {
	Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
	Tcl_AppendResult (interp, "must be a valid Tcl list", (char *) NULL);
	goto cleanup;
    }

	/* check for -tag, -event and -interrupt flags */

    for (i = listArgc - 2, j = listArgc - 1; i >= 0; i -= 2, j -= 2) {

	for (k = TAG; k <= INTERRUPT; k = (flagTypes) (k + 1)) {
	    if (!strcmp(listArgv[j], flags[k])) {
		Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
		Tcl_AppendResult (interp, "misplaced \"", listArgv[j], "\"", (char *) NULL);
	        goto cleanup;
	    }
	}

	for (k = TAG; k <= INTERRUPT; k = (flagTypes) (k + 1)) {
	    if (!strcmp(listArgv[i], flags[k])) {
		break;
	    }
	}

	if (k == TAG) {

	    if (tagFound) {
		Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
		Tcl_AppendResult (interp, "can not have multiple \"-tag\" parameters", (char *) NULL);
		goto cleanup;
	    } else {
		tag = listArgv[j];
		tagFound = TRUE;
	   }

	} else if ((k == EVENT) || (k == EVENT_ALT) || (k == INTERRUPT)) {

	    if (handlerFound) {
		Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
		Tcl_AppendResult (interp, "must have only one \"-event\", \"-handler\" or \"-interrupt\" parameter", (char *) NULL);
		goto cleanup;
	    } else {
		type = (k == INTERRUPT) ? AgentMaskHandler::INTERRUPT_HANDLER : AgentMaskHandler::EVENT_HANDLER;
		handler = listArgv[j];
		handlerFound = TRUE;
	    }

	} else {

	    break;

	}
    }
   
    listArgc = j + 1;

        /* the id can be either "default" or a standard id list */

    if ((listArgc < 1) || (listArgc > 4)) {

	    /* format is wrong */

	Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
        Tcl_AppendResult (interp, "must be \"SERVER IP NAME ID ?-tag TAG? ?-event HANDLER | -interrupt HANDLER?\"", (char *) NULL);
	goto cleanup;

    } else if (!strcmp (listArgv[0], "default")) {

	    /* we have a "default" id */ 

	if (listArgc != 1) {
	    Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
	    Tcl_AppendResult (interp, "\"default\" is a complete id", (char *) NULL);
	    goto cleanup;
	} 

	if (!handlerFound) {
	    Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
	    Tcl_AppendResult (interp, "\"default\" must appear with an \"-interrupt\" or \"-event\" parameter", (char *) NULL);
	    goto cleanup;
	}

    } else {

	    /* we have a normal id */

	if (listArgc == 4) {

		/* four arguments means we have a server name and IP ... */

	    if (isAnyString (listArgv[0])) {

		if (!isAnyString (listArgv[1])) {
		    if (tcpip_stringToIP (listArgv[1], ip) != e_TCPIP_OK) {
			goto cleanup;
		    }
		}

	    } else {

		server = listArgv[0];

		if (isAnyString (listArgv[1])) {

		    if (findIp (interp, ip, server) != TCL_OK) {
			goto cleanup;
		    }

		} else {

		    if (tcpip_stringToIP (listArgv[1], ip) != e_TCPIP_OK) {
			goto cleanup;
		    }
		}
             }
	    
		/* and a symbolic name ... */

	    if (!isAnyString (listArgv[2])) {
		name = listArgv[2];
	    }

		/* and a numeric id */

	    if (parseId (interp, id, listArgv[3]) != TCL_OK) {
		goto cleanup;
	    }

	} else if (listArgc == 3) {

	 	/* three arguments means a server name ... */

	    server = listArgv[0];

	    if (findIp (interp, ip, server) != TCL_OK) {
		goto cleanup;
	    } 

		/* and a symbolic name ... */

	    if (!isAnyString (listArgv[1])) {
		name = listArgv[1];
	    }

		/* and a numeric id */

	    if (parseId (interp, id, listArgv[2]) != TCL_OK) {
		goto cleanup;
	    }

	} else if (listArgc == 2) {

		/* two arguments means a server name ... */

	    server = listArgv[0];

	    if (findIp (interp, ip, server) != TCL_OK) {
		goto cleanup;
	    }

		/* and either a name or numeric id */

	    if (parseId (interp, id, listArgv[1]) != TCL_OK) {
		Tcl_ResetResult (interp);
	 	name = listArgv[1];
	    }

	} else /* if (listArgc == 1) */ {
  
		/* one argument means that we better have "ANY" */

	    if (!isAnyString (listArgv[0])) {
		Tcl_SetErrorCode (interp, "SYNTAX", (char *) NULL);
		Tcl_AppendResult (interp, "incomplete agent identification", (char *) NULL);
		goto cleanup;
	    }
	} 

	    /* the agent identification */

	maskId = new AgentId (server, ip, name, id);
    }

	/* construct the AgentMaskEntry */
   
    if (handlerFound) {
	maskHandler = new MASK_HANDLER_TCL (type, handler);
    } 

    mask = new AgentMaskEntry (maskId, tag, maskHandler);

cleanup:

    delete (maskId);
    delete (maskHandler);
    delete (listArgv);
    return (mask);
}
