package MpmasQLElementDefinition;
use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use SimpleObject;
use MpmasQLFunctionLanguage ;
use MpmasQLFieldDefinition;
use MpmasMILPConstraint;
use MpmasMILPActivity;
use MpmasNetworkObject;
use MpmasPerennialObject;
use MpmasLivestockObject;
use MpmasQLModelElementAbstract;

use MpmasMILPSOS;

our @ISA = qw(SimpleObject);

#class variables
my @special_fields = qw(matel vfc wasby ifnet grouping dyninfo grouping_verbosity thismperiod mperiods);
my $livestock_deadlink_already_created = 0;

#sub routines
sub reset {
	$livestock_deadlink_already_created = 0;
}

sub create {
	my ($class, $id,$type) = @_;
	my $self = {};
	$self->{id} = $id;
	$self->{type} = $type;
	$self->{fields} = SimpleObjectRegistry->create;
	bless $self, $class;
	return $self;

}
sub id {
	return $_[0]->{id};
}
sub set_id {
	$_[0]->{id} = $_[1];
}
sub vfc {
	return $_[0]->{vfc};
}
sub set_vfc {
	$_[0]->{vfc} = $_[1];
}

sub type {
	return $_[0]->{type};
}
sub set_type {
	$_[0]->{type} = $_[1];
}
sub by_construct {
      return $_[0]->{by_construct};
}
sub set_by_construct {
      my ($self, $by) = @_;
      if ($by =~/[-&#+\/*~[\]=<>!\\^]+/ )  {
	  ($self->{by_construct}, $self->{by_verbose}) = MpmasQLFunctionLanguage->translate_to_perl($by);
	  $self->{by_is_complex} = 1;
      }
      else {
	  $self->{by_is_complex} = 0;
	  $self->{by_construct} = $by;
      }
}
sub delete_by_construct {
      delete $_[0]->{by_construct};
}
sub thisby {
	return $_[0]->{wasby};
}
sub set_thisby {
	$_[0]->{wasby} = $_[1];
}
sub thismperiod {
	return $_[0]->{thismperiod};
}
sub set_thismperiod {
	$_[0]->{thismperiod} = $_[1];
}
sub ifnet {
	return $_[0]->{ifnet};
}
sub set_ifnet {
	$_[0]->{ifnet} = $_[1];
}
sub add_specvars {
	my ($self,$hashref) = @_;
	for my $i (keys %$hashref) {
	    $self->{specvars}->{$i} = $hashref->{$i};
	}
}
sub specvar {
      return $_[0]->{specvars}->{$_[1]};
}
sub list_specvars {
      return keys %{$_[0]->{specvars}};
}
sub specvarhash {
      return $_[0]->{specvars};
}

sub dyninfo {
	return $_[0]->{dyninfo};
}
sub set_dyninfo {
	$_[0]->{dyninfo} = $_[1];
}
sub add_field {
      $_[0]->{fields}->add_objects($_[1]);
}
sub add_or_replace_field {
      $_[0]->{fields}->add_or_replace_objects($_[1]);
}
sub create_field {
      my $self = shift;
      my @attributes = @_;
      my $newfield = MpmasQLFieldDefinition->create(@_);
      $self->{fields}->add_objects($newfield);
}
sub create_simple_scalar_field {
      my ($self,$fieldname,$content) = @_;
      my $newfield = MpmasQLFieldDefinition->create(
		    id => $fieldname,
		    fieldname => $fieldname,
		    content => $content,
		    type => "scalar" 
      );
      $self->{fields}->add_objects($newfield);
}
sub fields {
	return $_[0]->{fields}->object_list;
}
sub field {
	return $_[0]->{fields}->get_object_by_id($_[1]);
}
sub delete_field {
	my ($self,$which) = @_;
	my $f= $self->field($which);
	$self->{fields}->delete_objects($f);	
}
sub delete_field_if_exists {
	my ($self,$which) = @_;
	my $f= $self->field($which);
	$self->{fields}->delete_objects($f) if (defined $f);	
}
sub set_field_content {
	my ($self,$which, $content) = @_;
	my $f = $self->{fields}->get_object_by_id($which);
	$f->set_content($content);
}
sub field_content {
	my ($self,$which) = @_;
	my $f = $self->{fields}->get_object_by_id($which);
	SimpleObject::croak ("Field $which does not exist in Element: ".$self->id."\n") unless (defined $f);
	return $f->content;
}
sub field_verbosity {
	my ($self,$which,$content_verbosity ) = @_;
	my $f = $self->{fields}->get_object_by_id($which);
	SimpleObject::croak ("Field $which does not exist in Element: ".$self->id."\n") unless (defined $f);
	return ($f->content_verbosity) if ($content_verbosity) ;
	return ($f->fieldname_verbosity) ;
}
sub subelements {
    return keys %{$_[0]->{subelements}};
}
sub delete_subelement {
	delete $_[0]->{subelements}->{$_[1]};
}
sub add_subelement_field {
      my ($self, $subelement, $object) = @_;
      $self->{subelements}->{$subelement} = SimpleObjectRegistry->create unless (exists $self->{subelements}->{$subelement});
      $self->{subelements}->{$subelement}->add_objects($object);
}
sub add_or_replace_subelement_field {
      my ($self, $subelement, $object) = @_;
      $self->{subelements}->{$subelement} = SimpleObjectRegistry->create unless (exists $self->{subelements}->{$subelement});
      $self->{subelements}->{$subelement}->add_or_replace_objects($object);
}
sub subelement_fields {
	my ($self,$subelement) = @_;
	return () unless (exists $self->{subelements}->{$subelement});
	return $self->{subelements}->{$subelement}->object_list;
}
sub subelement_field {
	my ($self, $subelement, $which) = @_;
	return undef unless (exists $self->{subelements}->{$subelement});
	return $self->{subelements}->{$subelement}->get_object_by_id($which);
}
sub delete_subelement_field {
	my ($self, $subelement, $which) = @_;
#	delete $self->{fields}->{$which};
	return $self->{subelements}->{$subelement}->delete_object($which);	
}
sub has_subelement {
	my ($self, $subelement) = @_;
	return exists ($self->{subelements}->{$subelement})
}
sub groupings {
	return keys %{$_[0]->{grouping}};
}
sub classify {
	my ($self,$classification,$group, $group_verbose) = @_;
	$self->{grouping}->{$classification} = $group;
	$self->{grouping_verbosity}->{$classification} = $group_verbose;
}
sub group {
	return $_[0]->{grouping}->{$_[1]};
}
sub add_prepared_field {
      $_[0]->{prepared}->{$_[1]} = $_[2];
}
sub prepared_field {
      return $_[0]->{prepared}->{$_[1]};
}
sub prepared_fields {
      return keys %{$_[0]->{prepared}};
}

sub transform {
	##transform the Class Elements into the right MpmasModelElement (Constraint, Activity or NetworkObject)
	my ($self,$curInstanceId, $setup, $milplist, $verbose, $instance_attributes, $numMperiods) = @_;
	$verbose = 0 unless (defined $verbose);

	my $mn = $curInstanceId."_".$self->id; # Code of resulting input file entry (Matrix row, column, network file entry
	
	print "Preparing:". $self->type() .":$mn \n" if $verbose;
	
	###Declarations
	my @new_untransformed_elements; #collection of new MPMASQL elements that will have to be transformed later on also


	###Special pre-treatments for specific element types
	## 1. By-element expansion
	if ($self->type =~ /by$/ ){ 
		print "\nExpanding by-construct of Element: ".$self->id." for instance $curInstanceId\n" if $verbose;
		my $newelementmatel = $self->type;
		$newelementmatel =~ s/by$//;
		my ($bylist, $byfor);
		my $by = $self->by_construct;
		if ($self->{by_is_complex}){
			my $locvarhash = {(
					  instance => $curInstanceId,
					  classMember =>  $curInstanceId
					 ), %$instance_attributes};
			if ( substr($milplist->[0],  1) == 3) {
				$locvarhash->{PO} = substr($milplist->[0], 1);
			}
			my ($intres, $intresfor) ;
			eval { ($intres, $intresfor) = MpmasQLFunctionLanguage->interprete ($by, $locvarhash, $self->{by_verbose}); };
			print STDERR "Error when interpreting by-string of instance ".$curInstanceId.", element ".$self->id."\n$@\n" and exit(1) if $@;
			unless (@$intres) {
			      print "By clause interpretation returned empty list!\n" if $verbose;
			      return undef, undef, \@new_untransformed_elements;;
			}
			my $ret = scalar @$intres;
			print "By clause interpretation returned $ret  entries!\n" if $verbose;
			$bylist = $intres;
			$byfor = $intresfor;
			
		} 
		elsif ( $by =~/^\s*(\w+)\s*/ ){
			
			print STDERR "Error: By-list $1 does not exist\n" and exit(1) unless (exists $setup->{$1});
			$bylist = $setup->{$1};
			$byfor = [@{$setup->{$1}}];
		} 
		else {
			print STDERR "Error: Instance: $curInstanceId, Element: ".$self->id."(".$self->type."): By-argument '$by' not interpretable!\n";
			exit(1);
		}
		print "\tExpanding over $by \n" if $verbose;
		for my $byi (0..$#{$bylist}){
			#make a new element from each entry of bylist and copy fields from self, and push on unprepared elements stack
			my $newelementid = $self->id."_".$bylist->[$byi];
			my $newelement = $self->create_copy; 
			$newelement->set_id($newelementid);
			$newelement->set_type($newelementmatel);
			$newelement->set_thisby($byfor->[$byi]);
			$newelement->delete_by_construct; 
			push @new_untransformed_elements, $newelement;
			print "\tCreated new ".$newelement->type." ".$newelement->id. ".\n" if $verbose;
		}
		return undef, undef, \@new_untransformed_elements;
	}
	## 2. Perennial elements
	# => create an investment activity, a production activity, an endowment constraint, a network entry and a perennials file entry for each perennial element 
	elsif ($self->type eq "perennial") {	    
		print "\tCompound perennial element: ". $mn." found\n" if $verbose;

		my %auxhash = 	(			
			produce => {(
				type	=> "activity",
				nameprefix => "\"Perennial Growing \"",
				subelements_to_fields=> [("production")], 
				subelements_to_copy=>[("cropwat", "edicmatlab","external_crop_growth", "yield_expectation")],
				sharedfields => [("unit")],
				newscalarfields => {(type => "'MASperennial'",
						    $mn."_endow" => 1
							)},
				specvars => {(  subelement => "production")} 
				)},	
			invest => {(
				type	=> "activity",
				nameprefix => "\"Perennial Investment \"",
				subelements_to_fields => [("investment")], 
				subelements_to_copy=>[("cropwat","external_crop_growth", "yield_expectation")],
				sharedfields => [("unit")],
				newscalarfields => {(type => "'MASperennial'" )},
				specvars => {(  subelement => "investment")} 
				)},
			asset => {(
				subelements_to_fields => [("asset")], 
				type	=> "asset",
				nameprefix => "",
				sharedfields => [("lifetime", "acqcost", "terrain")],
				newscalarfields => {(act => "'".$mn."_produce"."'", 
									 con => "'".$mn."_endow"."'",
									is_perennial => 1,
									type	=> 0)},
				specvars => {(  subelement => "asset")} 
			)},
			endow => {(
				subelements_to_fields =>[("endowment")],
				type	=> "constraint",
				nameprefix => "\"Perennial Endowment \"",
				sharedfields => [("unit")],
				newscalarfields => {(type => "'MASperennial'" )},
				specvars => {(  subelement => "endowment")} 
			)},
			perennialfileentry => {(
				type	=> "perennialfileentry",
				nameprefix => "",
				subelements_to_fields => [("dynamic")],
				subelements_to_copy=>[("production", "cropwat")],
				sharedfields => [("lifetime","acqcost", "terrain", "managementswitch","unit")],
				dyninfo => "lifetime",
				newscalarfields => {(obj =>"'". $mn."_asset"."'")},
				specvars => {(  subelement => "pf")} 
			)}					
		);
		
		my @new_elements = $self->expand_compound_element($curInstanceId, \%auxhash,{}, $verbose);
		push @new_untransformed_elements, @new_elements;
		return undef, undef, \@new_untransformed_elements;
	}
	## 3. Perennial file entries
	elsif ($self->type eq "perennialfileentry"){ 
		### Create switching activities for each perennialfileentry element, and transform dyninfo
		#read management switch entry in order to know which switching activities to create
		my $cf = [];
		if (defined $self->field("managementswitch")) {
			my $x = $self->field_content("managementswitch");			
			
			my $locvarhash = {(
				instance =>  $curInstanceId,
				classMember =>  $curInstanceId,
				by => $self->thisby ), %$instance_attributes};
			eval{
				local $SIG{__WARN__}=sub{};
				$cf = MpmasQLFunctionLanguage->interprete ($x, $locvarhash, $self->field_verbosity("managementswitch",1)); 
			};
			print STDERR "Error $@\n Instance: $curInstanceId, Perennial Switching Array\n"  and exit(1) if $@;
			$self->delete_field("managementswitch"); #transformed field will later be added
		}
		my %switchlist;
		### create the appropriate number of switching activities
		for my $switchfrom (@$cf) {
			next unless (defined $switchfrom);
			my $y = $self->id;
			$y =~ s/_perennialfileentry$//;
			my $newelementname = $y."_switch_from_".$switchfrom;
			my $newelement = MpmasQLElementDefinition->create($newelementname, "activity");
			# set standard attributes
			$newelement->set_thisby($self->thisby);
			$newelement->create_simple_scalar_field("type", "'MASswitch'");
			my $unitfield = $self->field("unit");
			$newelement->add_field($unitfield->create_copy) if (defined $unitfield);
			for my $g ($self->groupings) {
				$newelement->classify($g, $self->group($g) );
			}
			$newelement->create_simple_scalar_field("name","\"Perennial Switched Growing: ".$switchfrom." to \".&this('instance').\"_".$y."\"");
			$newelement->create_simple_scalar_field($switchfrom."_endow", 1);
			  # copy all entries from the production subelement of the perennialfileentry element
			for my $i ($self->subelement_fields("production")) {
			      $newelement->add_field($i->create_copy); 
			}
			for my $k ( $self->subelement_fields("cropwat")){
			      $newelement->add_subelement_field("cropwat",$k->create_copy) if (defined $k);
			}
			for my $k ( $self->subelement_fields("external_crop_growth")){
			      $newelement->add_subelement_field("external_crop_growth",$k->create_copy) if (defined $k);
			}
			for my $k ( $self->subelement_fields("yield_expectation")){
			      $newelement->add_subelement_field("yield_expectation",$k->create_copy) if (defined $k);
			}
			#place new switching activity elements at the end of the element stack
			push @new_untransformed_elements, $newelement;
			# include into the updated switch list for the perennialfileentry
			$switchlist{$switchfrom."_perennialfileentry"} = $curInstanceId."_".$newelementname;
		}
		$self->delete_subelement("production");
		$self->delete_subelement("cropwat");
	
		$self->add_prepared_field("managementswitch",\%switchlist);
	      #-->go on after elsif(type) block
	}
	## 4. Livestock elements
	# => create an investment activity, a production (maintain) activity, an endowment constraint for each age,a sell at end of period entry for each age, a sell at start of period for each age, a network entry and a livestock file entry for each livestock element 
	elsif ($self->type eq "livestock" ) {	    
		print "\tCompound livestock element: ". $mn." found\n" if $verbose;
		
		unless ($setup->{"LIVESTOCK_MODEL"} ) {
			print STDERR "Error: You are trying to use a #livestock element without activating the LIVESTOCK_MODEL!\n";
			exit(1);
		}
		
		unless ($livestock_deadlink_already_created) {
			my $newelement = MpmasQLElementDefinition->create("livestock_deadlink", "constraint");
			# set standard attributes
			$newelement->create_simple_scalar_field("type", "'MASforget'");
			$newelement->create_simple_scalar_field("name","\"Empty auxiliary constraint. Unused, but required livestock resources are linked here.\"");
			
			push @new_untransformed_elements, $newelement;
			$livestock_deadlink_already_created = 1;
		};


		my @sexinfo = ("f", "m", "u");
		my @sexes = (0,1);
		if (defined $self->field("unisex")) {
		      my $ltu;
		      my $ux = $self->field_content("unisex");			
		      my $locvarhash = {(
				instance =>  $curInstanceId,
				by => $self->thisby
		      )};
		      eval{
				local $SIG{__WARN__}=sub{};
				$ltu = MpmasQLFunctionLanguage->convert($ux, $locvarhash, 1, $self->field_verbosity("unisex",1)); 
		      };

		      if ($ltu) {
				@sexes = (2);
		      }
		}
		for my $sex (@sexes) {
			my $lt;
			my $x = $self->field_content("lifetime");			
			
			my $locvarhash = {(
				instance =>  $curInstanceId,
				classMember =>  $curInstanceId,
				by => $self->thisby,
				sex => $sexinfo[$sex] ), %$instance_attributes};
			eval{
				local $SIG{__WARN__}=sub{};
				$lt = MpmasQLFunctionLanguage->convert($x, $locvarhash, 1, $self->field_verbosity("lifetime",1)); 
			};
			print STDERR "Error $@\n Instance: $curInstanceId, lifetime\n" and exit(1) if $@;
			print "Error: Invalid lifetime: $lt\n Instance: $curInstanceId\n" and exit(1) unless ((defined $lt) and ($lt > 0));
			my %auxhash = (
				"$sexinfo[$sex]_maintain" => {(
					type	=> "activity",
					nameprefix => "\"Maintain $sexinfo[$sex] \"",
					subelements_to_fields=> [("maintain")], 
					subelements_to_copy=>[()],
					sharedfields => [("unit","grouping")],
					newscalarfields => {(type => "'MASlivestock'",
							      integ => 1,
							      $mn."_$sexinfo[$sex]_endow" => 1
							    )},
					specvars => {(  subelement => "maintain")} 
					)},	
				"$sexinfo[$sex]_invest" => {(
					type	=> "activity",
					nameprefix => "\"Livestock investment $sexinfo[$sex] \"",
					subelements_to_fields => [("investment")], 
					subelements_to_copy=>[()],
					sharedfields => [("unit","grouping")],
					newscalarfields => {(type => "'MASlivestock'",
							      integ => 1
							     )},
					specvars => {(  subelement => "investment")}  
					)},
				"$sexinfo[$sex]_asset" => {(
					  subelements_to_fields => [("asset")], 
					  type	=> "asset",
					  nameprefix => "\"$sexinfo[$sex] \"",
					  sharedfields => [("lifetime", "acqcost")],
					  newscalarfields => {(act => "'".$mn."_$sexinfo[$sex]_maintain"."'", 
							      con => "'".$mn."_$sexinfo[$sex]_endow"."'",
							      div => 0,
								  is_livestock => 1
							      )},
					  specvars => {(  subelement => "asset")} 
				  )},
				"$sexinfo[$sex]_endow" => {(
					  subelements_to_fields =>[("total_endowment")],
					  type	=> "constraint",
					  nameprefix => "\"Livestock total endowment $sexinfo[$sex] \"",
					  sharedfields => [("unit")],
					  newscalarfields => {(type => "'MASlivestock'",
								eqtype	=>	1
							      )},
					  specvars => {(  subelement => "total_endowment")} 
				  )},
# Not needed anymore
#				"$sexinfo[$sex]_lvdisinvest" => {(
#					  subelements_to_fields =>[()],
#					  type	=> "constraint",
#					  nameprefix => "\"Consume/Sell All $sexinfo[$sex] \"",
#					  sharedfields => [("unit")],
#					  newscalarfields => {(type => "'MASdisinvest_lv'")},
#					  specvars => {(  subelement => "disinvest")} 
#				  )},
				  "$sexinfo[$sex]_livestockfileentry" => {(
					  type	=> "livestockfileentry",
					  nameprefix => "",
					  subelements_to_fields => [("dynamic")],
					  subelements_to_copy=>[("dyn_offspring", "dyn_internal", "dyn_sales", "dyn_liveweight", "dyn_land", "dyn_liquidity", "dyn_labor", "dyn_max_demand")],
					  sharedfields => [("lifetime","salesactivity_meat", "acqcost", "purchase_age")],
					  dyninfo => "sexspecificlifetime",
					  newscalarfields => {(obj => "'".$mn."_$sexinfo[$sex]_asset"."'",
							       act =>  "'".$mn."_$sexinfo[$sex]_maintain"."'",
							       s0act => "'".$mn."_$sexinfo[$sex]_sell0_age000"."'",
							       s0con => "'".$mn."_$sexinfo[$sex]_endow_age000"."'",
							       sexspecificlifetime => $lt
					  )},
					  specvars => {(  subelement => "lv")} 
				  )}
			);

		
			#die "Error! Instance: $curInstanceId\n lifetime cannot be a list!\n" if (defined $cf[1][0]);
			
			for my $age (0..$lt -1) {
  
				#Endowment Constraint for each age
				my $endageid = "$sexinfo[$sex]_endow_age".sprintf("%03d", $age) ;
				$auxhash{$endageid} = {(
					 subelements_to_fields =>[("endowment")],
					  subelements_to_copy=>[()],
					  type	=> "constraint",
					  nameprefix => "\"Livestock Age $age Endowment $sexinfo[$sex] \"",
					  sharedfields => [("unit")],
					  newscalarfields => {(type => "'MASlivestock'",
							       eqtype => ($age == $lt - 1) ? 3 : 1
							      )},
					specvars => {( age => $age,
						       subelement => "endowment")}  
				)};
			      my $sstartid ="$sexinfo[$sex]_sell0_age".sprintf("%03d", $age);
				$auxhash{$sstartid } = {(
					type	=> "activity",
					nameprefix => "\"Sell Livestock at Start of Period, Age $age, $sexinfo[$sex],\" ",
					subelements_to_fields=> [("salesstart")], 
					dynfields_neg_to_fields=> [("dyn_liveweight")], 
					dynfields_pos_to_fields=> [( )],
					subelements_to_copy=>[()],
					sharedfields => [("unit")],
					newscalarfields => {( type => "'MASlivestock'",
							      #integ	=> 1,
							      #consfix 	=> 1,
							      $mn."_$sexinfo[$sex]_endow_age".sprintf("%03d", $age) => 1,
							      $mn."_$sexinfo[$sex]_endow" => 1
							     )},
					specvars => {( tdyn => $age,
						       age => $age,
						       subelement => "salesstart")}

				)};
				my $sendid = "$sexinfo[$sex]_sell1_age".sprintf("%03d", $age) ;
				$auxhash{ $sendid } = {(
					type	=> "activity",
					nameprefix => "\"Sell Livestock at End of Period, Age $age, $sexinfo[$sex], \"",
					subelements_to_fields=> [("salesend")], 
					dynfields_neg_to_fields=> [("dyn_offspring", "dyn_internal", "dyn_sales", "dyn_liveweight")], 
					dynfields_pos_to_fields=> [( "dyn_land", "dyn_liquidity", "dyn_labor", "dyn_max_demand")],
					subelements_to_copy=>[()],
					sharedfields => [("unit")],
					newscalarfields => {( type => "'MASlivestock'",
							      integ	=> 1,
							      $mn."_$sexinfo[$sex]_endow_age".sprintf("%03d", $age) => 1,
							      $mn."_$sexinfo[$sex]_endow" => 1
							    )},
					specvars => {( tdyn => $age + 1,
						       age => $age,
						       subelement => "salesend"  )}
				)};
			}
			my @new_elements = $self->expand_compound_element($curInstanceId, \%auxhash,{sex => $sexinfo[$sex]}, $verbose);
			push @new_untransformed_elements, @new_elements;
		}
		return undef, undef, \@new_untransformed_elements;
	}
	## 5. Livestock File Entries
	elsif ($self->type eq "livestockfileentry"){ 
	
	}
	## 6. Time-preference penalty
	elsif ($self->type eq "timepreference_penalty") {	    
		print "\tCompound perennial element: ". $mn." found\n" if $verbose;
		my %auxhash = 	(			
			tppenalty_act => {(
				type	=> "activity",
				nameprefix => "\"Time preference penalty activity\"",
				subelements_to_fields=> [("activity")], 
				
				sharedfields => [("name")],
				newscalarfields => {(type => "'MAStppenalty'",
						    $mn."_tppenalty_con" => -1
							)}
				)},	
			tppenalty_con => {(
				type	=> "constraint",
				nameprefix => "\"Time preference penalty constraint\"",
				subelements_to_fields=> [("constraint")], 
				
				sharedfields => [("name")],
				newscalarfields => {(type => "'MAStppenalty'"
						    
							)}
				)},
			tppenalty_entry => {(
				type	=> "timepreference_penalty_entry",
				nameprefix => "",

				sharedfields => [("name","delay", "penalized_activity" )],
				newscalarfields => {(act => "'".$mn."_tppenalty_act"."'",
						     con => "'".$mn."_tppenalty_con"."'",     
									)}
			)}					
		);
		
		my @new_elements = $self->expand_compound_element($curInstanceId, \%auxhash,{}, $verbose);
		push @new_untransformed_elements, @new_elements;
		return undef, undef, \@new_untransformed_elements;
	}
	## 7.
	elsif ($self->type eq "timepreference_penalty_entry"){ 
	
	}
	### 8. Expansion of multiperiod activities and constraints
	elsif ($numMperiods > 0 &&  ($self->type eq "constraint" or $self->type eq "activity") && (not defined $self->thismperiod()) ) {
		
		my @periods = (0..$numMperiods);
		
		if(defined ($self->field("mperiods"))) { 
			my $locvarhash = {(
					  instance => $curInstanceId,
					 ), %$instance_attributes};
			if ( substr($milplist->[0],  1) == 3) {
				$locvarhash->{PO} = substr($milplist->[0], 1);
			}
			my ($intres, $intresfor) ;
			my $mperiodfield = $self->field_content("mperiods");
			eval { ($intres, $intresfor) = MpmasQLFunctionLanguage->interprete ($mperiodfield, $locvarhash); };
			print STDERR "Error when interpreting by-string of instance ".$curInstanceId.", element ".$self->id."\n$@\n" and exit(1) if $@;
			@periods = @$intres;
		}
		for my $mi (@periods){
			my $newelementid = $self->id .":".$mi;
			my $newelement = $self->create_copy; 
			$newelement->set_id($newelementid);
			$newelement->set_thismperiod($mi);
			$newelement->delete_field_if_exists("mperiods");
			push @new_untransformed_elements, $newelement;
		}
		return undef, undef, \@new_untransformed_elements;
	}
	### transform dyninfo field for use below
	if ($self->dyninfo) {
		my $x = $self->dyninfo;
		my $locvarhash = {(
			instance =>  $curInstanceId,
			element => $self->id,
			by => $self->thisby ), %$instance_attributes};
		my $cf;
		eval{
			local $SIG{__WARN__}=sub{};
			#CT! ?? verbosity
			$cf = MpmasQLFunctionLanguage->interprete ($x, $locvarhash); 
		};
		print STDERR "Error $@\n Instance: $curInstanceId, Element: ".$self->id."(".$self->type.") could not interprete number of dynamic stages!\n" and exit(1) if $@;
		$self->set_dyninfo($cf->[0]);
		
	}

	### Declare associative array for fields of model element to be created
	my $newfieldhash ={};
	#Iterate over all fields of the QLClass element and transform them
	my @fieldstack = $self->fields;
	FIELDSTACK: while (@fieldstack) {
	      my $curField = shift @fieldstack;
	      print scalar @fieldstack."\n" if $verbose;
	      print "Field: ".$curField->id."\n" if $verbose;
	      my ($content_by_milp, $new_untransformed_fields) = $curField->transform($curInstanceId, $self->id, $self->thisby, $self->dyninfo, $self->specvarhash, $milplist, $verbose, $instance_attributes, $self->thismperiod());
	      push @fieldstack, @$new_untransformed_fields;
	      if ( scalar @$milplist > 1) {
		      for my $thismilp (@$milplist[1..$#{$milplist}] ) {
		      	$newfieldhash->{othermilp}->{$thismilp}->{$curField->id} = $content_by_milp->{$thismilp} if (defined $content_by_milp->{$thismilp});
		      }
	      }
          $newfieldhash->{$curField->id} = $content_by_milp->{$milplist->[0]} if (defined $content_by_milp->{$milplist->[0]});
	}
	#Iterate over all fields of subelements, which have not been used otherwise yet
	my $newsubelementhash = {};
	my $newothermilpsubelementhash = {};
	for my $s ($self->subelements) {
	    my @subfieldstack = $self->subelement_fields($s);
	    SUBFIELDSTACK: while (@subfieldstack) {
		  my $curField = shift @subfieldstack;
		  print scalar @subfieldstack."\n" if $verbose;
		  print "Field: ".$curField->id."\n" if $verbose;
		  my ($content_by_milp, $new_untransformed_fields) = $curField->transform($curInstanceId, $self->id, $self->thisby, $self->dyninfo, $self->specvarhash, $milplist, $verbose, $instance_attributes, $self->thismperiod());
		  push @subfieldstack, @$new_untransformed_fields;
		  $newsubelementhash->{$s}->{$curField->id} = $content_by_milp->{$milplist->[0] } if (defined $content_by_milp->{$milplist->[0] }); 
		  if ( scalar @$milplist > 1) {
		      for my $thismilp (@$milplist[1..$#{$milplist}] ) {
		      	$newothermilpsubelementhash->{$thismilp}->{$s}->{$curField->id} = $content_by_milp->{$thismilp} if (defined $content_by_milp->{$thismilp});
		      }
		  }
	    }
	}

	$newfieldhash->{thiscomp} = $curInstanceId;				
	MpmasQLModelElementAbstract->switch_on_defaults();	
	my $new;
	if ($self->type eq "constraint"	) {
		$new = MpmasMILPConstraint->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);		
		if ($numMperiods > 0) { 
			$new->set_attr("mperiod", $self->thismperiod());
			$new->set_attr("name", $new->attr("name"). " (MPERIOD ". $self->thismperiod().")");
		}
	}
	elsif ($self->type eq "activity") {
		$new = MpmasMILPActivity->create($mn, $newfieldhash, $newsubelementhash,$newothermilpsubelementhash, $verbose);	
		if ($numMperiods > 0) { 
			$new->set_attr("mperiod", $self->thismperiod());
			$new->set_attr("name", $new->attr("name"). " (MPERIOD ". $self->thismperiod().")");
			$new->multiperiodMIP_set_correct_period();
		}
	}
	elsif ($self->type eq "asset") {
		$new = MpmasNetworkObject->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);	
	}
	elsif ($self->type eq "perennialfileentry") {
		$new = MpmasPerennialObject->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);	
	}
	elsif ($self->type eq "livestockfileentry") {
		$new = MpmasLivestockObject->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);	
	}
	elsif ($self->type eq "timepreference_penalty_entry") {
		$new = MpmasTPPenaltyObject->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);	
	}
	elsif ($self->type eq "sos") { 
	    	$new = MpmasMILPSOS->create($mn, $newfieldhash, $newsubelementhash, $newothermilpsubelementhash, $verbose);	
	}
	$new->set_standard_milp($milplist->[0]);
	### Set the grouping attribute of the new model element
	for my 	$curGroup ($self->groupings)	{
		my $code = $self->group($curGroup);
		my $locvarhash = {(
			instance => $curInstanceId, 
			classMember =>  $curInstanceId,
			element => $self->id, 
			grouping => $curGroup, 
			by => $self->thisby,
			)};
		 if ( substr($milplist->[0],  1) == 3) {
			$locvarhash->{PO} = substr($milplist->[0], 1);
		 }
		 my %mergedlocvars;
		 if (defined $self->specvarhash) {
		      %mergedlocvars = ( %$locvarhash , %{$self->specvarhash}, %$instance_attributes);
		 }
		 else {
		     %mergedlocvars = (%$locvarhash, %$instance_attributes);
		 }
		my $group;
		eval {$group = MpmasQLFunctionLanguage->interprete ( $code, \%mergedlocvars, $self->{grouping_verbosity}->{$curGroup});};
		print STDERR "Error when interpreting group:\n $@\n Class: $curInstanceId, Element: ".$self->id.", Grouping: $curGroup\n" and exit(1) if $@;
		$new->set_group($curGroup, $group->[0]);
	}

	#add all fields that have already been prepared previously  (such as switching groups etc.)
	for my $i ($self->prepared_fields) {
	      $new->set_attr($i, $self->prepared_field($i));
	}
	return $new, $self->type, \@new_untransformed_elements;
}







sub expand_compound_element {
		my ($self,$curInstanceId,$auxhashref,$specvarref, $verbose) = @_;
	        $verbose = 0 unless (defined $verbose);

		my %auxhash = %{$auxhashref} if (ref $auxhashref eq "HASH");
		my @new_elements;
    
		for my $ext (keys %auxhash) {
			my $newelementid = $self->id."_".$ext;
			my $newelement = MpmasQLElementDefinition->create($newelementid, $auxhash{$ext}->{type});
			my $fullname = ( $auxhash{$ext}->{nameprefix} ne "" ) ? $auxhash{$ext}->{nameprefix}."." .$self->field_content("name") : $self->field_content("name");
			$newelement->create_simple_scalar_field("name", $fullname);
			$newelement->set_thisby($self->thisby) if (defined $self->thisby);

			#Important: First, so it can be overriden by subelements
			for my $g (keys %{$auxhash{$ext}->{newscalarfields}}) {
				$newelement->create_simple_scalar_field($g, $auxhash{$ext}->{newscalarfields}->{$g});
			}


			for my $e (@{$auxhash{$ext}->{sharedfields}}) {	
				my $oldfield = $self->field($e);
				$newelement->add_or_replace_field($oldfield->create_copy) if (defined $oldfield); 
			}


			for my $g (@{$auxhash{$ext}->{subelements_to_copy}}) {
				for my $oldfield ( $self->subelement_fields($g) ){
					$newelement->add_or_replace_subelement_field($g, $oldfield->create_copy) if (defined $oldfield);
				}
			}
			for my $g (@{$auxhash{$ext}->{dynfields_neg_to_fields}}) {
				for my $oldfield ( $self->subelement_fields($g) ){
				  if (defined $oldfield) {
					my $f = $oldfield->create_copy;
					my $tc = $f->content() ;
					$tc =~ s/&this\s*\(\s*'dyn'/&this('tdyn'/; #Replace dyn by tdyn

					$f->set_content("-1 * (". $tc .")");
					$newelement->add_or_replace_field($f) ;
				    }
				}
			}
			for my $g (@{$auxhash{$ext}->{dynfields_pos_to_fields}}) {
				for my $oldfield ( $self->subelement_fields($g) ){
				  if (defined $oldfield) {
					my $f = $oldfield->create_copy;
					my $tc = $f->content() ;
					$tc =~ s/&this\s*\(\s*'dyn'/&this('tdyn'/; #Replace dyn by tdyn

					$f->set_content( $tc );
					$newelement->add_or_replace_field($f) ;
				    }
				}
			}
			 
			for my $g (@{$auxhash{$ext}->{subelements_to_fields}}) {
				for my $oldfield ( $self->subelement_fields($g) ){
					$newelement->add_or_replace_field($oldfield->create_copy) if (defined $oldfield);
				}
			}
			my $dnf = $auxhash{$ext}->{dyninfo};
			if (defined $dnf) {
				$newelement->set_dyninfo($newelement->field_content($dnf));
			}		
			#specvars specific to some elements
			$newelement->add_specvars($auxhash{$ext}->{specvars}) if (exists $auxhash{$ext}->{specvars}) ;
			#specvars equal for all elements
			$newelement->add_specvars($specvarref);
			for my $g ($self->groupings) {
				$newelement->classify($g, $self->group($g) );
			}
			push @new_elements, $newelement;
			print "\tNew ".$newelement->type." ".$newelement->id." added to instance: ".$curInstanceId."\n" if $verbose;
		}
		return @new_elements;
}

1;
