/* 
 * cmd.c --
 *
 *	This module implements command structure lookups.
 *
 * Copyright (c) 1997,1998 Jeffrey Hobbs
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

#include "mm.h"
#include "tkPort.h"
#include "tkInt.h"
#include "tkVMacro.h"

#ifndef TCL_ONLY
/*
 * Functions for handling custom options that use Cmd_Structs
 */

int
Cmd_OptionSet(ClientData clientData, Tcl_Interp *interp,
	      Tk_Window unused, Arg value, char *widgRec, int offset)
{
  Cmd_Struct *p = (Cmd_Struct *)clientData;
  int mode = Cmd_GetValue(p,value);
  if (!mode) {
    Cmd_GetError(interp,p,value);
    return TCL_ERROR;
  }
  *((int*)(widgRec+offset)) = mode;
  return TCL_OK;
}

Arg
Cmd_OptionGet(ClientData clientData, Tk_Window unused,
	      char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
{
  Cmd_Struct *p = (Cmd_Struct *)clientData;
  int mode = *((int*)(widgRec+offset));
  return LangStringArg(Cmd_GetName(p,mode));
}

/*
 * Options for bits in an int
 * This will set/clear one bit in an int, the specific bit
 * being passed in clientData
 */
int
Cmd_BitSet(ClientData clientData, Tcl_Interp *interp,
	   Tk_Window unused, Arg value, char *widgRec, int offset)
{
  int mode;
  if (Tcl_GetBoolean(interp, value, &mode) != TCL_OK) {
    return TCL_ERROR;
  }
  if (mode) {
    *((int*)(widgRec+offset)) |= (int)clientData;
  } else {
    *((int*)(widgRec+offset)) &= ~((int)clientData);
  }
  return TCL_OK;
}

char *
Cmd_BitGet(ClientData clientData, Tk_Window unused,
	   char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
{
  return (*((int*)(widgRec+offset)) & (int) clientData)?"1":"0";
}
#endif /* TCL_ONLY */

/*
 * simple Cmd_Struct lookup functions
 */

char *
Cmd_GetName(const Cmd_Struct *cmds, int val)
{
  for(;cmds->name && cmds->name[0];cmds++) {
    if (cmds->value==val) return cmds->name;
  }
  return NULL;
}

int  
Cmd_GetValue(const Cmd_Struct *cmds, Arg arg)
{
  char *argstring = LangString(arg);
  unsigned int len = strlen(argstring);
  for(;cmds->name && cmds->name[0];cmds++) {
    if (!strncmp(cmds->name, argstring, len)) return cmds->value;
  }
  return 0;
}

void
Cmd_GetError(Tcl_Interp *interp, const Cmd_Struct *cmds, Arg arg)
{
  int i;
  char *argstring = LangString(arg);
  Tcl_AppendResult(interp, "bad option \"", argstring, "\" must be ", (char *) 0);
  for(i=0;cmds->name && cmds->name[0];cmds++,i++) {
    Tcl_AppendResult(interp, (i?", ":""), cmds->name, (char *) 0);
  }
}

/*
 * Parses a command string passed in an arg comparing it with all the
 * command strings in the command array. If it finds a string which is a
 * unique identifier of one of the commands, returns the index . If none of
 * the commands match, or the abbreviation is not unique, then it sets up
 * the message accordingly and returns 0
 */

int
Cmd_Parse(Tcl_Interp *interp, Cmd_Struct *cmds, const char *arg)
{
  unsigned int len = strlen(arg);
  Cmd_Struct *matched = (Cmd_Struct *) 0;
  int err = 0;
  Cmd_Struct *next = cmds;

  while (*(next->name)) {
    if (strncmp(next->name, arg, len) == 0) {
      /* have we already matched this one if so make up an error message */
      if (matched) {
	if (!err) {
	  Tcl_AppendResult(interp, "ambiguous option \"", arg,
			   "\" could be ", matched->name, (char *) 0);
	  matched = next;
	  err = 1;
	}
	Tcl_AppendResult(interp, ", ", next->name, (char *) 0);
      } else {
	matched = next;
	/* return on an exact match */
	if (len == (int)strlen(next->name)) {
	  return matched->value;
	}
      }
    }
    next++;
  }
  /* did we get multiple possibilities */
  if (err) return 0;
  /* did we match any at all */
  if (matched) {
    return matched->value;
  } else {
    Tcl_AppendResult(interp, "bad option \"", arg, "\" must be ",
		     (char *) NULL);
    next = cmds;
    while (1) {
      Tcl_AppendResult(interp, next->name, (char *) NULL);
      /* the end of them all ? */
      if (!*((++next)->name)) return 0;
      /* or the last one at least */
      if (*((next + 1)->name))
	Tcl_AppendResult(interp, ", ", (char *) NULL);
      else
	Tcl_AppendResult(interp, " or ", (char *) NULL);
    }
  }
}
