
import java.io.IOException;
import java.io.File;
import java.text.DateFormat;
import java.util.TimeZone;
import java.util.Date;

/*
 * The SMART format is at such a premature stage that incorporating it
 * properly into the TDLC software is difficult.  Therefore, we will just
 * hack something up over here, for the time being...
 *
 * Notes:
 *  - Task-level constraints must be applied in the invoking task.
 *    (This implies the use of -s, -I <file>, and -U <file> flags in TDLC.)
 *  - Sole constraints are "disable plan" and "disable achi"
 *  - Handlig is not available.
 *  - One (and only one) "tag" per Spawn, must be unique.
 *  - parent must be referred to as the task name.
 *  - Supply a command line option for SMART's "main" line?
 *    (Perhaps the -A flag in TDLC?)
 */

public class TDLtoSMART extends Object
{
  public static void printSMARTFile ( File     theInputFile,
				      File     theOutputFile,
				      DataFile theDataFile,
				      boolean  theIsVerbose,
				      String   theTextToAdd,
				      TDLC     theTDLCObject )
  {
    DataDestinationFile  fileDestination = new DataDestinationFile();
    DataDestination      dataDestination = null;
    DateFormat           dateFormatter;
    int                  i;


	/* Configure DateFormat */
    dateFormatter = DateFormat.getDateTimeInstance ( DateFormat.FULL,
						     DateFormat.LONG );
	/* For some reason DateFormat defaults to PST *
	 * instead of the local timezone.             */
    dateFormatter . setTimeZone ( TimeZone.getDefault() );

    try
    {
	/* Where are we generating to? */
      if ( theOutputFile == null )
      {
	if ( theIsVerbose )
	  System.out.println ( "\nResults:" );

	dataDestination = new DataDestinationStringBuffer();
      }
      else
      {
		/* Is this an acceptable file? */
	if ( fileDestination . setFile ( theOutputFile . getAbsolutePath() )
	     == false )
	{
	  System.err.println ( "TDLC Error:  Failed to open output file  \""
			       + theOutputFile . getAbsolutePath() + "\"." );
	  System.exit ( -2 );
	}
	else
	{
	  dataDestination = fileDestination;
	}
      }

	/* Write auto-generated comment-header at the beginning of the file */
      dataDestination . setNewlineText ( "// " );
      dataDestination . write ( "//  * File generated by TDLC version " );
      dataDestination . write ( TDLC.VERSION_NUMBER );
      dataDestination . write ( "\n * On.................: " );
      dataDestination . write ( dateFormatter . format ( new Date() ) );
      dataDestination . write ( "\n * Source      File...: " );
      if ( theInputFile == null )
	dataDestination . write ( "*STDIN*" );
      else
	dataDestination . write ( theInputFile . getAbsolutePath() );
      dataDestination . write ( "\n * Destination File...: " );
      if ( theOutputFile == null )
	dataDestination . write ( "*STDOUT*" );
      else
	dataDestination . write ( theOutputFile . getAbsolutePath() );
      dataDestination . write ( "\n * Command Line.......: java TDLC" );
      for ( i=0;   i < theTDLCObject . getArgs() . length;   i++ )
      {
	dataDestination . write ( " " );
	dataDestination . write ( theTDLCObject . getArgs() [ i ] );
      }
      dataDestination . clearNewlineText();
      dataDestination . write ( "\n\n" );


	/* Write any beginning-of-file strings... */
      dataDestination . write ( theTextToAdd );
      if ( theTextToAdd . length() > 0 )
	dataDestination . write ( "\n" );


	/* Translate tasks to SMART... */
      for ( i=0;  i < theDataFile . getSubcomponentsCount();  i++ )
      {
	if (   (   theDataFile . isSubcomponentADataComponent ( i )   )
	    && ( ( theDataFile . getDataComponentSubcomponent ( i ) )
		 instanceof DataTaskDefinition                        ) )
	{
	  printTaskAsSMARTCode (
		      ( (DataTaskDefinition)
			(theDataFile . getDataComponentSubcomponent ( i ) ) ),
		      dataDestination );
	}
      }


	/* And finish up... */
      if ( theOutputFile == null )
      {
	System.out.print ( ((DataDestinationStringBuffer) dataDestination)
			     . getString() );
      }
      else
      {
	fileDestination . close();
      }
    }
    catch ( CompilationException  theException )
    {
      System.err.print ( theException . getMessage() );
      System.exit ( -3 );
    }
  }


  public static void printTaskAsSMARTCode (
				     DataTaskDefinition  theDataTaskDefinition,
				     DataDestination     theOutputDestination )
  {
    int                   i, j;
    DataSpawnTask         spawn;
    DataTaskDefinition    task;
    DataConstraint        dataConstraint;
    DataConstrainedObject dataConstrainedObject;
    DataComponent         dataComponent;
    String                constraintTagTask, stateString, intervalString;
    DataVector            statements
		            = theDataTaskDefinition.generateStatementsVector();
    StringBuffer          disablePlanningBuffer    = new StringBuffer();
    StringBuffer          disableAchievementBuffer = new StringBuffer();

	/* SMART limits us to just Goals & Commands */
    if (   (    theDataTaskDefinition . getTaskType()
	     != DataTaskDefinition.GOAL_TASK          )
	&& (    theDataTaskDefinition . getTaskType()
	     != DataTaskDefinition.COMMAND_TASK       ) )
    {
      System.err.println ( "Warning:  Unable to translate "
			   + theDataTaskDefinition . getTaskTypeString()
			   + " "
			   + theDataTaskDefinition . getTaskName()
			   + " to SMART format." );
      return;
    }

	/* Write task as a comment */
    theOutputDestination . setNewlineText ( "// " );
    theOutputDestination . write ( "\n" );
    theOutputDestination . setStripLeadingWhitespace();
    theDataTaskDefinition . generate ( theOutputDestination,
				       DataComponent.ENTIRE_OBJECT );
    theOutputDestination . clearNewlineText();
    theOutputDestination . write ( "\n" );
    

	/* Write SMART start-of-task */
    theOutputDestination
      . write ( theDataTaskDefinition . getTaskTypeString() );
    theOutputDestination . write ( " " );
    theOutputDestination . write ( theDataTaskDefinition . getTaskName() );
    theOutputDestination . write ( "\nbegin\n" );


	/* SMART Ignores all conditionals, iteration, etc...
	 * SMART just deals with the spawn statements.
	 */
    for ( i=0;  i < statements . count();  i++ )
    {
      if ( ( (statements . elementAt ( i ) ) instanceof DataSpawnTask )
	   == false )
      {
	continue;  /* Nothing interesting here. */
      }
      spawn = (DataSpawnTask) (statements . elementAt ( i ) );


      theOutputDestination
	. write ( theDataTaskDefinition . getIdentifierForSubtask ( spawn ) );
      theOutputDestination . write ( " : " );
      theOutputDestination . write ( spawn . getTaskName() );
      theOutputDestination . write ( "\n" );
    }

	/* Deal with constraints */
    for ( i=0;  i < statements . count();  i++ )
    {
      if ( ( (statements . elementAt ( i ) ) instanceof DataSpawnTask )
	   == false )
      {
	continue;  /* Nothing interesting here. */
      }
      spawn = (DataSpawnTask) (statements . elementAt ( i ) );
      disablePlanningBuffer    . setLength ( 0 );
      disableAchievementBuffer . setLength ( 0 );


	/* Deal with task-level constraints */
      task = DataTaskDefinition.getFirstTaskForName ( spawn . getTaskName() );
      if ( task == null )
      {
	System.err.println( "Warning:  Unable to locate definition for task \""
			    + spawn . getTaskName() + "\"" );
      }
      else if (   ( task . getTaskType() == DataTaskDefinition.GOAL_TASK    )
	       || ( task . getTaskType() == DataTaskDefinition.COMMAND_TASK ) )
      {
	if ( task . getHasConstraint ( DataConstraint.EXPAND_FIRST ) )
	{
	  disableAchievementBuffer . append ( " p_c " );
	  disableAchievementBuffer
	    . append ( theDataTaskDefinition
		         . getIdentifierForSubtask ( spawn ) );
	}

	if ( task . getHasConstraint ( DataConstraint.DELAY_EXPANSION ) )
	{
	  disablePlanningBuffer . append ( " a_e " );
	  disablePlanningBuffer
	    . append ( theDataTaskDefinition
		         . getIdentifierForSubtask ( spawn ) );
	  disablePlanningBuffer . append ( " " );
	}
      }


	/* Deal with spawn or enclosing with-do constraints */
      dataComponent = spawn;
      dataConstrainedObject = spawn;
      while ( dataComponent != null )
      {
	for ( j=0;  j < dataConstrainedObject.getConstraintCount();  j ++ )
	{
	  dataConstraint = dataConstrainedObject . getConstraint ( j );

	  switch ( dataConstraint . getConstraintType() )
	  {
	    case DataConstraint.EXPAND_FIRST:
	      disableAchievementBuffer . append ( " p_c " );
	      disableAchievementBuffer
		. append ( theDataTaskDefinition
			     . getIdentifierForSubtask ( spawn ) );
	      break;


	    case DataConstraint.DELAY_EXPANSION:
	      disablePlanningBuffer . append ( " a_e " );
	      disablePlanningBuffer
		. append ( theDataTaskDefinition
			     . getIdentifierForSubtask ( spawn ) );
	      break;


	    case DataConstraint.SEQUENTIAL_EXPANSION:
	      constraintTagTask
		= getConstraintsEventTagTask ( dataConstraint,
					       spawn,
					       theDataTaskDefinition );
	      if ( constraintTagTask != null )
	      {
		disablePlanningBuffer . append ( " p_c " );
		disablePlanningBuffer . append ( constraintTagTask );
	      }
	      else
	      {
		System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
	      }
	      break;


	    case DataConstraint.SEQUENTIAL_EXECUTION:
	      constraintTagTask
		= getConstraintsEventTagTask ( dataConstraint,
					       spawn,
					       theDataTaskDefinition );
	      if ( constraintTagTask != null )
	      {
		disableAchievementBuffer . append ( " a_c " );
		disableAchievementBuffer . append ( constraintTagTask );
	      }
	      else
	      {
		System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
	      }
	      break;


	    case DataConstraint.SERIAL:
	      constraintTagTask
		= getConstraintsEventTagTask ( dataConstraint,
					       spawn,
					       theDataTaskDefinition );
	      if ( constraintTagTask != null )
	      {
		disablePlanningBuffer . append ( " h_c " );
		disablePlanningBuffer . append ( constraintTagTask );
		disablePlanningBuffer . append ( " p_c " );
		disablePlanningBuffer . append ( constraintTagTask );
		disablePlanningBuffer . append ( " a_c " );
		disablePlanningBuffer . append ( constraintTagTask );

		disableAchievementBuffer . append ( " h_c " );
		disableAchievementBuffer . append ( constraintTagTask );
		disableAchievementBuffer . append ( " p_c " );
		disableAchievementBuffer . append ( constraintTagTask );
		disableAchievementBuffer . append ( " a_c " );
		disableAchievementBuffer . append ( constraintTagTask );
	      }
	      else
	      {
		System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
	      }
	      break;


	    case DataConstraint.DISABLE_FOR_TIME:
		/* If DISABLE_FOR_TIME does not have an AFTER clause...
		 *   Then ignore it.
		 *   Else treat it as a DISABLE_UNTIL_EVENT constraint.
		 */
	      if ( dataConstraint . getHasEventTagTask() == false )
		break;

		/* ***** !!!!!  NO BREAK !!!!! ***** */

	    case DataConstraint.DISABLE_UNTIL_EVENT:
	      if (   ( dataConstraint . getHasConstraintOption() == false )
		  || ( dataConstraint . getConstraintOption()
		       == DataConstraint.EXPANSION ) 
		  || ( dataConstraint . getConstraintOption()
		       == DataConstraint.EXECUTION ) )
	      {
		constraintTagTask
		  = getConstraintsEventTagTask ( dataConstraint,
						 spawn,
						 theDataTaskDefinition );
		if ( constraintTagTask != null )
		{
		  switch ( dataConstraint . getStateBoundary() )
		  {
		    case DataConstraint.ENABLED:     stateString = "e"; break;
		    case DataConstraint.ACTIVE:      stateString = "a"; break;
		    case DataConstraint.COMPLETED:   stateString = "c"; break;
		    default:
		      System.err.println (
				     "Warning:  Bad State (using Completed):  "
				     + dataConstraint . toString() );
		      stateString = "_c";
		      break;
		  }

		  switch ( dataConstraint . getEventConstraintOption() )
		  {
		    case DataConstraint.HANDLING:  intervalString = "h"; break;
		    case DataConstraint.EXPANSION: intervalString = "p"; break;
		    case DataConstraint.EXECUTION: intervalString = "a"; break;
		    default:
		      if ( dataConstraint . getStateBoundary()
			   == DataConstraint.COMPLETED )
			intervalString = "a";
		      else
			intervalString = "h";
		      break;
		  }

		  if (   ( dataConstraint . getHasConstraintOption() == false )
		      || ( dataConstraint . getConstraintOption()
			   == DataConstraint.EXPANSION ) )
		  {
		    disablePlanningBuffer . append ( " " );
		    disablePlanningBuffer . append ( intervalString );
		    disablePlanningBuffer . append ( "_" );
		    disablePlanningBuffer . append ( stateString );
		    disablePlanningBuffer . append ( " " );
		    disablePlanningBuffer . append ( constraintTagTask );
		  }

		  if (   ( dataConstraint . getHasConstraintOption() == false )
		      || ( dataConstraint . getConstraintOption()
			   == DataConstraint.EXECUTION ) )
		  {
		    disableAchievementBuffer . append ( " " );
		    disableAchievementBuffer . append ( intervalString );
		    disableAchievementBuffer . append ( "_" );
		    disableAchievementBuffer . append ( stateString );
		    disableAchievementBuffer . append ( " " );
		    disableAchievementBuffer . append ( constraintTagTask );
		  }
		}
		else
		{
		  System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
		}
	      }
	      else
	      {
		System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
	      }
	      break;


	    default:
	      System.err.println (
		   "Warning:  Unable to translate constraint to SMART format: "
		   + dataConstraint . toString() );
	      break;

	  } /* switch ( dataConstraint . getConstraintType() ) */
	} /* FOR ( 0 <= j < dataConstrainedObject.getConstraintCount() ) */

	  /* Find any enclosing with-do statements constraints ... */
	do {
	  dataComponent = dataComponent . getParent();
	} while (   ( dataComponent != null )
		 && ((dataComponent instanceof DataConstrainedObject) == false)
		 );
	dataConstrainedObject = (DataConstrainedObject) dataComponent;

      } /* while ( dataComponent != null ) */

      if ( disablePlanningBuffer . length() > 0 )
      {
	theOutputDestination . write ( "disable planning    " );
	theOutputDestination
	  . write ( theDataTaskDefinition . getIdentifierForSubtask( spawn ) );
	theOutputDestination . write ( " until " );
	theOutputDestination . write ( disablePlanningBuffer . toString() );
	theOutputDestination . write ( "\n" );
      }

      if ( disableAchievementBuffer . length() > 0 )
      {
	theOutputDestination . write ( "disable achievement " );
	theOutputDestination
	  . write ( theDataTaskDefinition . getIdentifierForSubtask( spawn ) );
	theOutputDestination . write ( " until " );
	theOutputDestination . write ( disableAchievementBuffer . toString() );
	theOutputDestination . write ( "\n" );
      }

    } /* for ( i=0;  i < statements . count();  i++ )

	/* Write SMART end-of-task */
    theOutputDestination . write ( "end\n\n" );
  }


  public static String getConstraintsEventTagTask (
				     DataConstraint     theDataConstraint,
				     DataSpawnTask      theDataSpawnTask,
				     DataTaskDefinition theDataTaskDefinition )
  {
    int                 i;
    DataSpawnTask       spawn;
    DataLabelStatement  label;
    DataVector          statements
			 = theDataTaskDefinition . getCachedStatementsVector();

    if ( theDataConstraint . getHasEventTagTask() == false )
      return null;

    if ( theDataConstraint . getHasEventTagTaskOfThis() )
      return theDataTaskDefinition . getTaskName();

    if ( theDataConstraint . getHasEventTagTaskOfChild() )
      return theDataTaskDefinition . getIdentifierForSubtask (
							    theDataSpawnTask );

    if ( theDataConstraint . getHasEventTagTaskOfPrevious() )
      return null;


    for ( i=0;  i < statements . count();  i++ )
    {
      if ( ( (statements . elementAt ( i ) ) instanceof DataSpawnTask )
	   == false )
      {
	continue;  /* Nothing interesting here. */
      }
      spawn = (DataSpawnTask) (statements . elementAt ( i ) );

      if ( spawn . getTaskName()
	         . equals ( theDataConstraint . getEventTagTask() ) )
      {
	return theDataTaskDefinition . getIdentifierForSubtask ( spawn );
      }

      for ( label  = spawn . getLabel();
	    label != null;
	    label  = label . getLabel() )
      {
	if (    label . hasId()
	     && label . getId()
		      . equals ( theDataConstraint . getEventTagTask() ) )
	{
	  return theDataTaskDefinition . getIdentifierForSubtask ( spawn );
	}
      }
    }

	/* Unable to find spawn matching  theDataConstraint.getEventTagTask()*/
    return null;
  }
}
