#!/usr/bin/env perl
#
# Generate multi-cycle ALF, based on Brian Grayson's alf_generator script
#
# Flow:
# 1.  Read in an FLA-generator file
# 2.  Generate the FLA
# 3.  Convert FLA to ALF (one-liner in sed)
#
###########################################################################

use Getopt::Long;

use strict;

#--------------------------------------------------
# Global variables
#
my ($DEFAULT_NUM_T)	= 41;	# Default number of cycles to generate
my ($DEFAULT_PREFIX)	= 'top.cpu.core0';


#--------------------------------------------------
# Process command-line options
#
my ($main_vr) = { };

&initialize_vars(\$main_vr);
&process_command_line_options($main_vr);


#--------------------------------------------------
# Set up tmp FLA files
# - multiple files allow us to more easily choose draw order
#   - later files are drawn after (on top of) earlier files
# - currently we have three FLA files:
#   - fla 'MAIN' is the main one for all elements
#   - fla 'SEP'  is for separators
#   - fla '_SCH'  is for schedules (need to be written first)
$main_vr->{'fla_file_keys'} = [ '_SCH', 'MAIN', 'SEP' ];

foreach my $fla_key (@{$main_vr->{'fla_file_keys'}}) {
  my ($fla_file) = $main_vr->{'fla_file'}{$fla_key} =
    {
     'NAME'	=> "/tmp/$0.$$.${fla_key}.fla",
     'FH'	=> undef,
    };

  open ($fla_file->{'FH'}, ">", $fla_file->{'NAME'}) or die;

}

#foreach my $fla_file (values %{$main_vr->{'fla_file'}}) { print $fla_file->{'NAME'}, "\n"; }


#--------------------------------------------------
# Execute commands from command line
#
if ($main_vr->{'COMMAND-LINE COMMANDS'} ne '') {
  my ($cmd_fh);

  my ($cmds) = $main_vr->{'COMMAND-LINE COMMANDS'};
  open ($cmd_fh, "<", \$cmds) or die "*** Cannot parse command line commands\n";

  &execute_cmds($main_vr, $cmd_fh);

  close ($cmd_fh);
}


#--------------------------------------------------
# Execute cmd file
#
if (@ARGV == 1) {
  my ($cmd_file) = shift;		# Get cmd file name from command line
  print STDERR "Using command file \"$cmd_file\"\n";

  &execute_command_file($main_vr, $cmd_file);
}

foreach my $fla_file (values %{$main_vr->{'fla_file'}}) {
  close $fla_file->{'FH'};
}


#--------------------------------------------------
# Convert FLA to ALF
#
&fla2alf($main_vr);

foreach my $fla_file (values %{$main_vr->{'fla_file'}}) {
  system("rm " . $fla_file->{'NAME'});
}


exit;


###########################################################################
# sub initialize_vars($vars_hashref_ref)
#
sub initialize_vars {
  my ($vars_hashref_ref) = @_;

  $$vars_hashref_ref =
    {
     'fla_file'		=> { },
     'num_t'		=> $DEFAULT_NUM_T,
     'dir_mode'		=> 0,
     'column_right_x'	=> 0,
    };

}	# sub initialize_vars()


###########################################################################
# sub process_command_line_options($vr)
#
sub process_command_line_options {
  my ($vr) = @_;

  # This doesn't work because installed version of Getopt::Long is too old:
  #use Getopt::Long qw(GetOptionsFromString);

  Getopt::Long::Configure("bundling"); # Enable bundling of single-char options

  my ($print_help)	= 0;
  my (@commands)	= ( );
  my ($num_t)		= undef;
  my ($inc_lsu)		= 0;
  my ($inc_fp)		= 0;
  my ($stall_window)	= 0;
  my ($explicit_prefix)	= 0;

  my (@OPTION_ASSIGNMENTS) = ('h'	=> \$print_help,
			      't=i'	=> \$num_t,
			      'c=s'	=> \@commands,
			      'l'	=> \$inc_lsu,
			      'f'	=> \$inc_fp,
			      'S=i'	=> \$stall_window,
			      'p'	=> \$explicit_prefix,
			     );


  GetOptions(@OPTION_ASSIGNMENTS) or &print_usage_and_exit();

  &print_usage_and_exit() if ($print_help or @ARGV > 1);

  push (@commands, "num_t = $num_t") if defined($num_t);

  push (@commands, "inc_lsu = $inc_lsu");
  push (@commands, "inc_fp  = $inc_fp");
  push (@commands, "stall_window = $stall_window");
  push (@commands, "explicit_prefix = $explicit_prefix");


  &add_var($vr, 'COMMAND-LINE COMMANDS', join("\n", @commands), 'NO PARSE');

}	# sub process_command_line_options()


###########################################################################
# sub print_usage_and_exit()
#
sub print_usage_and_exit {
  print <<"END_USAGE";
Usage:  $0 [options] cmd_file

Options:
  -h         = help
  -t <num_t> = number of cycles to generate (default = $DEFAULT_NUM_T)
  -c <cmd>   = set \$var to val
  -l         = include additional LSU pipeline elements in wave*.alf
  -f         = include FPU pipeline elements in wave*.alf
  -S <num>   = include <num> cycles of stall reasons in wave*.alf
  -p         = input file will explicitly contain all prefixes; otherwise assume prefix = "$DEFAULT_PREFIX"

Examples:
  \$ $0 -t 40 wave_fla.cmd > wave40.alf
  \$ $0 -c dirsize=30 -t 40 wave_fla.cmd > wave40.alf
  \$ $0 -c 'grid_color = "gray50"' -t 40 wave_fla.cmd > wave40.alf

Note:  if a variable is set on the command line via the -c option, then
this variable will be ignored if set inside the cmd file.

END_USAGE

  exit;
}	# sub print_usage_and_exit()


###########################################################################
# sub execute_cmds($vr, $cmd_fh)
# - writes cmd vars to %vr
#
sub execute_cmds {
  my ($vr, $cmd_fh) = @_;

  my %schedules;
  while (<$cmd_fh>) {
    # Ignore everything following '#'
    s/#.*//;

    # Ignore blank line
    next unless /\S/;


    #--------------------------------------------------
    # Look for conditional
    #
    if (/^\s*\[\s*(\w+)\s*\]\s*(.*)/) {
      my ($cond_var, $cmd) = ($1, $2);

      next unless $vr->{$cond_var};

      # Condition is true, so evaluate the command
      $_ = $cmd;
    }


    #--------------------------------------------------
    # Process commands
    #
    if (/^\s*print(last)?\s+(.*)/) {					# print
      &print_str($vr, $1, $2);

    } elsif (/^\s*caption\s+(.*)/) {					# caption
      &output_caption($vr, $1);

    } elsif (/^\s*([+-]?\d+)?\s*pipe([msticlIfP]*)\s+"(.*)"\s+(\S+)(?:\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?)?(?:\s+(\S+))?/) {	# pipe
      &output_pipe($vr, \%schedules, $1, $2, $3, $4, $5, $6, $7, $8);

    } elsif (/^\s*stall\s+"(.*)"\s+(\S+)/) {				# stall
      &output_stall($vr, $1, $2);

    } elsif (/^\s*(\w+)\s*=\s*(.*)/) {					# variable assignment
      &add_var($vr, $1, $2);

    } elsif (/^\s*show_ver_sep/) {					# show_ver_sep
      &show_ver_sep($vr);

    } elsif (/^\s*show_cycles/) {					# show_cycles
      &show_cycles($vr);

    } elsif (/^\s*include\s+(.*)/) {					# include
      &execute_command_file($vr, $1);

    } else {
      print STDERR "*** WARNING:  cannot parse line $.: $_";
    }
  }	# Process each line of cmd file

  my $key;
  for $key (keys %schedules) {
    &output_schedule($vr, $schedules{$key});
  }

  close (CMD_FILE);
}	# sub execute_cmds()


###########################################################################
# sub print_str($vr, $last, $str)
#
sub print_str {
  my ($vr, $last, $str) = @_;

  chomp($str);

  my ($last_fla_file_key) = $vr->{'fla_file_keys'}[$#{$vr->{'fla_file_keys'}}];
  my ($fla_key) = $last ? $last_fla_file_key : 'MAIN';
  my ($fh);
  if ($str eq '---') {
    #hack to make sure --- is at top.
    $fh = $vr->{'fla_file'}{'_SCH'}{'FH'};
  }
  else {
    $fh = $vr->{'fla_file'}{$fla_key}{'FH'};
  }
  print $fh "$str\n";
}	# sub print_str()


###########################################################################
# sub output_caption($vr, $caption, $y)
#
sub output_caption {
  my ($vr, $caption, $y) = @_;

  $y = $vr->{'y'} unless defined $y;

  my ($grid_color) = defined($vr->{'caption_color'}) ? $vr->{'caption_color'} : $vr->{'grid_color'};

  my ($fh) = $vr->{'fla_file'}{'MAIN'}{'FH'};
  printf $fh ("- Content: caption^  caption: %s^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)\n",
	      $caption, $grid_color,
	      $vr->{'cap_wt'}, $vr->{'ht'},
	      $vr->{'caption_x'}, $y);

}	# sub output_caption()


###########################################################################
# sub output_pipe($vr, $time_offset, $mstic, $caption, $base, $seq1, $seq2, $seq3, $schedules_ref)
#
sub output_pipe {
  my ($vr, $schedules_ref, $time_offset, $mstic, $caption, $base, $seq1, $seq2, $seq3, $field) = @_;

  my ($mini)     = $mstic =~ /m/i;
  my ($space)    = $mstic =~ /s/i;
  my ($truncate) = $mstic =~ /t/i;
  my ($indexed_caption) = $mstic =~ /i/;
  my ($auto_color_basis_stf_id) = $mstic =~ /I/;
  my ($auto_color_basis_field_name) = $mstic =~ /f/;
  my ($auto_color_basis_python) = $mstic =~ /P/;
  my ($collectable) = $mstic =~ /c/i;
  my ($cycle_legend) = $mstic =~ /l/i;

  my ($dir_mode) = $vr->{'dir_mode'};

  my ($start_t, $num_t);
  if ($dir_mode) {
    $start_t = $vr->{'dir_t'};
    $start_t += $time_offset if defined($time_offset);

    $num_t   = 1;
  } else {
    $start_t = $vr->{'start_t'};
    $num_t   = $vr->{'num_t'};
  }

  &output_caption($vr, $caption) unless $indexed_caption;

  my ($start, $end, $step);

  my ($no_idx) = !(defined $seq1);

  if ($no_idx) {
    $start = 0;
    $end   = 0;
    $step  = 1;		# Must be nonzero
  } else {
    if (defined $seq3) {
      $start = $seq1;
      $step  = $seq2;
      $end   = $seq3;
    } else {
      $start = $seq1;
      $end   = $seq2;
      $step  = 1;
    }
  }

  my ($orig_y) = $vr->{'y'};

  my ($last) = ($end > $start) ? $end : $start;

  my ($element_args) = {
			'CMA'		=> $mini ? 'CM' : ($collectable ? 'CC' : 'CA'),
			'BASE'		=> $base,
			'HEIGHT'	=> $mini ? $vr->{'mht'} : $vr->{'ht'},
			'NO_VER_SEP'	=> $dir_mode,
                        'AUTO_COLOR_STYLE' => ($auto_color_basis_stf_id) ? 'ACI_STFID' : (($auto_color_basis_field_name) ? "ACI_FIELD_$field" : (($auto_color_basis_python) ? "ACI_PYTHON_FUNC_$field" : '')),
		       };

  my ($v_spc) = $mini ? $vr->{'mht_spc'} : $vr->{'ht_spc'};

  my ($end_base)   = int(($end - $start) / $step);

  my ($sep_int) = $vr->{'sep_interval'};

  my ($num_rows_displayed) = ($step > 0) ? 0 : ($sep_int - ($end_base + 1) % $sep_int);
  $num_rows_displayed = 0 if $num_rows_displayed == $sep_int;

  foreach my $idx (map { $_ * $step + $start } 0 .. $end_base) {
    &output_hor_separator($vr) if ($num_rows_displayed != 0 and $num_rows_displayed % $sep_int == 0);

    --$idx if ($truncate and $idx == $last);

    &output_caption($vr, "$caption\[$idx\]") if $indexed_caption;

    $element_args->{'IDX'} = $no_idx ? '' : $idx;

    $vr->{'column_right_x'} = $vr->{'content_base_x'} + $vr->{'stall_width'} + ($vr->{'wt_spc'} - 1) *$num_t;
    if ($num_t == 1) {
      &loop_over_cycles($vr, $element_args, $start_t, $num_t, \&output_element, \&update_x_due_to_ver_sep)
    } elsif ($num_t > 1) {
      # optimized element
      my ($key) = "$start_t:$vr->{'content_base_x'}:$vr->{'column_right_x'}";

      my %properties = %$vr; #copy
      # poach needed values
      $properties{'pixel_offset'} = $vr->{'pixel_offset'};
      $properties{'height'} = $element_args->{'HEIGHT'};
      $properties{'idx'} = $element_args->{'IDX'};
      $properties{'base'} = $base;
      $properties{'cma'} = $element_args->{'CMA'};
      $properties{'type'} = $cycle_legend ? 'schedule_line_ruler' : 'schedule_line';
      $properties{'auto_color_style'} = $element_args->{'AUTO_COLOR_STYLE'};
      push (@{$schedules_ref->{$key}}, \%properties);
    }

    $vr->{'y'} += $v_spc;
    ++$num_rows_displayed;
  }

  $vr->{'y'} = $orig_y + $vr->{'ht_spc'} if ($vr->{'y'} < $orig_y + $vr->{'ht_spc'});

  &output_hor_separator($vr, $vr->{'caption_x'}) if $space;

}	# sub output_pipe()


###########################################################################
# sub loop_over_cycles($vr, $element_args, $start_t, $num_t,
#                      \&output_element_sub, \&output_ver_sep_sub)
#
sub loop_over_cycles {
  my ($vr, $element_args, $start_t, $num_t,
      $output_element_subref, $output_ver_sep_subref) = @_;

  my ($dir_t)            = $vr->{'dir_t'};
  my ($ver_sep_interval) = $vr->{'ver_sep_interval'};

  $element_args->{'ELEMENT X'} = $vr->{'content_base_x'};

  my $time = $start_t;
  foreach my $time ($start_t .. ($start_t + $num_t - 1)) {
    &$output_ver_sep_subref($vr, $element_args) if ($time == $dir_t);

    &$output_ver_sep_subref($vr, $element_args, $vr->{'ver_sep_interval_color'})
      if ($ver_sep_interval != 0 and
	  $time != $dir_t and $time != $dir_t + 1 and
	  ($time - $dir_t) % $ver_sep_interval == 0);

    &$output_element_subref($vr, $element_args, $time) if defined($output_element_subref);

    $element_args->{'ELEMENT X'} += $vr->{'wt_spc'};

    &$output_ver_sep_subref($vr, $element_args) if ($time == $dir_t);
  }

}	# sub loop_over_cycles()


###########################################################################
# sub output_schedule($vr, $schedule_lines)
#
sub output_schedule {
  my ($vr, $schedule_lines) = @_;

  my $properties = $schedule_lines->[0];
  my $width = $properties->{'column_right_x'}-$properties->{'content_base_x'}+1;
  my ($fh) = $properties->{'fla_file'}{'_SCH'}{'FH'};

  # to find height of container, find y-bounds of contents
  my $highest_y = 0;
  my $temp_high_bound;
  foreach my $prop_settings (@$schedule_lines) {
    $temp_high_bound = $prop_settings->{'height'}+$prop_settings->{'y'};
    if ($temp_high_bound > $highest_y) {
      $highest_y = $temp_high_bound;
    }
  }
  my $height = $highest_y-$properties->{'y'};

  #make container
  my $DEFAULT_CLOCK_FOR_SCALE = 1;
  printf $fh ("- type: schedule^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)^  pixel_offset: %s^  time_scale: %s^  children:\n",
	      $properties->{'grid_color'}, $width,
              $height, $properties->{'content_base_x'}, $properties->{'y'}, $properties->{'pixel_offset'}, $DEFAULT_CLOCK_FOR_SCALE/$properties->{'wt_spc'});

  #fill in lines
  my ($loc_cmd) = $vr->{'explicit_prefix'} ? 'LOP ' : 'LOC.';

  foreach my $prop_settings (@$schedule_lines) {
    # The auto_color_style stuff is sneaky -- if not set, we want to print
    # nothing.  If set, we need to print the caret and then the contents.  It is
    # jammed right after the t_offset %s, with no spaces, so that the ALF still looks clean.
    printf $fh ("  - %s    type: %s^    ${loc_cmd}%s%s^    color: %s^    dimensions: (%s, %s)^    position: (%s, %s)^    t_offset: %s%s\n",
    	      $prop_settings->{'cma'}, $prop_settings->{'type'}, $prop_settings->{'base'}, $prop_settings->{'idx'}, $prop_settings->{'grid_color'},
    	      $prop_settings->{'wt_spc'}*$properties->{'num_t'}, $prop_settings->{'height'}, $prop_settings->{'content_base_x'}, $prop_settings->{'y'}, $prop_settings->{'start_t'}, ($prop_settings->{'auto_color_style'}) ? "^    $prop_settings->{'auto_color_style'}":"");
  }
}	# sub output_schedule()


###########################################################################
# sub output_element($vr, $element_args, $time)
#
sub output_element {
  my ($vr, $element_args, $time) = @_;

  my ($cma, $base, $idx,
      $height, $x, $auto_color_style) = @{$element_args}{
	'CMA', 'BASE', 'IDX',
	'HEIGHT', 'ELEMENT X', 'AUTO_COLOR_STYLE'};

  my ($fh) = $vr->{'fla_file'}{'MAIN'}{'FH'};
  my ($loc_cmd) = $vr->{'explicit_prefix'} ? 'LOP ' : 'LOC.';

  # The auto_color_style stuff is sneaky -- if not set, we want to print
  # nothing.  If set, we need to print the caret and then the contents.  It is
  # jammed right after the t_offset %s, with no spaces, so that the ALF still looks clean.
  printf $fh ("- %s  ${loc_cmd}%s%s^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)^  t_offset: %s%s\n",
	      $cma, $base, $idx, $vr->{'grid_color'},
	      $vr->{'wt'}, $height, $x, $vr->{'y'}, $time, ($auto_color_style) ? "^  ${auto_color_style}":"");

}	# sub output_element()


###########################################################################
# sub update_x_due_to_ver_sep($vr, $element_args)
#
sub update_x_due_to_ver_sep {
  my ($vr, $element_args) = @_;

  return if ($element_args->{'NO_VER_SEP'});

  $element_args->{'ELEMENT X'} += $vr->{'sep_wt'} - 1;
}	# sub update_x_due_to_ver_sep()


###########################################################################
# sub output_hor_separator($vr, $x, $y, $width)
#
sub output_hor_separator {
  my ($vr, $x, $y, $width) = @_;

  $x     = $vr->{'content_base_x'}          unless defined($x);
  $y     = $vr->{'y'}                       unless defined($y);
  $width = $vr->{'column_right_x'} - $x + 1 unless defined($width);

  my ($fh) = $vr->{'fla_file'}{'SEP'}{'FH'};
  printf $fh ("- Content: caption^  caption: ^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)\n",
	      $vr->{'sep_color'},
	      $width, $vr->{'sep_ht'}, $x, $y);

  $vr->{'y'} += $vr->{'sep_ht'} - 1;
}	# sub output_hor_separator()


###########################################################################
# sub output_stall($vr, $caption, $base)
#
sub output_stall {
  my ($vr, $caption, $base) = @_;

  my ($dir_t)        = $vr->{'dir_t'};
  my ($stall_window) = $vr->{'stall_window'};

  $vr->{'column_right_x'} = $vr->{'content_base_x'} + $vr->{'stall_width'} - 1;

  my ($element_args) = {
			'CMA'		=> 'CA',
			'BASE'		=> $base,
			'IDX'		=> '',
			'HEIGHT'	=> $vr->{'ht'},
			'NO_VER_SEP'	=> 1,
		       };

  foreach my $time ($dir_t - $stall_window .. $dir_t + $stall_window) {
    &output_hor_separator($vr, $vr->{'caption_x'}) if $time == $dir_t;

    &output_caption($vr, "$caption $time");

    $element_args->{'ELEMENT X'} = $vr->{'content_base_x'};
    &output_element($vr, $element_args, $time);

    $vr->{'y'} += $vr->{'ht_spc'};

    &output_hor_separator($vr, $vr->{'caption_x'}) if $time == $dir_t;
  }
}	# sub output_stall()


###########################################################################
# sub add_var($vr, $var, $val_expr)
# - writes cmd vars to %$vr
#
sub add_var {
  my ($vr, $var, $val_expr, $no_parse) = @_;

  #--------------------------------------------------
  # Parse value expression
  #
  my ($val) = undef;

  if (defined $no_parse) {
    $val = $val_expr;
  } else {
    my ($MAX_ITERS) = 50;
    my ($num_iters) = 0;
    while ($val_expr =~ /(\$(\w+))/ or
	   $val_expr =~ /(\$\{(\w+)\})/) {
      my ($var_str, $subst_var) = ($1, $2);

      $var_str = "\\$var_str";

      if (!defined($vr->{$subst_var})) {
	#-------------------------
	# This variable found in the value expression is not currently defined
	#
	print STDERR "*** WARNING:  variable \"$subst_var\" has not been defined yet in \"$val_expr\"\n";
	return 0;
      }

      $val_expr =~ s/$var_str/$vr->{$subst_var}/;

      # Make sure we don't evaluate too many expressions
      last if (++$num_iters > $MAX_ITERS);
    }

    eval ("\$val = $val_expr");

    if ($@) {
      print STDERR "*** WARNING:  Can't evaluate expression \"$val_expr\":\n$@\n";
    }
  }


  #--------------------------------------------------
  # Add new variable value to variable hash
  #
  $vr->{$var} = $val;

}	# sub add_var()


###########################################################################
# sub show_ver_sep($vr)
#
sub show_ver_sep {
  my ($vr) = @_;

  my ($element_args) = { };

  my ($start_t) = $vr->{'start_t'};
  my ($num_t)   = $vr->{'num_t'};

  &loop_over_cycles($vr, $element_args, $start_t, $num_t, undef, \&output_ver_separator);
}	# sub show_ver_sep()


###########################################################################
# sub output_ver_separator($vr, $element_args, $color)
#
sub output_ver_separator {
  my ($vr, $element_args, $color) = @_;

  return if ($element_args->{'NO_VER_SEP'});

  $color = $vr->{'sep_color'} unless defined($color);

  my ($fh) = $vr->{'fla_file'}{'SEP'}{'FH'};
  printf $fh ("- Content: caption^  caption: ^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)\n",
	      $color,
	      $vr->{'sep_wt'}, $vr->{'y'} - $vr->{'top_margin'} + 1,
	      $element_args->{'ELEMENT X'}, $vr->{'top_margin'});

  &update_x_due_to_ver_sep($vr, $element_args);
}	# sub output_ver_separator()


###########################################################################
# sub show_cycles($vr)
# Legacy support: please use pipe cycle legends now.
sub show_cycles {
  my ($vr) = @_;

  my ($fh) = $vr->{'fla_file'}{'MAIN'}{'FH'};

  if ($vr->{'dir_mode'}) {
    #--------------------------------------------------
    # In directory mode, simply output a spacer
    #
    printf $fh ("- Content: caption^  caption: C=%s^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)\n",
		$vr->{'cycle_legend_color'}, $vr->{'sep_color'},
		$vr->{'column_right_x'} - $vr->{'caption_x'} + 1, $vr->{'ht'},
		$vr->{'caption_x'}, $vr->{'y'});
  } else {
    #--------------------------------------------------
    # Display text "Cyc"
    #
    &output_caption($vr, 'C=' . $vr->{'cycle_legend_color'} . ' Cyc');


    #--------------------------------------------------
    # Display current cycle
    #
    my ($grid_color) = defined($vr->{'caption_color'}) ? $vr->{'caption_color'} : $vr->{'sep_color'};
    printf $fh ("- Content: cycle^  LOC.rob.ReorderBuffer.ReorderBuffer0^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)^  t_offset: %s\n",
		$grid_color,
		$vr->{'cycle_legend_current_wt'}, $vr->{'ht'},
		$vr->{'caption_x'} + $vr->{'cap_wt'} - $vr->{'cycle_legend_current_wt'}, $vr->{'y'},
		$vr->{'dir_t'});


    #--------------------------------------------------
    # Display cycle offsets
    #
    my ($element_args) = { };

    my ($start_t) = $vr->{'start_t'};
    my ($num_t)   = $vr->{'num_t'};

    &loop_over_cycles($vr, $element_args, $start_t, $num_t, \&output_cycle_caption, \&update_x_due_to_ver_sep);
  }

  $vr->{'y'} += $vr->{'ht_spc'};

  &output_hor_separator($vr, $vr->{'caption_x'});
}	# sub show_cycles()


###########################################################################
# sub output_cycle_caption($vr, $element_args, $time)
#
sub output_cycle_caption {
  my ($vr, $element_args, $time) = @_;

  my ($fh) = $vr->{'fla_file'}{'MAIN'}{'FH'};
  printf $fh ("- Content: caption^  caption: C=%s %s^  color: %s^  dimensions: (%s, %s)^  position: (%s, %s)\n",
	      $vr->{'cycle_legend_color'}, $time,
	      $vr->{'sep_color'},
	      $vr->{'cycle_disp_width'}, $vr->{'ht'},
	      $element_args->{'ELEMENT X'}, $vr->{'y'}) if (($time - $vr->{'dir_t'}) % $vr->{'cycle_legend_interval'} == 0);

}	# sub output_cycle_caption()


###########################################################################
# sub execute_command_file($vr, $filename)
#
sub execute_command_file {
  my ($vr, $filename) = @_;

  my ($cmd_fh);
  open ($cmd_fh, "<", $filename) or die "*** Cannot open command file \"$filename\"\n";

  &execute_cmds($vr, $cmd_fh);

  #  print STDERR "Variables:  \n";
  #  while (my ($var, $val) = each %$vr) {
  #    print STDERR "\t$var\t= $val\n";
  #  }

  close ($cmd_fh);
}	# sub execute_command_file()


###########################################################################
# sub fla2alf($vr)
# - send output to stdout
# - we process FLA files in the order given by $vr->{'fla_file_keys'}
#
sub fla2alf {
  my ($vr) = @_;

  foreach my $fla_key (@{$main_vr->{'fla_file_keys'}}) {
    open (FLA_FILE, "<", $vr->{'fla_file'}{$fla_key}{'NAME'}) or die;

    while (<FLA_FILE>) {
      s/CA/Content: auto_color_annotation^/g;
      s/CM/Content: auto_color_anno_notext^/g;
      s/CC/Content: auto_color_anno_nomunge^/g;
      s/ACI_STFID/auto_color_basis: stf id=^/g;
      s/ACI_FIELD_(.*)/auto_color_basis: \1=^/g;
      s/(\s*)ACI_PYTHON_FUNC_(.*)/\1auto_color_basis: \2^\1color_basis_type: python_func^/g;
      s/LOC/LocationString: $DEFAULT_PREFIX/go;
      s/LOP/LocationString:/g;

      # Colors
      s/black/(0,0,0)/;
      s/gray50/(128,128,128)/;
      s/gray75/(192,192,192)/;
      s/gray25/(64,64,64)/;

      # Newlines
      s/\^/\n/g;


      # Write output
      print;
    }

    close (FLA_FILE);

  }
}	# sub fla2alf()
