package MpmasQLConfigReader;
use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use CtUtilities qw(trim is_in_list);
use MpmasQLClassDefinition;
use MpmasQLElementDefinition;
use MpmasQLFieldDefinition;
use MpmasQLTableDefinition;
use SimpleObjectRegistry;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(ReadIni ReadCfg ReadVar ReadCll ReadDst);

#### Hard-coded defaults

## 
my $ini_spec_tables = "RHS_SOILTAB|SCENDEF|RHSDEF|RHSLIST|SCENLIST|COMMON";


######## Read in the conversion control settings
sub ReadIni {
	my $fn = shift;;
	my $inihash ={};

	my $im = -1; # indicates which input block is currently read
	my $com = 0;
	my $instring = 0;
	my $curfield;
	open (my $ini , "<" , $fn) or die "\nError when trying to open conversion control file:\n $!\n";
	READINGIN: while (<$ini>) {	
		my $ir = $_;
		#check for start and end of multiline comments
		if ($com == 1) {
			if ($ir =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n"
			}	
			else {
				next;
			}
		}
		if ($ir =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			$com = 1;
			next ;
		}
		#read in conversion control settings
		next READINGIN if ($ir =~ /^\s*$/ or $ir =~ /^\s*\'/ or $ir =~ /^\s*\[/);
		unless ($instring) {
		      my ($inifield, $aux) = split /=/, $ir,2;
		      next READINGIN unless $aux;
		      $inifield = trim($inifield);
		      if ($inifield =~ /^\s*($ini_spec_tables)\s*$/) {
			    $curfield = $1;
			    $instring = 1;
			    $aux =~ /^\s*\(\s*(\w+)\s*\)\s*{([^}]*)?(\})?/;
			    print STDERR "Error in ini file: invalid table definition\n" and exit(1) unless (defined $1);
			    $inihash->{$curfield} = MpmasQLTableDefinition->create(id => $curfield, type => $1);
			    $inihash->{$curfield}->concat_to_content($2) if (defined $2);
			    $instring = 0 if (defined $3);
		      }
		      elsif ($aux =~ /,/ and not $aux =~ /(#|\[)/) { 
			      my $aux1 = trim($aux);
			      my @content;	
			      my @precontent = split /,/, $aux1; 	
			      for my $i (0..$#precontent){
						$content[$i] = trim($precontent[$i]);
			      }
			      $inihash->{trim($inifield)} = [@content];
		      }
		      else { 
			      my $aux1 =	trim($aux);		
			      $inihash->{trim($inifield)} = $aux1;
		      }
		}
		else {
			    $instring = 0 if ($ir =~ s/\s*\}\s*$//);
			    $ir =~ s/^\s*//;
			    $ir =~ s/\s*$//;
			    $inihash->{$curfield}->concat_to_content(" ".$ir) if (defined $ir);
			    undef $curfield if ($instring == 0);
		}
	}
	#expand $inihash->{DBGMILPS} into a list if only one entry
#	if ($inihash->{DBGMILPS} and not ref $inihash->{DBGMILPS}) {
#		my $s4 = "$inihash->{DBGMILPS}";
#		$inihash->{DBGMILPS} = [$s4];
#	}

	#expand $inihash->{SKIP} into a list if only one entry
	if ($inihash->{SKIP} and not ref $inihash->{SKIP}) {
		my $s4 = "$inihash->{SKIP}";
		$inihash->{SKIP} = [$s4];
	}

	return $inihash;
}

sub ReadCfg {
	my ($cfgname, $baseconfigs) = @_;
	my $cfg;
	print "\e[K \rReading configuration from $cfgname\r";	
	open ($cfg , "<" , $cfgname) or print STDERR "\nError when trying to open $cfgname: $!\n" and exit(1);
	my $com = 0;
	CFGIN: while (<$cfg>) {
		my $ir = $_;
		next CFGIN if ($ir =~ /^\s*$/   or $ir =~ /^\s*\'/ or $ir =~ /^\s*\[/);
		if ($com == 1) {
			if ($ir =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n"
			}	
			else {
				next;
			}
		}
		if ($ir =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			$com = 1 unless ($ir =~ /\*\/\s*$/);
			next ;
		}
		my ($inifield, $aux) = split /=/, $ir, 2;
		next CFGIN unless $aux;
		$inifield = trim($inifield);	
		if ($aux =~ /,/ and not $aux =~ /[[#]/){	
			my $aux1 =	trim($aux);
			my @content;	
			my @precontent = split /,/  , $aux1; 	
		
			for my $i (0..$#precontent){
				$content[$i] = trim($precontent[$i]);
			}
			$baseconfigs->{trim($inifield)} = [@content];
		}
		else { 
			my $aux1 =	trim($aux);		
			$baseconfigs->{trim($inifield)} = $aux1;
		}
	} #END CFGIN
	close $cfg;
	
	return $baseconfigs;
}
sub ReadVar {
	my ($varfname, $baseconfigs) = @_ ;
	#$mpmasoff = 0 unless (defined $mpmasoff);

	##### READ IN VARIABLES FILE
	print "\e[K \rReading model data file $varfname...\r";
	my $basesetup = {}; #used as reference to anonymous hash containing entries of order and variables section
	my $baseglobals = {};
	my $basecomps = {}; #used as reference to anonymous hash containing entries of components section
	my $basetables = {}; #used as reference to anonymous hash containing entries of tables section
	my $globalrank = {}; my $globalcount = 0; #used to preserve order of global reading
	 
	open (my $varf , "<" , $varfname) or die "\nError when trying to open model data file: $!\n";
	my $im = -1; # keeps track in what kind of block the routine currently is
	my $com = 0;
	my ($curtab, $instring) = (undef,0);
	my $linecount = 0;
	COMPIN: while (<$varf>) {	
		
		my $ir = $_;
		chomp($ir);
		#skip empty and commented lines
		next COMPIN if ($ir =~ /^\s*$/   or $ir =~ /^\s*\'/); 
		if ($com == 1) {
			if ($ir =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n";
				next;
			}	
			else {
				next;
			}
		}
		if ($ir =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			 $com = 1 unless ($ir =~ /\*\/\s*$/);
			next ;
		}

		# switch status and start next loop if section heading appears
#		if ($ir =~ /^\s*\[ORDER\]/) {
#			$im = 1;
#			next COMPIN;
#		}
		if ($ir =~ /^\s*\[\s*GLOBALS\s*\]/) {
			$im = 2;
			next COMPIN;
		}
		elsif ($ir =~ /^\s*\[\s*MPMAS\s*PARAMETERS\s*\]/) {
			$im = 7;
			next COMPIN;
		}
		elsif ($ir =~ /^\s*\[\s*MPMAS\s*TABLES\s*\]/) {
			$im = 6;
			next COMPIN;
		}
		elsif ($ir =~ /^\s*\[\s*USER\s*TABLES\s*\]/) {
			$im = 3;
			next COMPIN;
		}
		elsif ($ir =~ /^\s*\[\s*USER\s*VARIABLES\s*\]/) {
			$im = 4;
			next COMPIN;
		}
		elsif ($ir =~ /^\s*\[\s*INSTANCES\s*\]/ ) {
			$im = 5;
			next COMPIN ;
		}

		#split the line into name and content
		if ($im == 2){#GLOBALS
			my ($inifield, $aux) = split /=/, $ir, 2;
			next COMPIN unless (defined $aux);
			$baseglobals->{trim($inifield)} =trim( $aux);
			$globalrank->{trim($inifield)} = $globalcount++;
		}
		if ($im == 3){#USER TABLES
			unless ($instring) { 
			    $instring = 1;
			    $ir =~ /^\s*(\w+)\s*=\s*\(\s*(\w+)\s*,\s*(\d+)\s*\)\s*{([^}]*)?(\})?/;
			    print STDERR "Error in model data file (line $.): invalid table definition\n" and exit(1) unless (defined $1);
			    $curtab = $1;
			    $basetables->{$curtab} = MpmasQLTableDefinition->create(id => $curtab, type => $2, keys => $3);
			    $basetables->{$curtab}->concat_to_content($4) if (defined $4);
			    $instring = 0 if (defined $5);
			}
			else {
			    $instring = 0 if ($ir =~ s/\s*\}\s*$//);
			    $ir =~ s/^\s+//;
			    $ir =~ s/\s+$//;
			    $basetables->{$curtab}->concat_to_content(" ".$ir) if (defined $ir);
			    undef $curtab if ($instring == 0);
			}
		}
		if ($im == 4){#VARIABLES
			my ($inifield, $aux) = split /=/, $ir, 2;
			next COMPIN unless (defined $aux);
			$basesetup->{trim($inifield)} =trim( $aux);
		}
		if ($im == 5){#INSTANCES 
			my ($inifield, $aux) = split /=/, $ir, 2;
			next COMPIN unless $aux;
			$basecomps->{trim($inifield)} =trim( $aux);
		}
		if ($im == 6){#MPMAS TABLES
			unless ($instring) { 
			    $instring = 1;
			    $ir =~ /^\s*(\w+)\s*=\s*\(\s*(\w+)\)\s*{([^}]*)?(\})?/;
			    print STDERR "Error in model data file (line $.): invalid table definition\n" and exit(1) unless (defined $1);
			    $curtab = $1;
			    $baseconfigs->{$curtab} = MpmasQLTableDefinition->create(id => $curtab, type => $2);
			    $baseconfigs->{$curtab}->concat_to_content($3) if (defined $3);
			    $instring = 0 if (defined $4);
			}
			else {
			    $instring = 0 if ($ir =~ s/\s*\}\s*$//);
			    $ir =~ s/^\s+//;
			    $ir =~ s/\s+$//;
			    $baseconfigs->{$curtab}->concat_to_content(" ". $ir) if (defined $ir);
			    undef $curtab if ($instring == 0);
			}
			
		}
		if ($im == 7){#MPMAS PARAMETERS
			my ($inifield, $aux) = split /=/, $ir, 2;
			next COMPIN unless (defined $aux);
			if ($aux =~ /,/ and not $aux =~ /[[#]/){	
				my $aux1 =	trim($aux);
				my @content;	
				my @precontent = split /,/  , $aux1; 	
			
				for my $i (0..$#precontent){
					$content[$i] = trim($precontent[$i]);
				}
				$baseconfigs->{trim($inifield)} = [@content];
			}
			else { 
				my $aux1 =	trim($aux);		
				$baseconfigs->{trim($inifield)} = $aux1;
			}
		}

	}
	close $varf;
	
	return $basesetup , $baseglobals, $basecomps, $basetables, $baseconfigs, $globalrank;
	
}

sub ReadCll {
	my ($cllname, $baseclassregistry,$baseconfigs, $verbose, $mpmasoff) = @_;
	$verbose = 0 unless(defined $verbose);
	$mpmasoff = 0 unless (defined $mpmasoff);

	#########	READ IN STRUCTURE FILE
	print "\e[K \rReading model structure from $cllname ...\r";
	open (my $cll , "<" , $cllname) or print STDERR "\nError when trying to open model structure file:\n $!\n" and exit(1);
	my $cmod = -10; #keeps track of definition level status
	my ($curClass, $curElement, $curClassId, $curElementId);
	my $linecount = 0;
	my $vfcount = 0;
	my $com = 0;
	my ($atstring, $ctstring) = (" ", " ");
	my $iarank = 0;
	my $instances;
	CLASSIN: while (<$cll>){
		$linecount = $linecount + 1;
		my $inline = $_;
		chomp($inline);
		next CLASSIN if ($inline =~ /^\s*$/ or $inline =~ /^\s*\'/);
		if ($com == 1) {
			if ($inline =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n"
			       next
			}	
			else {
				next;
			}
		}
		if ($inline =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			$com = 1 unless ($inline =~ /\*\/\s*$/);
			next ;
		}
		if ($inline =~/^\s*\[\s*ACTIVITY\s*TYPES\s*\]/) {
			$cmod = -4;
			print "\nACTIVITY TYPES:\n" if $verbose;
			next;
		}
		if ($inline =~/^\s*\[\s*CONSTRAINT\s*TYPES\s*\]/) {
			$cmod = -3;
			print "\nCONSTRAINT TYPES:\n" if $verbose;
			next;
		}
		if ($inline =~/^\s*\[\s*DEFAULTS\s*\]/) {
			$cmod = -2;
			$curClassId = "DEFAULT";
			unless (defined $baseclassregistry->get_object_by_id($curClassId)) {
				my $newclass = MpmasQLClassDefinition->create($curClassId);
				$baseclassregistry->add_objects($newclass);
			}
			$curClass = $baseclassregistry->get_object_by_id($curClassId);
			print "\nCLASS:".$curClassId."\r\n" if $verbose;
			next;
		}
		if ($inline =~/^\s*\[\s*INSTANCES\s*\]/) { #not for main farmer decision matrix
			$cmod = -5;
			print "\nINSTANCES:\n" if $verbose;
			next;
		}
		if ($inline =~/^\s*\[\s*MODEL\s*\]/) {
			$cmod = 0;

			next;
		}
		if ($cmod == -5) { #not for main farmer decision matrix
			$inline =~ s/^\s*//;
			$inline =~ s/\s*$//;
			my ($inifield, $aux) = split /#?=/, $inline, 2;
			next CLASSIN unless $aux;
			$instances->{trim($inifield)} =trim( $aux);
			print "$inifield -> $aux\n" if $verbose;
		}
		if ($cmod == -4) {
			$inline =~ s/^\s*//;
			$inline =~ s/\s*$//;
			$atstring .= $inline ;
			print $inline."\n" if $verbose;
		}
		if ($cmod == -3) {
			$inline =~ s/^\s*//;
			$inline =~ s/\s*$//;
			$ctstring .= $inline ;
			print $inline."\n" if $verbose;
		}
		if ($cmod == 0)	{
			if ($inline =~ /^\s*#class\s*(\w+)\s*{/) {
				$curClassId = $1;
				unless (defined $baseclassregistry->get_object_by_id($curClassId)) {
					my $newclass = MpmasQLClassDefinition->create($curClassId);
					$baseclassregistry->add_objects($newclass);
				}
				$curClass = $baseclassregistry->get_object_by_id($curClassId);
				print "CLASS:".$curClassId."\r\n" if $verbose;
				$cmod = 1;
				next CLASSIN;
			}
			else {
				print STDERR "\nSyntax Error in structure file at line $linecount: only class definitions allowed at level zero (#class); Hint: check curly brackets. \n closing curly brackets have to be on a separate line \n Opening curly brackets have to be on the same line as the class or element definition\n\n";
				exit(1);
			}
		}
		if ($cmod == 1 or $cmod == -2){
			if ($inline =~ /^\s*\}\s*/){
				print STDERR "\nSyntax Error in structure file at line $linecount: unmatched right curly bracket in DEFAULTS section\n" and exit(1) if ($cmod == -2);
				print "End CLASS:".$curClassId."\n\n" if $verbose;
				undef $curClassId; undef $curClass;
				$cmod = 0;
				next CLASSIN;
			}
				
			elsif ($inline =~ /^\s*#inherits\s*\(([\w,*_\s]*)\)\s*from\s*(\w*)\s*/ or $inline =~/^\s*#(?:has|have)\s*\(([\w,*_\s]*)\)\s*elements\s+like\s*(\w*)\s*/ ){
				my $elementlist = $1;
				my $parentId = $2;
				my $parent = $baseclassregistry->get_object_by_id($parentId);
				print STDERR "Warning!!! $curClass inherited nothing from $parentId, as class $parentId was unknown at parse time!\n Note: Parent class has to defined before you can inherit from it. Check order of classes.\n" unless ($parent);
				my @elements_to_bequeath;
				if ($elementlist =~ /\*/)  {
					@elements_to_bequeath = $parent->elements;
				}
				else {
					my @list = split /\s*,\s*/,$elementlist;
					for my $cle (@list) {
					      my $ce = $parent->element($cle);
					      print STDERR "Error while copying elements to $curClassId, $cle is not an element of $parentId\n" and exit(1) unless (ref $ce);
					      push @elements_to_bequeath, $ce;
					}
				}
				for my $ce (@elements_to_bequeath){
					print "\t $curClassId inherits $ce from $parentId\n" if $verbose;
					
					$curClass->add_elements( dclone($ce))
				}
				$cmod = 1;
				next CLASSIN;
			}
			elsif ($inline =~ /^\s*#instance_attribute>\s*(\w+)\s*#=\s*(.+)$/){
				my $clattr = $1;
				my $cont = $2;
				my ($tcont, $cont_verbose, $cont_verbose_for) = MpmasQLFunctionLanguage->translate_to_perl($cont);
				my $newfield = MpmasQLFieldDefinition->create(
						  id => $clattr,
						  fieldname => $clattr,
						  content   => $tcont,
						  type	    => "scalar",
						  fn_verbose => undef,
						  cont_verbose => $cont_verbose,
						  fn_verbose_for => undef,
						  cont_verbose_for => $cont_verbose_for,
						  rank => $iarank++
				);
				$curClass->add_instance_attributes($newfield);
			}
			else {
				my $elementtype;
				my $by;

				my @knownelements = (qw(activity constraint asset perennial livestock timepreference_penalty sos));

				if ($inline =~ /^\s*#(\w+)\s+(\w+)\s*([^{]*)\s*{/ ){
				      $elementtype = $1;
				      $curElementId = $2;
				      $by = $3;
				      $by =~ s/^\s*//;
				      $by =~ s/\s*$//;
				}
				else {
				      print STDERR "Syntax error in structure file at line $linecount:\n $inline\n is not a valid element definition\nOnly element definitions allowed at level 1, check curly brackets.\n closing curly brackets have to be on a separate line \n Opening curly brackets have to be on the same line as the class or element definition\n";
				      exit(1);
				}
				my $shortelementtype = $elementtype;
				$shortelementtype =~ s/by$//;
				unless (is_in_list($shortelementtype, \@knownelements)) {
					print STDERR "\nSyntax error in structure file at line $linecount: Unknown element type: $elementtype\n";
					exit(1);
				}
				print STDERR "No by-list specified for $elementtype $curElementId\n" and exit(1) if ($elementtype =~ /by\s*$/ and $by =~ /^\s*$/);


				if (not defined $curClass->element($curElementId) ) {
					my $newelement = MpmasQLElementDefinition->create($curElementId, $elementtype);
					$curClass->add_elements($newelement);

				}
				$curElement = $curClass->element($curElementId);
				$curElement->set_by_construct($by) if ($by);
				print "\t ELEMENT \"$elementtype\" $curElementId\r\n" if $verbose;
				$cmod = 2;
				next CLASSIN;
			}
			
		}
		if ($cmod == 2){
			if ($inline =~ /^\s*\}\s*/ ){
				$curElement->set_vfc($vfcount);
				print "\t\t ELEMENT has ".$curElement->vfc." DYNAMIC FIELD(S)\n" if $verbose; 
				print "\t End ELEMENT:".$curElementId."\r\n" if $verbose;
				$vfcount = 0;
				undef  $curElementId;
				undef $curElement;
				$cmod = 1;
				next CLASSIN;
			}
			elsif ($inline =~ /^\s*#resultgroup\s*>\s*(\w+)\s*#=\s*(.+)$/){
				my $classification = $1;
				my $group = $2;
				print "\t\t GROUP FIELD classification: $classification, GROUP: $group \n" if $verbose;
					my ($tgroup, $group_verbose) =  MpmasQLFunctionLanguage->translate_to_perl($group);
					$curElement->classify($classification,$tgroup, $group_verbose) ;
					my @kindof;
					if ($curElement->type =~ /activity/) {
						push @kindof , "activity";
					}
					if ($curElement->type =~ /constraint/) {
						push @kindof , "constraint";
					}
					if ($curElement->type =~ /perennial/) {
						push @kindof , "constraint";
						push @kindof , "activity";
					}
					if ($curElement->type =~ /livestock/) {
						push @kindof , "constraint";
						push @kindof , "activity";
					}
					if ($curElement->type =~ /timepreference_penalty/) {
						push @kindof , "constraint";
						push @kindof , "activity";
					}
					for my $kndof (@kindof) {
						$baseconfigs->{Groupings}->{$kndof}->{$classification} = {} if (not exists $baseconfigs->{Groupings}->{$kndof}->{$classification});
						$baseconfigs->{Groupings}->{$kndof}->{$classification} += 1;
					}
					next CLASSIN;
			}
			elsif ($inline =~ /#=/){
				(my $fn , my $cont) = split("#=", $inline, 2);
				$fn = trim($fn);
				$cont = trim($cont);
				#Determine field type: standard: scalar, $$ list
				my $fieldtype = "scalar";
				$fieldtype = "list" if ($fn =~ s/^\$\$//);
				my $subelement;
				if ($fn =~ /^#\s*(\w+)\s*>\s*(.*)/) {
					$subelement = $1;
					#switch tempref to subarray of current element for compound classes
					$fn = $2;
				}
				my ($fn2, $tfn, $fn_verbose, $fn_verbose_for);
				if ($fn =~ /#/){
					$vfcount = $vfcount + 1;
					$fn2 = "mult".$vfcount;
					print "\t\t DYNAMIC FIELD: $fn -> $cont\n" if $verbose;
					($tfn, $fn_verbose, $fn_verbose_for) = MpmasQLFunctionLanguage->translate_to_perl($fn);
				}
				else{
					print "\t\t CONSTANT FIELD:  $fn -> $cont\n" if $verbose;
					$fn2 = $fn;
					$tfn = $fn;
				}
				
				my ($tcont, $cont_verbose, $cont_verbose_for) = MpmasQLFunctionLanguage->translate_to_perl($cont);
				my $newfield = MpmasQLFieldDefinition->create(
						  id => $fn2,
						  fieldname => $tfn,
						  content   => $tcont,
						  type	    => $fieldtype,
						  fn_verbose => $fn_verbose,
						  cont_verbose => $cont_verbose,
						  fn_verbose_for => $fn_verbose_for,
						  cont_verbose_for => $cont_verbose_for
				);
				if ($subelement) {
				      $curElement->add_or_replace_subelement_field($subelement, $newfield);
				}
				else {
				      $curElement->add_or_replace_field($newfield);
				}
			}
			else {
				print STDERR "\nSyntax Error in Class File at line $linecount: only field definitions allowed at level 2;\nHint: Check curly brackets.\n Closing brackets have to be on a separate line. \nOpening brackets have to be on the same line as the class or element definition\n";
				exit(1);
			}
		}
		
	}
	close $cll;
		
	if (exists $baseconfigs->{ACTTYPEORDER} ) {
		$baseconfigs->{ACTTYPEORDER} =~ s/###/$atstring/;
	}
	else {
		  $baseconfigs->{ACTTYPEORDER} = $atstring;
	}
	if (exists $baseconfigs->{CONTYPEORDER} ) {
		$baseconfigs->{CONTYPEORDER} =~ s/###/$ctstring/;
	}
	else {
		  $baseconfigs->{CONTYPEORDER} = $ctstring ;
	}

	return $baseclassregistry, $baseconfigs, $instances;
}
sub ReadDst {
	my $fn = shift;
	my $verbose = shift || 0;
	
	my $inihash ={};
	my $setup ={};
	my $tables = {};
	my $disinfo;
	my $discollect = {};
	my $ppinfo = {};
	my $varrank = {};
	my $maps = {};
	my $stepsclude = {};
	my $im = -1; # indicates which input block is currently read
	my $block;
	my $step;
	my $cc = 0;
	my $vr = 0;
	my $com = 0;
	my $instring;
	my $curtab;
	open (my $ini , "<" , $fn) or die "\nError when trying to open .dst file \"$fn\"\n $!\n";
	 while (<$ini>) {
		my $ir = $_;
		chomp($ir);
#skip empty and commented lines
		next  if ($ir =~ /^\s*$/   or $ir =~ /^\s*\'/); 
		if ($com == 1) {
			if ($ir =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n"
				next;
			}	
			else {
				next;
			}
		}	 
		
		if ($ir =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			$com = 1;
			next ;
		}
		# switch status and start next loop if section heading appears
		if ($ir =~ /^\s*\[DB\]/) {
			$im = 1;
			next ;
		}
		elsif ($ir =~ /^\s*\[VARIABLES\]/) {
			$im = 2;
			next ;
		}
		elsif ($ir =~ /^\s*\[TABLES\]/) {
			$im = 3;
			next;
		}
		elsif ($ir =~ /^\s*\[DISTRIBUTION\]/) {
			$im = 4;
			next ;
		}
		elsif ($ir =~ /^\s*\[MAPS\]/ ) {
			$im = 5;
			next  ;
		}
		elsif ($ir =~ /^\s*\[CONTROL\]/ ) {
			$im = 6;
			next  ;
		}
		if ($im == 1){
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$inihash->{trim($key)} = trim($value);
		}
		elsif ($im == 2){#VARIABLES
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$setup->{trim($key)} =trim( $value);
			$varrank->{trim($key)} = $vr++;
		}
		elsif ($im == 3){#USER TABLES
			unless ($instring) { 
			    $instring = 1;
			    $ir =~ /^\s*(\w+)\s*=\s*\(\s*(\w+)\s*,\s*(\d+)\s*\)\s*{([^}]*)?(\})?/;
			    print STDERR "Error in .dst file (line $.): invalid table definition\n" and exit(1) unless (defined $1);
			    $curtab = $1;
			    $tables->{$curtab} = MpmasQLTableDefinition->create(id => $curtab, type => $2, keys => $3);
			    $tables->{$curtab}->concat_to_content($4) if (defined $4);
			    $instring = 0 if (defined $5);
			}
			else {
			    $instring = 0 if ($ir =~ s/\s*\}\s*$//);
			    $ir =~ s/^\s+//;
			    $ir =~ s/\s+$//;
			    $tables->{$curtab}->concat_to_content(" ".$ir) if (defined $ir);
			    undef $curtab if ($instring == 0);
			}
		}
		elsif ($im == 4) {#distribution 
			if (not defined $step) {
				if ($ir =~ /^\s*STEP\s*(\d+)\s*\{\s*$/) {
					$step = $1;
					$discollect->{$step} = {};
					$disinfo = $discollect->{$step};
				}
			}
			else {	
				if (not defined $block ) {
					if ($ir =~ /^\s*\w+\s*=\s*/) {
						my ($key, $value) = split /\s*=\s*/, $ir,2;
						$disinfo->{trim($key)} = trim($value);
					}
					if ($ir =~ /^\s*(\w+)\s+([^{]*)\{\s*$/) {
						$block = $1;
						$disinfo->{$block}->{list} = trim($2) if (defined $2);
						
					}
					elsif ($ir =~ /\}\s*$/) {
						$ir =~ s/\}\s*$//;
						undef $step;
					}
				}
				else {
					if ($ir =~ /\}\s*$/) {
						$ir =~ s/\}\s*$//;
						undef $block;
						$cc = 0;
					}	
					else {
						$disinfo->{$block}->{lines}->[$cc++] = trim($ir);
					}
				}
			}
		}
		elsif ($im == 5) {
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$maps->{trim($key)} =trim( $value);
		}
		elsif ($im == 6) {
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$stepsclude->{trim($key)} =trim( $value);
		}
	}
	return $inihash, $setup, $tables, $discollect, $ppinfo, $varrank, $maps, $stepsclude;
}
sub ReadTst {
	my $fn = shift;
	my $verbose = shift || 0;
	
	my $inihash ={};
	my $setup ={};
	my $tables = {};
	my $varrank = {};

	my $im = -1; # indicates which input block is currently read
	my $block;
	my $step;
	my $cc = 0;
	my $vr = 0;
	my $com = 0;
	my $instring;
	my $curtab;
	open (my $ini , "<" , $fn) or die "\nError when trying to open input file \"$fn\"\n $!\n";
	 while (<$ini>) {
		my $ir = $_;
		chomp($ir);
#skip empty and commented lines
		next  if ($ir =~ /^\s*$/   or $ir =~ /^\s*\'/); 
		if ($com == 1) {
			if ($ir =~ /\*\/\s*$/) {
				$com = 0;
				#print "comment ending in line $.\n"
				next;
			}	
			else {
				next;
			}
		}	 
		
		if ($ir =~ /^\s*\/\*/) {
			#print "comment starting in line $.\n";
			$com = 1;
			next ;
		}
		# switch status and start next loop if section heading appears
		if ($ir =~ /^\s*\[DB\]/) {
			$im = 1;
			next ;
		}
		elsif ($ir =~ /^\s*\[VARIABLES\]/) {
			$im = 2;
			next ;
		}
		elsif ($ir =~ /^\s*\[TABLES\]/) {
			$im = 3;
			next;
		}

		if ($im == 1){
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$inihash->{trim($key)} = trim($value);
		}
		elsif ($im == 2){#VARIABLES
			my ($key, $value) = split /\s*=\s*/, $ir,2;
			$setup->{trim($key)} =trim( $value);
			$varrank->{trim($key)} = $vr++;
		}
		elsif ($im == 3){#USER TABLES
			unless ($instring) { 
			    $instring = 1;
			    $ir =~ /^\s*(\w+)\s*=\s*\(\s*(\w+)\s*,\s*(\d+)\s*\)\s*{([^}]*)?(\})?/;
			    print STDERR "Error in .dst file (line $.): invalid table definition\n" and exit(1) unless (defined $1);
			    $curtab = $1;
			    $tables->{$curtab} = MpmasQLTableDefinition->create(id => $curtab, type => $2, keys => $3);
			    $tables->{$curtab}->concat_to_content($4) if (defined $4);
			    $instring = 0 if (defined $5);
			}
			else {
			    $instring = 0 if ($ir =~ s/\s*\}\s*$//);
			    $ir =~ s/^\s+//;
			    $ir =~ s/\s+$//;
			    $tables->{$curtab}->concat_to_content(" ".$ir) if (defined $ir);
			    undef $curtab if ($instring == 0);
			}
		}
	}
	return $inihash, $setup, $tables, $varrank;
}

1;
