
static char VehicleConfiguration_id[] = "$Header: /u/oreilly/rov/tmacs/RCS/VehicleConfiguration.cc,v 1.7 1998/12/18 21:20:29 oreilly Exp $";

/*
$Log: VehicleConfiguration.cc,v $
Revision 1.7  1998/12/18 21:20:29  oreilly
Add SemiMotor

Revision 1.6  1998/05/18 20:18:38  oreilly
*** empty log message ***

Revision 1.5  1998/02/23 16:14:03  oreilly
*** empty log message ***

Revision 1.4  1997/09/10 08:54:02  oreilly
*** empty log message ***

Revision 1.3  97/05/06  16:33:10  16:33:10  oreilly (Thomas C. O'Reilly)
Added 3x5 micros

Revision 1.2  97/03/20  12:31:45  12:31:45  oreilly (Thomas C. O'Reilly)
..

Revision 1.1  96/10/28  09:12:50  09:12:50  oreilly (Thomas C. O'Reilly)
Initial revision

*/
#include <malloc.h>
#include <string.h>
#include "VehicleConfiguration.h"
#include "Micro.h"
// #include "MotorMicro.h"
#include "SemiMotorMicro.h"
#include "CameraMicro.h"
#include "PanTiltMicro.h"
#include "Sio32Micro.h"
#include "TelemMicro.h"
#include "PduMicro.h"
#include "Stack.h"
#include "parseUtils.h"

VehicleConfiguration::VehicleConfiguration(const char *configFilename)
{
  toolsled = NULL;
  
  if (configFilename)
    _configFilename = strdup(configFilename);
  
  vehicleBase = new VehicleBase("Tiburon");

  char errorBuf[512];
  FILE *fp;

  fprintf(stderr, "Reading Vehicle Configuration file...\n");
  
  if ((fp = fopen(configFilename, "r")) == NULL)
  {
    sprintf(errorBuf, "Couldn't open file \"%s\"", configFilename);
    setError(errorBuf);
    return;
  }
  
  if (parseComponents(fp, errorBuf) == -1)
  {
    char buf[512];
    sprintf(buf, "Error parsing components in file \"%s\":\n%s", 
	    configFilename, errorBuf);
	
    setError(buf);
    return;
  }

  fclose(fp);
  fprintf(stderr, "Done reading Vehicle Configuration.\n");

  // DEBUG
  // print();
  
}


VehicleConfiguration::~VehicleConfiguration()
{
  if (_configFilename) free(_configFilename);
 
  delete vehicleBase;
  delete toolsled;
}


void VehicleConfiguration::print()
{
  vehicleBase->print();
  vehicleBase->printChildren(1);
}


int VehicleConfiguration::parseComponents(FILE *fp, char *errorMsg)
{
  char line[512];
  char componentName[100];
  int nbytes;
  int childLevel = 0;
  const int maxLevels = 16;
  
  ParentComponent *parent = new ParentComponent(vehicleBase);
  
  Stack<ParentComponent *> parentStack(maxLevels);
  int stmntPos;
  const int MaxArgs = 16;
  
  resetLineNo();
  MBool error = FALSE;
  
  while ((nbytes = getLine(fp, line, FALSE, &stmntPos)) != EOF)
  {
    char buf[512];
    strcpy(buf, line);
    char *ptr;

    int nArgs = 0;
    char *args[MaxArgs];
    
    ptr = buf;
    char *token;
    int bufLen = strlen(buf);

    // First tokenize line by whitespace
    while ((token = strtok(ptr, " \t")) != NULL)
      ptr = NULL;

    MBool inString = FALSE;
    int i;
    // De-tokenize quoted strings
    for (i = 0; i < bufLen; i++)
    {
      if (buf[i] == '"')
      {
	inString = !inString;
        // Remove quote symbol
	buf[i] = '\0';
      }
      
      if (buf[i] == '\0' && inString)
	buf[i] = ' ';
    }

    if (inString)
    {
      setParseError("Unbalanced \"", errorMsg);
      error = TRUE;
      break;
    }
    
    // Copy tokens into args array
    for (token = buf; token - buf < bufLen; token += (strlen(token) + 1))
    {
      if (nArgs >= MaxArgs)
      {
	sprintf(buf, "Exceeded %d arguments", MaxArgs);
	setParseError(buf, errorMsg);
	error = TRUE;
	break;
      }
      while (*token == ' ' || *token == '\t' || *token == '\0')
	token++;
      
      args[nArgs++] = strdup(token); 
    }
    if (error)
      break;
    
    MBool create = FALSE;
    VehicleComponent *component;
    
    if (nArgs > 1)
    {
      create = TRUE;
    
      // Create appropriate component object
      component =
	createComponent(parent->component(), nArgs - 1, args, errorMsg);
    }
    
    if (create && !component) 
    {
      // createComponent() failed
      error = TRUE;
      break;
    }

    if (args[nArgs-1][0] == '{')
    {
      childLevel++;

      /* This component is a new parent; push its parent onto stack */
      if (parentStack.push(parent) == -1)
      {
	setParseError("Parent stack overflow", errorMsg);
	error = TRUE;
	break;
      }
      parent = new ParentComponent(component);
    }

    else if (args[nArgs-1][0] == '}')
    {
      childLevel--;
      if (childLevel < 0)
      {
	setParseError("Unbalanced braces", errorMsg);
	error = TRUE;
	break;
      }
      // Go back to previous parent
      delete parent;
      parent = parentStack.pop();
    }

    // Reset arguments
    for (i = 0; i < nArgs; i++)
      free(args[i]);

    nArgs = 0; 
  }                                       // End of read loop 

  if (error)
    return -1;
  
  if (childLevel > 0)
  {
    setParseError("Missing closing brace(s)", errorMsg);
    return -1;
  }

  return 0;
}



VehicleComponent 
  *VehicleConfiguration::createComponent(VehicleComponent *parent,
					 int nArgs, char **args,
					 char *errorMsg)
{
  char buf[512];
  VehicleComponent *component = NULL;
  
  if (nArgs < 1)
  {
    setParseError("Insufficient arguments", errorMsg);
    return NULL;
  }
  
  VehicleComponent::Type componentType = 
    VehicleComponent::mnemonicType(args[0]);

  char *componentName = args[1];
  
  switch (componentType)
  {
    case VehicleComponent::BusType:
    if (nArgs != 2)
    {
      sprintf(buf, "Syntax error - usage: %s name", args[0]);
      setParseError(buf, errorMsg);
      component = NULL;
    }
    else
      component = new Bus(componentName);

    break;

    
    case VehicleComponent::SwitchType:
    case VehicleComponent::VicorSwitchType:
    case VehicleComponent::IsoIoSwitchType:
    case VehicleComponent::SwitchStatusType:

    if (nArgs != 3)
    {
      sprintf(buf, "Syntax error - usage: %s name dmItem",
	      args[0]);

      setParseError(buf, errorMsg);
    }
    else
    {
      char *dmItem = args[2];
      if (!strcmp(dmItem, "dummy"))
        // "dummy" item with no associated data manager item
	dmItem = NULL;
      
      if (componentType == VehicleComponent::SwitchType)
	component = new Switch(componentName, dmItem);

      else if (componentType == VehicleComponent::VicorSwitchType)
	component = new VicorSwitch(componentName, dmItem);

      else if (componentType == VehicleComponent::IsoIoSwitchType)
	component = new IsoIoSwitch(componentName, dmItem);

      else if (componentType == VehicleComponent::SwitchStatusType)
	component = new SwitchStatus(componentName, dmItem);

    }
    break;
    
    case VehicleComponent::MotorSwitchType:
    if (nArgs != 4)
    {
      sprintf(buf, "Syntax error - "
	      "usage: %s name dmItem enableDmItemPrefix",
	      args[0]);

      setParseError(buf, errorMsg);
    }
    else
      component = new MotorSwitch(componentName, args[2], args[3]);
    
    break;
      
    case VehicleComponent::IbcType:
    case VehicleComponent::DconType:
      //    case VehicleComponent::MotorMicroType:
    case VehicleComponent::SemiMotorMicroType:
    case VehicleComponent::CameraMicroType:
    case VehicleComponent::PanTiltMicroType:
    case VehicleComponent::Sio32MicroType:
    case VehicleComponent::TelemMicroType:
    case VehicleComponent::PduMicroType:
    if (nArgs != 3)
    {
      sprintf(buf, "Syntax error - usage: %s name dmItemPrefix",
	      args[0]);

      setParseError(buf, errorMsg);
    }
    else
    {
      char *dmItem = args[2];

      if (componentType == VehicleComponent::IbcType)
	component = new Ibc(componentName, dmItem);

      else if (componentType == VehicleComponent::DconType)
	component = new Dcon(componentName, dmItem);

      //      else if (componentType == VehicleComponent::MotorMicroType)
      // component = new MotorMicro(componentName, dmItem);

      else if (componentType == VehicleComponent::SemiMotorMicroType)
	component = new SemiMotorMicro(componentName, dmItem);

      else if (componentType == VehicleComponent::CameraMicroType)
	component = new CameraMicro(componentName, dmItem);

      else if (componentType == VehicleComponent::PanTiltMicroType)
	component = new PanTiltMicro(componentName, dmItem);

      else if (componentType == VehicleComponent::Sio32MicroType)
	component = new Sio32Micro(componentName, dmItem);

      else if (componentType == VehicleComponent::TelemMicroType)
	component = new TelemMicro(componentName, dmItem);

      else if (componentType == VehicleComponent::PduMicroType)
	component = new PduMicro(componentName, dmItem);

    }
    
    break;


    case VehicleComponent::CommRelaysType:
    if (nArgs != 3)
    {
      sprintf(buf, "Syntax error - usage: %s name dmItemPrefix",
	      args[0]);

      setParseError(buf, errorMsg);
    }
    else
    {
      component = new CommRelaysComponent(args[1], args[2]);
    }
    break;
      

    case VehicleComponent::ToolsledType:
    if (nArgs != 2)
    {
      sprintf(buf, "Syntax error - usage: %s type\nValid types: %s",
	      args[0], 
	      ToolsledComponent::validTypes());

      setParseError(buf, errorMsg);
    }
    else
    {
      toolsled = new ToolsledComponent(args[1]);
      if (toolsled->error())
      {
	toolsled->errorMsg(buf);
	setParseError(buf, errorMsg);
      }
      else
	component = toolsled;
    }

    break;
    
      
    case VehicleComponent::UnknownType:
    sprintf(buf, "Invalid VehicleComponent type \"%s\"\n",  args[0]);
    setParseError(buf, errorMsg);
    component = NULL;
    break;

    default:
    sprintf(buf, "Creation of component \"%s\" not yet implemented", args[0]);
    setParseError(buf, errorMsg);
    component = NULL;
  }

  if (component == NULL)
    return NULL;
  
  parent->addChild(component);
  return component;
}



