###################################### 
##### Model Transformation Tools #####
######################################

# gawk script: rbg_fig2m.awk
# Raw  bond-graph conversion from fig to matlab
# P.J.Gawthrop June 1996
# Copyright (c) P.J.Gawthrop, 1996.

###############################################################
## Version control history
###############################################################
## $Id$
## $Log$
## Revision 1.21  1997/08/04 12:49:17  peterg
## Modified to use named (as opposed to numbered) ports.
## Generates a list of component ports in the .rbg file.
## As octave handles string vectors properly, the .cmp file format is not
## really necessary - but I've let it be for the moment.
##
## Revision 1.20  1997/03/19 12:02:01  peterg
## Now writes an error message if a lable is used twice in the fig file.
##
# Revision 1.19  1997/03/19  09:49:39  peterg
# Ports now written in cmp file.
#
# Revision 1.18  1997/03/19  09:42:08  peterg
# Now writes out the following additional fig  files:
# _head.fig	The fig header
# _bnd.fig	The bonds actually used
# _cmp.fig	The components actually used.
#
# Revision 1.17  1997/01/02  11:21:17  peterg
# Now assumes all components bonds etc at depth zero in fig file.
# Ie anything at depth>0 is ignored.
# Thanks to Donald for suggesting this.
#
## Revision 1.16  1996/12/30 20:00:29  peterg
## Fixed bent-bond bug.
## NB unfixed problems:
## 1. xfig writes multi line fields if more than about 5 segments.
## 2. rbg2abg takes a multi-segment bond as a straignt line between the
## end points - so computation of stroke and arrow directions may be
## iffy.
##
## Revision 1.15  1996/12/30 19:23:35  peterg
##  Allows for bent bonds - ie bonds with more than 2 line segments.
##
## Revision 1.14  1996/12/21 19:47:53  peterg
## Changed \* to \\*
##
## Revision 1.13  1996/12/21 19:47:23  peterg
## Put back under VC
##
# Revision 1.12  1996/08/24  16:30:12  peter
# Fixed error in nonport_regexp.
#
## Revision 1.11  1996/08/19 10:48:57  peter
## Added `-' to the component regexp.
##
## Revision 1.10  1996/08/19 09:03:13  peter
## Parses repetative components: ie suffixed by *n.
##
## Revision 1.9  1996/08/09 08:23:11  peter
## Fixed bug: ports not recognised.
##
## Revision 1.8  1996/08/05 20:12:43  peter
## Now writes a _fig.fig file.
##
## Revision 1.7  1996/08/05 18:44:56  peter
## Now writes out a _cbg file without ----- symbol.
##
## Revision 1.6  1996/08/05 12:17:37  peter
## n_ports now appear in the _abg file instead.
##
## Revision 1.5  1996/08/05 12:01:28  peter
## The _cmp function now returns the number of ports.
##
## Revision 1.4  1996/08/05 10:14:46  peter
## Made ports appear, in order, at top of component lists
##
## Revision 1.3  1996/08/04 20:32:28  peter
## Stopped complaint about missing lbl entry for port components
##
## Revision 1.2  1996/08/04 20:05:25  peter
## Included port components - eg SS:[1]
##
## Revision 1.1  1996/08/04 20:01:58  peter
## Initial revision
##
###############################################################


##############################################################
# This (g)awk script reads a fig file in fig 3.1 format.
# It interprets the picture as: bonds, arrows and components
# as follows:
#
# Bonds are firm (not dashed etc) polylines with n line segments - 
#  fig represents this by  a firstline record where
#    field 1 = 2 (always 2)
#    field 2 = 1 (polyline)
#    field 3 = 0 (style is a firm line)
#    field 7 = 0 (depth is zero [top level])
#    field 14 = 0 (no forward arrow)
#    field 15 = 0 (backward arrow) 
#    field 16 = Number of point in line (points=segments+1)
#  a data field starting with a tab followed by  points (x,y) cordinates
#
#
# Strokes are polylines with 1 line segment and and no arrow 
#  fig represents this by  a firstline record where
#    field 1 = 2 (always 2)
#    field 2 = 1 (polyline)
#    field 3 = 0 (style is a firm line)
#    field 14 = 0 (no forward arrow)
#    field 15 = 0 (backward arrow) 
#    field 16 = Number of point in line =2
#  a data field starting with a tab followed by 2 (x,y) cordinates
#
# Arrows are polylines with 1 line segment and an arrow 
#  fig represents this by  a firstline record where
#    field 1 = 2 (always 2)
#    field 2 = 1 (polyline)
#    field 3 = 0 (style is a firm line)
#    field 14 = 1 for a forward arrow  
#    field 15 = 1 for a backward arrow 
#  an additional data files
#  a data field starting with a tab followed by 2(x,y) cordinates
#
# Components appear in two files -- the fig file and the lbl file
# these two files are concatenated with the lbl file first
#  	The lbl file represents components by 3 fields
#		field  1 is the name
#		field  2 is the CR name
#		field  3 is the CR arguments
#  	The fig file represents components by 14 fields
#             field 1 = 4
#		fields 12 and 13 are the coordinates
#		field 14 is the type:name string terminated by \001
# To prevent text being confused with components, components consist
# of alphanumeric characters and : and _ only.
# The lbl file is used to sort the components.
##############################################################

function exact_match(name1, name2) {
  return ((match(name1,name2)>0)&&(length(name1)==length(name2)))
    }

function process_lbl() {
# This puts the components in the lable file at the top of the list
# and saves up the corresponding CR and arguments
# note that there may be more than one component per label
  if ((match($1,"%")==0)&&(NF>0))
    { 
      i_label++;
      name = $1;
      CR   = $2;
      args = $3;
      label[i_label,1] = name; 
      label[i_label,2] = CR;
      label[i_label,3] = args
	}
}

function fig_info() {
# Grabs the fig-file information for a component
  return(sprintf("%s %s %s %s %s %s %s %s %s %s %s ", \
		 $1, $2, $3, $4, $5, $6, $7, \
		 $8, $9, $10, $11))
	 }

function process_text() {
# The text string is field 14 onwards
  str = $14; 
  for (i=15; i<=NF; i++) {
    str = sprintf("%s %s", str, $i)
      }

# The depth is field 4
  depth = $4; 

# It is terminated by \001 - so delete this termination
  str = substr(str,1,length(str)-4);

# A component string contains only alphanumeric  _ and :
  isa_plain_component = match(str, component_regexp)==0;
# It must also be specified at depth 0
  isa_plain_component = isa_plain_component && (depth==0);

# A port is a string within []
  isa_port = (match(str, port_regexp)>0)

# It must also be specified at depth 0
  isa_port = isa_port && (depth==0);

# A port component is SS followed by : followed by a port string
  isa_port_component = 0;
  if (match(str, delimiter)) {
    split(str,a,delimiter);
    isa_port_component = (exact_match(a[1], "SS"))&&
      (match(a[2], port_regexp)>0)
      }
# It must also be specified at depth 0
  isa_port_component = isa_port_component && (depth==0);

# A component is a plain or a port component
  isa_component = isa_plain_component||isa_port_component;

# Coordinates in fields 12 & 13
  x_coord = $12;
  y_coord = $13;

# Do the ports
  if (isa_port) {
    i_port++;
    port_name = str;
    ports[i_port] = sprintf("%s %s %s", x_coord, y_coord, port_name);
  }

# Do the port components
  if (isa_port_component) {
    i_port_component++;
    # Port number is the bit between the []
    port_label  = substr(a[2],2,length(a[2])-2);
    x_port[i_port_component] = x_coord;
    y_port[i_port_component] = y_coord;
    info_port[i_port_component] = fig_info();
    port_labels[i_port_component] = port_label;
  }

# Do the plain components
  if (isa_plain_component) {
    i_text++;

# Get repetitions (if any)
    if (match(str,repetition_regexp) > 0) {
      split(str,b,repetition_delimiter);
      repetitions = b[2];
      str = b[1];
	}
    else {
      repetitions = "1";
    };

    named_component = (match(str,delimiter) > 0);
    if (named_component) {
      split(str,a,delimiter);
      type = a[1];
      name = a[2];

# Check  if name is in label file and if used already
      found = 0; name_used = 0;
      for (i=1; i<=i_label; i++) {
	lname = label[i,1];
	if ( exact_match(name,lname) ) {
	  found = 1;
	  if (name in used) {
	    name_used = 1;
	    CR = label[i,2];
	    args = label[i,3];
	  }
	  else {
	    used[name] = 1
	      }
	  break
	    }
      }

	if (!found) {
	  if (isa_plain_component) {
	    printf(warning_f, name)
	  }
	  i_label++;
	  CR = default_cr;
	  args = "";
	  label[i_label,1] = name; 
	  label[i_label,2] = CR;
	  label[i_label,3] = args
	    }

# Give it a new entry if already used
#  -- also tell user as it is an error now(?)
      if (name_used) {
	printf(warning_u, name);
	i_label++;
	i_name++;
	name = sprintf("%1.0f", i_name);
	label[i_label,1] = name; 
	label[i_label,2] = CR;
	label[i_label,3] = args
	  }
    }

# Unnamed component
    if (named_component==0) {
      i_name++;
      name = sprintf("%1.0f", i_name);
      type = str;
      i_label++;
      label[i_label,1] = name;
      label[i_label,2] = default_cr;
      label[i_label,3] = default_args
	}

# Save in associative arrays by name	
    comp_type[name] = type;
    x[name] = x_coord;
    y[name] = y_coord;
    info[name] =  fig_info();
    reps[name] = repetitions;
  }

}

#Euclidean length of a line between (first_x,first_y) and (second_x,second_y)
function line_length(first_x,first_y,second_x,second_y) {
  x_length = second_x-first_x;
  y_length = second_y-first_y;
  return sqrt( x_length*x_length +  y_length*y_length );
}

# Returns 1 if (bond) arrow at beginning of field or 2 if arrow at end of field
function arrow_end(first_x,first_y,second_x,second_y,penultimate_x,penultimate_y,last_x,last_y) {
  if ( line_length(first_x,first_y,second_x,second_y) < line_length(first_x,first_y,second_x,second_y) ) {
  return 1
  }
  else {
  return 2
  }
}

function process_bond() {

  arg_count++;
  if ( (arg_count-arrow)==1 ) 
    {

#Save up bond coords - no arrow and more segments than a stroke has.
# Allows for bent bonds - but just write out the relevant coordinates
      if ( (!arrow)&& (NF>2*stroke_coords+1) ) {
	i_bond++;
        a_end = arrow_end($2,$3,$4,$5,$(NF-3),$(NF-2),$(NF-1),$NF);
        if (a_end==1) {
	bonds[i_bond] = sprintf("%s %s %s %s %s %s", \
				$2, $3, $4, $5, $(NF-1), $(NF));
}
else {
	bonds[i_bond] = sprintf("%s %s %s %s %s %s", \
				$2, $3, $(NF-3),$(NF-2),$(NF-1),$NF);
}
      }
      
#Save up arrow coords 
      if ( (arrow)&&(NF==(2*arrow_coords+1)) ) {
	i_arrow++;
	arrows[i_arrow] = sprintf("%s %s %s %s",  $2, $3, $4, $5);
      }
      
#Save up stroke coords
      if ( (!arrow)&&(NF==(2*stroke_coords+1)) ) {
	i_stroke++;
	strokes[i_stroke] = sprintf("%s %s %s %s",  $2, $3, $4, $5);
      }
    }
}

function write_fig() {
# Create _fig.fig file from _abg file - not components
# and write out the components in a _cmp.fig file
# and write out the bonds in a _bnd.fig file

#Everything except components
  if ( ((object!=text)||(isa_component==0)) ) {
    #Replace the data_symbol
    if (exact_match($1,data_symbol)) {
      field_1 = out_data_symbol
	}
    else {
      field_1 = $1
      }

    printf field_1   >> fig_file
      for (i=2; i<=NF; i++)
	printf(" %s", $i)  >> fig_file;
    printf("\n") >> fig_file
    }

# Header
  if ( NF<3 ) {
	printf("%s", $1)  >> head_file;
        for (i=2; i<=NF; i++)
	  printf(" %s", $i)  >> head_file;
    printf("\n") >> head_file
    }
# Bonds
  if (isa_bond) {

    #Replace the data_symbol
    if (exact_match($1,data_symbol)) {
      field_1 = out_data_symbol
	}
    else {
      field_1 = $1
	}

    printf field_1   >> bnd_file
      for (i=2; i<=NF; i++)
	printf(" %s", $i)  >> bnd_file;
    printf("\n") >> bnd_file
      }

# Components & ports
  if ( isa_component||isa_port ) {
        for (i=1; i<=NF; i++)
	printf(" %s", $i)  >> cmp_file;
    printf("\n") >> cmp_file
}
}

function process_fig() {
# Test for the fig format first line and data line
  data_line = (match($1,data_symbol)>0);
  first_line = (data_line==0)&&(NF>min_line_length);

#Process firstline
  if (first_line) {
    object = $1;
    sub_type = $2;
    style = $3;
    zero_depth = (($7==0)&&(object=polyline)) || (($4==0)&&(object=text));
    f_arrow = ($14==1)&&(object=polyline);
    b_arrow = ($15==1)&&(object=polyline);
    arrow = f_arrow||b_arrow;
    arg_count = 0;
  }

#Process text
  if (object==text) {
    process_text()
      }

# Process bond
isa_bond = (zero_depth &&\
       (object==polyline)&& \
       (sub_type==sub_polyline)&& \
       (style==firm_style) \
       );   

if ( isa_bond && data_line)
{
    process_bond()
      }   

if (isa_fig_file){
     write_fig()
    }

    }

BEGIN {
  sys_name = ARGV[1];
  delete ARGV[1];

  b_file = sprintf("%s_rbg.m", sys_name);
  c_file = sprintf("%s_cmp.m", sys_name);
  fig_file = sprintf("%s_fig.fig", sys_name);
  cmp_file = sprintf("%s_cmp.fig", sys_name);
  bnd_file = sprintf("%s_bnd.fig", sys_name);
  head_file = sprintf("%s_head.fig", sys_name);

  warning_f = "WARNING %s \t in fig file but not lbl file  - using\n";
  warning_l = "WARNING %s \t in lbl file but not fig file  - ignoring\n";
  warning_p = "ERROR system ports are not consecutively numbered\n";
  warning_u = "ERROR %s has already appeared in the fig file\n";

  data_symbol = "----";
  out_data_symbol = "\t";
  default_cr = "";
  default_args = "";
  delimiter = ":";
  repetition_delimiter = "*";
  repetition_regexp = "\\*";
  q = "\047";
  terminator = "\\001";
  component_regexp = "[^0-9a-zA-Z_:\*-]";
  port_regexp = "^\[[a-zA-Z0-9_]*\]";
  nonport_regexp = "[a-zA-Z]";
    
  isa_fig_file = 0;
  min_line_length = 10;
  object = 0;
  polyline = 2;
  sub_polyline=1; 
  firm_style = 0;
  text = 4;
  bond_coords = 3;
  stroke_coords = 2;
  arrow_coords = 2;

  i_bond = 0;
  i_port = 0;
  i_stroke = 0;
  i_arrow = 0;
  i_label = 0;
  i_text = 0;
  i_name = 0;
  i_port_component = 0;

}
{
# Start of .fig file?
  if ( (NF>0) && (match("#FIG", $1) > 0) ) {
    isa_fig_file=1;
  }

  if (isa_fig_file==0) {
    process_lbl()    
      }
  else {
    process_fig()
      }
}

END {
#Print out the matlab functions
  printf("function [rbonds, rstrokes,rcomponents,port_coord,port_name,port_list] = %s_rbg\n", sys_name) > b_file;
  printf("%% [rbonds,rstrokes,rcomponents,port_coord,port_name,port_list] = %s_rbg\n", sys_name) > b_file;
  printf("%% Generated by MTT\n\n") > b_file;

  printf("function [comp_type, name, cr, arg, repetitions] = %s_cmp(i)\n",\
		 sys_name) > c_file;
  printf("%% [comp_type, name, cr, arg, repetitions] = %s_cmp\n", sys_name) > c_file;
  printf("%% Generated by MTT\n\n") > c_file;

  printf("rbonds = [\n") >> b_file;
  for (i = 1; i <= i_bond; i++)
    print  bonds[i] >> b_file;
  for (i = 1; i <= i_arrow; i++)
    print  arrows[i], "-1 -1" >> b_file;
  printf("];\n") >> b_file;
  
  printf("rstrokes = [\n") >> b_file;
  for (i = 1; i <= i_stroke; i++)
    print  strokes[i] >> b_file;
  printf("];\n") >> b_file;

  printf("rcomponents = [") >> b_file;
  j = 0;


# Do the port components, in order of appearance, first
  for (i = 1; i <= i_port_component; i++) {
    port_type = "SS";
    name = sprintf("[%s]", port_labels[i]);
    cr   = "MTT_port";
    arg  = i;

    if (length(x_port[i])==0)
      printf(warning_p);
    else {
      j++;
      print x_port[i], y_port[i], info_port[i] >> b_file;
      printf("if i==%1.0f\n", j) 	>> c_file;
      printf("\tcomp_type = %s%s%s;\n", q, port_type, q) >> c_file;
      printf("\tname = %s%s%s;\n", q, name, q) >> c_file;
      printf("\tcr = %s%s%s;\n", q, cr, q) >> c_file;
      printf("\targ = %s%s%s;\n", q, arg, q) >> c_file;
      printf("\trepetitions = 1;\n") >> c_file;
      print "end" >> c_file
	}
    
  }

# Now do the ordinary components (in no particular order)
  for (i = 1; i <= i_label; i++) {
    name = label[i,1];
    cr   = label[i,2];
    arg  = label[i,3];
    
    if (length(x[name])==0)
      printf(warning_l, name);
    else {
      j++;
      print x[name], y[name], info[name] >> b_file;
      printf("if i==%1.0f\n", j) 	>> c_file;
      printf("\tcomp_type = %s%s%s;\n", q, comp_type[name], q) >> c_file;
      printf("\tname = %s%s%s;\n", q, name, q) >> c_file;
      printf("\tcr = %s%s%s;\n", q, cr, q) >> c_file;
      printf("\targ = %s%s%s;\n", q, arg, q) >> c_file;
      printf("\trepetitions = %s;\n",  reps[name]) >> c_file;
      print "end" >> c_file
	}
  }
  printf("];\n") >> b_file;

# Print the (internal) ports list
  printf("port_coord = [\n") >> b_file;
  for (i = 1; i <= i_port; i++) {
    split(ports[i],a, " ");
    printf("%s %s\n", a[1], a[2]) >> b_file;
  }
  printf("];\n\n") >> b_file;

  printf("port_name = [\n") >> b_file;
  for (i = 1; i <= i_port; i++) {
    split(ports[i],a, " ");
    printf("'%s'\n", a[3]) >> b_file;
  }
  printf("];\n\n") >> b_file;

# Print the (external) port list
#  printf("n_ports = %1.0f;\n", i_port_component) >> b_file;
  printf("port_list = [\n") >> b_file;
  for (i = 1; i <= i_port_component; i++) 
    printf("'[%s]'\n", port_labels[i]) >> b_file;
  printf("];\n\n") >> b_file;
  
}


