package MpmasQLVariableProcessing;
use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use CtUtilities qw(queryhash extqueryhash);
use IndexedTable;
use MpmasQLFunctionLanguage;
use MpmasQLTableDefinition;

our @ISA = qw(Exporter);
our @EXPORT_OK = qw(adapt_for_scenario define_scenarios);

sub define_scenarios {
	my ($dbh, $s1, $s2, $s3)	= @_;    
	my $scenlist = [("_")]; 
	if (defined $s1 and UNIVERSAL::isa( $s1,"MpmasQLTableDefinition") ) {
		$s1->set_keys(1);
		my $x = $s1->transform($dbh);
		$scenlist = [$x->query([],0)] ;
	}
	my $scendef;
	if (defined $s2) {
		print STDERR "Error in ini: Invalid table definition for SCENDEF or RHSDEF\n" and exit(1) unless (UNIVERSAL::isa( $s2,"MpmasQLTableDefinition"));
	    $s2->set_keys(2);
	    $scendef = $s2->transform($dbh) if (defined $s2);
	}
	my $scencommon;
	if (defined $s3) {
		print STDERR "Error in ini: Invalid table definition for COMMON\n" and exit(1) unless (UNIVERSAL::isa( $s3,"MpmasQLTableDefinition"));
	    $s3->set_keys(1);
	    $scencommon = $s3->transform($dbh) if (defined $s3);
	}
	return ($scenlist, $scendef, $scencommon);
}

sub adapt_for_scenario {
	my ($curScen,$loudscen,$verbose,$dbh,$inihash, $scendef, $scencommon, $baseglobals, $basesetup, $baseinstances, $baseconfigs, $basetables, $globalrank, $baserhsdef, $singlemode) = @_;
	$verbose = 0 unless (defined $verbose);
	$loudscen = 0 unless (defined $loudscen);

	print "\e[K \rAdapting variables to scenario settings\r";
	my $mname = "$inihash->{PREFIX}"."$curScen";
	# copy hashes with baseline settings into specific settings for scenario
	my $globals =dclone($baseglobals);
	my $setup = dclone($basesetup);
	my $instances = dclone($baseinstances);
	my $configs = dclone($baseconfigs);
	my $tables = dclone($basetables);
	my $rhsdef;
	$rhsdef = dclone($baserhsdef) if (defined $baserhsdef) and (ref $baserhsdef );
	
	#replace values for common settings for all scenarios
	if (defined $scencommon ) {
		print "Replacing settings defined by COMMON\n" if $loudscen;
		for my $i ($scencommon->query([],0)) {
			if (exists $globals->{$i}) {
				$globals->{$i} = $scencommon->query([$i],2);
				print "Scenario $curScen: [global] $i = $globals->{$i}\n" if $loudscen;				
			}
			elsif (exists $configs->{$i} and (not defined ref $configs->{$i} or ref $configs->{$i} ne "MpmasQLTableDefinition")) {
				$configs->{$i} =  $scencommon->query([$i],2);
				print "Scenario $curScen: [configuration/MPMAS parameter] $i = $configs->{$i}\n" if $loudscen;				
			} 
			elsif (exists $configs->{$i} and ref $configs->{$i} eq "MpmasQLTableDefinition") {
				$configs->{$i}->set_content(  $scencommon->query([$i],2));
				print "Scenario $curScen: [MPMAS table] $i = $configs->{$i}\n" if $loudscen;				
			} 
			elsif(exists $setup->{$i} ){
				$setup->{$i} = 	 $scencommon->query([$i],2);
				print "Scenario $curScen: [user-defined variable/list] $i = $setup->{$i}\n" if $loudscen;
			} 
			elsif (exists $tables->{$i} ){
				$tables->{$i}->set_content( $scencommon->query([ $i],2));
				print "Scenario $curScen: [user-defined table] $i = $tables->{$i}\n" if $loudscen;
			} 
			elsif(exists $instances->{$i} ){
				$instances->{$i} = 	 $scencommon->query([$i],2);
				print "Scenario $curScen: [instance list] $i = $instances->{$i}\n" if $loudscen;
			}
			else{
				 print STDERR "Error: COMMON provides value for $i, but no such variable or table has been defined\n";
				 exit(1);
			}
		}
	}
	
	# replace values of scenario variables
	if (defined $scendef and scalar ($scendef->query([$curScen],0)) ) {
		print "Scenario entries for $curScen found in SCENDEF\n" if $loudscen;
		for my $i ($scendef->query([$curScen],0)) {
			if (exists $globals->{$i}) {
				$globals->{$i} = $scendef->query([$curScen, $i],3);
				print "Scenario $curScen: [global] $i = $globals->{$i}\n" if $loudscen;				
			}
			elsif (exists $configs->{$i} and (not defined ref $configs->{$i} or ref $configs->{$i} ne "MpmasQLTableDefinition")) {
				$configs->{$i} =  $scendef->query([$curScen, $i],3);
				print "Scenario $curScen: [configuration/MPMAS parameter] $i = $configs->{$i}\n" if $loudscen;				
			} 
			elsif (exists $configs->{$i} and ref $configs->{$i} eq "MpmasQLTableDefinition") {
				$configs->{$i}->set_content(  $scendef->query([$curScen, $i],3));
				print "Scenario $curScen: [MPMAS table] $i = $configs->{$i}\n" if $loudscen;				
			} 
			elsif(exists $setup->{$i} ){
				$setup->{$i} = 	 $scendef->query([$curScen, $i],3);
				print "Scenario $curScen: [user-defined variable/list] $i = $setup->{$i}\n" if $loudscen;
			} 
			elsif (exists $tables->{$i} ){
				$tables->{$i}->set_content( $scendef->query([$curScen, $i],3));
				print "Scenario $curScen: [user-defined table] $i = $tables->{$i}\n" if $loudscen;
			} 
			elsif(exists $instances->{$i} ){
				$instances->{$i} = 	 $scendef->query([$curScen, $i],3);
				print "Scenario $curScen: [instance list] $i = $instances->{$i}\n" if $loudscen;
			}
			else{
				 print STDERR "Error: Scenario $curScen provides value for $i, but no such variable or table has been defined\n";
				 exit(1);
			}
		}
	}
	elsif (defined $scendef ){

		print STDERR "Warning: Did not find scenario entries for $curScen in SCENDEF!!\n" ;
		if ($loudscen) {
			print join("\n", $scendef->query([],0));
		}
	}
	
	#interprete values for global variables
	for my $i (sort {$globalrank->{$a} <=> $globalrank->{$b} } keys %{$globals}) {
		MpmasQLFunctionLanguage->set_globals( $singlemode, $dbh, $globals,"",0);
		my $s = $globals->{$i};
		if ($s =~ /[-#+\/*~[\]\\^]+/) {
			my ($temp, $tempfor);
			eval { 	($temp, $tempfor) = MpmasQLFunctionLanguage->translate_and_interprete($s,{}); };
			print STDERR "Error when interpreting global list $i:\n $@" and exit(1) if $@;
			if ($#{$temp} > 0 or defined $tempfor->[0] ) {
				$globals->{$i} = $temp;
			}
			else {
				$globals->{$i} = $temp->[0];

			}
			if ($verbose) {
			    my $show = Dumper($globals->{$i});
			    $show =~ s/,\n\s*/, /sg;
			    $show =~ s/^\$VAR1\s*=//;
			  print "Global $i = $show";
			}
		}
	}	


	#####Replace all globals in config-file entries
	MpmasQLFunctionLanguage->set_globals( $singlemode, $dbh, $globals,"",0);
	for my $i (keys %{$configs}) {
		next if ref($configs->{$i});
		

		my ($temp, $tempfor);
		eval { ($temp, $tempfor) = MpmasQLFunctionLanguage->translate_and_interprete($configs->{$i},{}); };
		print STDERR "Error when interpreting configuration variable $i:\n $@" and exit(1) if $@;

		if ($#{$temp} > 0 or defined $tempfor->[0] ) {
			my @array;
			for my $i (0..$#{$temp}) {
				$array[$i] = $temp->[$i] if (defined $temp->[$i]) ;
			}
			$configs->{$i} = \@array;
		}
		else {
			$configs->{$i} = $temp->[0];
			
		}
		print "configuration entry $i is now: $configs->{$i}"."\n" if $verbose; 
	}	
	#Set Globals and Configs as Global Variables for the next interpretation steps
	my $globandconf = $globals;
	for my $l (keys %{$configs}) {
		$globandconf->{$l} = $configs->{$l} unless (exists $globandconf->{$l});
		
	}
	MpmasQLFunctionLanguage->set_globals( $singlemode, $dbh, $globandconf,"",0);
	#####Declare and initialize all prefetched tables
	my $tableregistry= SimpleObjectRegistry->create();
	for my $l (keys %{$tables}){
	    print "Table $l : ".$tables->{$l}->content ."...\n" if $verbose;
	    $tableregistry->add_objects($tables->{$l}->transform($dbh));
	    print "...stored as $l under ".$tableregistry->get_object_by_id($l)."\n" if $verbose; 
	}
	
	####Expand all setup lists and initialize variables and list from the variables file
	MpmasQLFunctionLanguage->set_globals( $singlemode, $dbh, $globandconf,$tableregistry,$verbose);
	for my $i(0..1) {
		my $cur;
		$cur = $setup if ($i == 0);
		$cur = $instances if ($i == 1);
		$cur = transform_vars($cur, {}, $verbose);

		if ($i == 0) {
			###### Merge the Globals and Setup Variable List:
			for my $l (keys %{$globals}) {
				$setup->{$l} = $globals->{$l} unless (exists $setup->{$l});
			}
  			#make this the new variables container for the interpretation function, so the variables are available when converting instances			
			MpmasQLFunctionLanguage->set_globals( 0, $dbh, $setup,$tableregistry,$verbose);

		}
		if ($i == 1) {
			# transform strings into one element array for 1-instance lists
			for my $l (keys %{$instances}) {
				my $s = $instances->{$l};
				$instances->{$l} =  [$s] unless (ref $instances->{$l})	 ; 
			}
			#Merge instance and variable list
			for my $l (keys %{$instances}) {
				$setup->{$l} = $instances->{$l} unless (exists $setup->{$l});
			}
	
		}
	}
	MpmasQLFunctionLanguage->set_globals( $singlemode, $dbh, $setup,$tableregistry,$verbose);
	if (defined $rhsdef) {
		for my $r ($rhsdef->query([],0)) {
			for my $v ($rhsdef->query([$r],0)) {
				my $val = $rhsdef->query([$r, $v],0);
				if ($val =~ /#/) {
					my $temp;
					eval { 	$temp = MpmasQLFunctionLanguage->translate_and_interprete($val,{}); };
					print STDERR "Error when trying to interprete RHS value for $v of agent $r:\n $!" and exit(1) if $@;
					my $newval = $temp->[0];
					$rhsdef->set_column_value([$r, $v],3, $newval);
				}
			}		
		}
	}	
	
	return ($mname, $setup, $instances, $configs, $tableregistry, $rhsdef)
}
sub transform_vars {
	my ($cur, $locvarhash, $verbose) = @_; 
	for my $l (keys %{$cur}){
		my $s = $cur->{$l};
		if ($s =~ /^\s*#verbatim\{/){
				$s =~ s/^\s*#verbatim\{//;
				$s =~ s/}\s*$//;
				$cur->{$l} = $s;
			
		}
		elsif ($s =~ /[-#+\/*~[\]=<>!\\^]+/) {
			
			my ($temp, $tempfor);
			eval { ($temp, $tempfor) = MpmasQLFunctionLanguage->translate_and_interprete($s,$locvarhash); };
			print STDERR "Error when interpreting variable or instance list $l:\n $@" and exit(1) if $@;

			if ($#{$temp} > 0 or defined $tempfor->[0] ) {
				my @array;
				for my $i (0..$#{$temp}) {
					$array[$i] = $temp->[$i] if (defined $temp->[$i]) ;
				}
				$cur->{$l} = \@array;
			}
			else {
				$cur->{$l} = $temp->[0];
				
			}
		}
		elsif ($s =~ /,/ ){	
			my @content = split /\s*,\s*/  , $s; 	
			$cur->{$l} = [@content];
			print "Expanding $l  ->\n" if $verbose;
			for my $i (@content) {	
				print "\t$i\n" if $verbose;
			}
			
		}
	}
	return $cur;
}

1;
