package MpmasCropExt;

use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use List::Util qw[min max];
use CtUtilities qw(queryhash extqueryhash);
use MpmasInputfile;
use MpmasInputBlock;
our @ISA = qw(MpmasInputfile);

sub filename {
	return "CropExt";
}

sub required {
	my $self = shift;
	return ($self->config_entry("CROP_MODEL") == 3);
}
sub prepare_shared{
	my $self = shift;
	my $dbh = $self->get_dbh;

	my $NumCropActTotal = 0;
	my @soilcount;

	my @sortedxnactivities;
	my @sortedxnactivitiespersoil;
	my @xnactivityRequiredAssetTab;

	
	my @sorted_xn_activity_objects;
	
	
	if ($self->config_entry("MULTIPERIOD")) {
		
		print STDERR "\nError: Missing EXTERNAL_CG_INFO_MULTIPERIOD [MPMAS TABLES].\n"  and exit(1) unless (defined $self->config_entry("EXTERNAL_CG_INFO_MULTIPERIOD"));
        my $xnlinktab = $self->config_entry("EXTERNAL_CG_INFO_MULTIPERIOD")->transform($dbh,2);
        $self->set_config_entry("CONVERTED_EXTERNAL_CG_INFO_MULTIPERIOD", $xnlinktab);
        my $ur = 7;
        if($self->config_entry("DYNAMIC_SOIL_TRANSITIONS")) {
			
			unless($xnlinktab->exists_columnname("new_soil_map_id")) {
					my $urname = $xnlinktab->get_name_of_column($ur );
					print STDERR "Warning: Column name 'new_soil_map_id' not found in EXTERNAL_CG_INFO_MULTIPERIOD. Column ". $ur. " is named ". $urname.". Ignoring inconsistent". 
					" naming and proceeding.\nCheck consistency of EXTERNAL_CG_INFO_MULTIPERIOD with BIOVERSION\n" ;
			}
			else {
				my $urname = $xnlinktab->get_name_of_column($ur );
				my $cn = $xnlinktab->column_by_name("new_soil_map_id");
				print STDERR "Error: Column $ur of table EXTERNAL_CG_INFO_MULTIPERIOD is named $urname not new_soil_map_id "   .
					" as expected, but column $cn is!\nCheck consistency of EXTERNAL_CG_INFO_MULTIPERIOD with BIOVERSION \n" and exit(1) unless($cn == $ur);
			}
			$ur += 1;
		}
        if( $self->config_entry("DYNAMIC_SOIL_TRANSITIONS")
					|| $self->config_entry("BIOVERSION") == 5
					|| $self->config_entry("BIOVERSION") == 6
					||	$self->config_entry("BIOVERSION") == 7
					|| $self->config_entry("BIOVERSION") == 8
		) 
		{
			
			unless($xnlinktab->exists_columnname("use_for_unowned_land")) {
					my $urname = $xnlinktab->get_name_of_column($ur );
					print STDERR "Warning: Column name 'use_for_unowned_land' not found in EXTERNAL_CG_INFO_MULTIPERIOD. Column ". $ur. " is named ". $urname.". Ignoring inconsistent". 
					" naming and proceeding.\nCheck consistency of EXTERNAL_CG_INFO_MULTIPERIOD with BIOVERSION /DYNAMIC_SOIL_TRANSITIONS\n" ;
			}
			else {
				my $urname = $xnlinktab->get_name_of_column($ur );
				my $cn = $xnlinktab->column_by_name("use_for_unowned_land");
				print STDERR "Error: Column $ur of table EXTERNAL_CG_INFO_MULTIPERIOD is named $urname not use_for_unowned_land "   .
					" as expected, but column $cn is!\nCheck consistency of EXTERNAL_CG_INFO_MULTIPERIOD with BIOVERSION / DYNAMIC_SOIL_TRANSITIONS\n" and exit(1) unless($cn == $ur);
			}
			$ur += 1;
		}
        
        
        my $xnperennialtab;
        
        my $tab = $self->config_entry("EXTERNAL_CG_INFO_REQUIRED_ASSETS");
        if (defined $tab) {
			$xnperennialtab = $tab->transform($dbh,2);
		}

		for my $s ( sort {$a <=> $b} $xnlinktab->query([],0)) {
			last if ($s >= $self->config_entry("NSOIL") );
			for my $k ( sort {$a <=> $b} $xnlinktab->query([$s],0)) {
				
				$NumCropActTotal++ ;
				$soilcount[$s] += 1;
				
				
				my $link = $xnlinktab->query([$s, $k],3);
				my @newxnact;
				
				
				$newxnact[2] = $xnlinktab->query([$s, $k],4);#description
				$newxnact[3] = $link;#link
				$newxnact[4] = $k; #cropManagementID
				$newxnact[5] = $s; #soil type
				$newxnact[6] = $xnlinktab->query([$s, $k],5) || 0;#initial crop yield
				$newxnact[7] = $xnlinktab->query([$s, $k],6) || 0;#initial stover yield
				my $nextcol = 7;
				if($self->config_entry("DYNAMIC_SOIL_TRANSITIONS")) {
					$newxnact[8] = $xnlinktab->query([$s, $k],$nextcol) ;
					++$nextcol;
				}
				else {
					$newxnact[8] = "";
				}
				if($self->config_entry("DYNAMIC_SOIL_TRANSITIONS")
					|| $self->config_entry("BIOVERSION") == 5
					|| $self->config_entry("BIOVERSION") == 6
					||	$self->config_entry("BIOVERSION") == 7
					|| $self->config_entry("BIOVERSION") == 8
					) {
					$newxnact[9] = $xnlinktab->query([$s, $k],$nextcol) ;
					++$nextcol;
				}
				else {
					$newxnact[9] = "";
				}
				my $cc = 0;
				for my $c ($self->get_cfg_list_empty_ok("EXTRA_CROPACT_CHARACTERISTICS") ) 	{
					
					$newxnact[10 +$cc] = $xnlinktab->query_by_columnname([$s, $k],$c);
					$cc++;
				}
				push @sortedxnactivities, [ @newxnact  ];
			
				next unless (defined $xnperennialtab);
				
				my @requiredAssets = $xnperennialtab->query([$link],0);
			
				next unless (defined $requiredAssets[0]);
				foreach my $ra (@requiredAssets) {
					my $no = $self->networkobject($ra);
					
					unless (defined $no) {
							print STDERR "Error in EXTERNAL_CG_INFO_REQUIRED_ASSETS: No such asset: $ra for ". $link."\n";
							exit(1);
					}
					my @newentry;
					$newentry[3] = $link;#link
					$newentry[4] = $no->oid ; #required asset objId
					
					my $minAge = $xnperennialtab->query([$link, $ra],3);
					my $maxAge = $xnperennialtab->query([$link, $ra],4);
					
					unless (defined $minAge and defined $maxAge) {
							print STDERR "Error in EXTERNAL_CG_INFO_REQUIRED_ASSETS: Missing minAge/maxAge for objID $ra for ". $link."\n";
							exit(1);
					}
					$newentry[5] = $minAge ; #required asset minAge
					$newentry[6] = $maxAge ; #required asset maxAge
				
					push @xnactivityRequiredAssetTab, [@newentry];
				}
			}
		}
		
		my %ExtraCropActCharCodes;

		my @ExtraCropActChars = $self->get_cfg_list_empty_ok("EXTRA_CROPACT_CHARACTERISTICS");
	
		for my $i (0..$#ExtraCropActChars) {
			$ExtraCropActCharCodes{$ExtraCropActChars[$i]} = $i;
		}
		$self->set_cropact_attribute_ids(\%ExtraCropActCharCodes);

	
	}
	elsif ($self->config_entry("STATICNRU") == 1 and $self->config_entry("EXTERNALCGFORMAT0")) { 
		my %xnactivity_lookup;
		my @xnactivities;
		my @persoilxnactivities;
		my %lpcolhash;

		#For static NRUs, i.e. soil types do correspond 1-to-1 to NRUs
		for my $i (@{$self->sorted_activity_objects}) {

			my $curlink = $i->subelement_field("external_crop_growth", "link");
			
			if (defined $curlink and $curlink > -1) {
				$NumCropActTotal++ ;
				unless (defined $i->subelement_field("external_crop_growth", "soil") and $i->subelement_field("external_crop_growth", "soil") >= 0 and $i->subelement_field("external_crop_growth", "soil") < $self->config_entry("NSOIL") ) {
						print STDERR "Error: invalid #external_crop_growth>soil for activity ",$i->id,"\n";
						exit(1);
				}
				my $soil = $i->subelement_field("external_crop_growth", "soil");
				$soilcount[$soil] += 1;
				
				my @newxnact;
				$xnactivity_lookup{$curlink} = $i;
				$newxnact[2] = $i->attr("name");
				$newxnact[3] = $curlink;
				$newxnact[4] = $soil;
				unless (defined $i->subelement_field("external_crop_growth", "crop_type") and $i->subelement_field("external_crop_growth", "crop_type") >= 0 and $i->subelement_field("external_crop_growth", "crop_type") <= 4 ) {
						print STDERR "Error: invalid #external_crop_growth>crop_type for activity ",$i->id,"\n";
						exit(1);
				}
				$newxnact[5] = $i->subelement_field("external_crop_growth", "crop_type");
				
				unless (defined $i->subelement_field("external_crop_growth", "priority")) {
						print STDERR "Error: missing #external_crop_growth>priority for activity ",$i->id,"\n";
						exit(1);
				}
				$newxnact[6] = $i->subelement_field("external_crop_growth", "priority");

				my $pb = $i->subelement_field("external_crop_growth", "product1_sales_activity");
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $ao = $self->activity($pb);
					print STDERR "Error in external crop growth interface: Activity ". $i->id." is associated to sales activity $pb, but no activity $pb has been defined\n" and exit(1) unless (defined $ao);
					$newxnact[7] = $ao->lp_column;
				}
				else {
					$newxnact[7] =  -1;
				}
				$newxnact[8] = $i->subelement_field("external_crop_growth", "initial_yield_product1") || 0;
				$newxnact[9] = $i->subelement_field("external_crop_growth", "initial_yield_product2") || 0;
				$lpcolhash{$curlink} = $i->lp_column;
				push @xnactivities, [ @newxnact  ];

				my @persoil;
				$persoil[1] = $curlink;
				$persoil[2] =$soil;
				$persoil[3] = $i->lp_column;

				$pb = $i->subelement_field("external_crop_growth", "product1_balance");
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $co = $self->constraint($pb);
					print STDERR "Error in external crop growth interface: Activity ". $i->id." is associated to product balance $pb, but no constraint $pb has been defined\n" and exit(1) unless (defined $co);
					$persoil[4] = $co->lp_row;
				}
				else {
					$persoil[4] =   -1;
				}
				$pb = $i->subelement_field("external_crop_growth", "product2_balance");
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $co = $self->constraint($pb);
					print STDERR "Error in external crop growth interface: Activity ". $i->id." is associated to product balance $pb, but no constraint $pb has been defined\n" and exit(1) unless (defined $co);
					$persoil[5]  = $co->lp_row;
				}
				else {
					$persoil[5] =   -1;
				}

				push @persoilxnactivities, [ @persoil];
			}
		}
		@sortedxnactivities = sort { $a->[4]  <=> $b->[4] ||   $lpcolhash{$a->[3]} <=> $lpcolhash{$b->[3]}  } @xnactivities;
		@sortedxnactivitiespersoil = sort {   $a->[2]  <=> $b->[2] ||   $a->[3] <=> $b->[3]  } @persoilxnactivities;
		@sorted_xn_activity_objects = map {$xnactivity_lookup{$_->[3]} } @sortedxnactivities
	}
	elsif ($self->config_entry("STATICNRU") == 0 || $self->config_entry("STATICNRU") >= 2 || not $self->config_entry("EXTERNALCGFORMAT0")) {
	#For dynamic NRUs, i.e. soil types do not correspond 1-to-1 to NRUS
	#Identify slots in LP for NRU
	
		print STDERR "\nError: Missing EXTERNAL_CG_INFO [MPMAS TABLES].\nThis is required because EXTERNALCGFORMAT0 has been set to 0 or STATICNRU has been set to 0 or >=2\n"  and exit(1) unless (defined $self->config_entry("EXTERNAL_CG_INFO"));
        my $xnlinktab = $self->config_entry("EXTERNAL_CG_INFO")->transform($dbh,2);
	
		my %slot_lookup;
		for my $i (@{$self->sorted_activity_objects}) {

			my $slot = $i->subelement_field("external_crop_growth", "slot");
			my $nru = $i->subelement_field("external_crop_growth", "nru");
			if (defined $slot and $slot > -1 and defined $nru and $nru > -1) {
				
				print STDERR "Error: Activity ",$i->id()," has the #external_crop_growth> subelement, but is not marked as MAScgact, MASperennial or MASswitch\n" and exit(1) if ($i->attr("type") ne "MAScgact" and  $i->attr("type") ne "MASperennial" and $i->attr("type") ne "MASswitch");
				if ($nru == 0) {
					$slot_lookup{$slot} = 1;
				}
				else {
					print STDERR "Error: Unequal number of slots in NRUs or cropping activities not sorted correctly (by NRU and slot)\nSlot $slot does not appear before NRU $nru\n" and exit(1) if (not exists $slot_lookup{$slot});
					$slot_lookup{$slot}++;
				}
				my @persoil;
				$persoil[1] = $slot;
				$persoil[2] = $nru;
				$persoil[3] = $i->lp_column;

				my $pb = $i->subelement_field("external_crop_growth", "product1_balance");
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $co = $self->constraint($pb);
					print STDERR "Error in external crop growth interface: Activity ". $i->id." is associated to product balance $pb, but no constraint $pb has been defined\n" and exit(1) unless (defined $co);
					$persoil[4] = $co->lp_row;
				}
				else {
					$persoil[4] =   -1;
				}
				$pb = $i->subelement_field("external_crop_growth", "product2_balance");
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $co = $self->constraint($pb);
					print STDERR "Error in external crop growth interface: Activity ". $i->id." is associated to product balance $pb, but no constraint $pb has been defined\n" and exit(1) unless (defined $co);
					$persoil[5]  = $co->lp_row;
				}
				else {
					$persoil[5] =   -1;
				}
				push @sortedxnactivitiespersoil, [ @persoil];
			}	

		}
		for my $i (keys %slot_lookup) {
				print STDERR "Error: Unequal number of slots in NRUs, Slot $i appears ". $slot_lookup{$i}." times (NNRU = ".$self->config_entry("NNRU").")\n" and exit(1) unless ($slot_lookup{$i} == $self->config_entry("NNRU"));
				my $crop_act_for_slot = 0;
				for my $s ( sort {$a <=> $b} $xnlinktab->query([],0)) {
					last if ($s >= $self->config_entry("NSOIL") );
					if ( defined $xnlinktab->query([$s, $i],3) ){
						++$crop_act_for_slot;
					}
				}
				print STDERR "Error in EXTERNAL_CG_INFO:  There is an LP activity with crop activity slot $i in the matrix\n, but in EXTERNAL_CG_INFO there is no cropping activity assigned to that slot in any soil type\n" and exit(1) unless ($crop_act_for_slot > 0);
		}


		for my $s ( sort {$a <=> $b} $xnlinktab->query([],0)) {
			last if ($s >= $self->config_entry("NSOIL") );
			for my $k ( sort {$a <=> $b} $xnlinktab->query([$s],0)) {
				print STDERR "\nWarning: EXTERNAL_CG_INFO contains cropping activities for slot $k, but no slot $k has been defined for any NRU\n(check #external_crop_growth>slot and #external_crop_growth>nru)\n" and next unless ($slot_lookup{$k});
				$NumCropActTotal++ ;
				$soilcount[$s] += 1;
				
				my @newxnact;
				$newxnact[2] = $xnlinktab->query([$s, $k],4);#description
				$newxnact[3] = $xnlinktab->query([$s, $k],3);#link
				$newxnact[4] = $s;
				$newxnact[5] = $xnlinktab->query([$s, $k],5); #crop_type
				$newxnact[6] = $xnlinktab->query([$s, $k],6); #priority

				my $pb = $xnlinktab->query([$s, $k],7);
				if (defined $pb and not $pb eq "-1"  and not $pb eq "0") {
					my $ao = $self->activity($pb);
					print STDERR "Error in external crop growth interface: Activity ". $xnlinktab->query([$s, $k],3)." is associated to sales activity $pb, but no activity $pb has been defined\n" and exit(1) unless (defined $ao);
					$newxnact[7] = $ao->lp_column; #lp column
				}
				else {
					$newxnact[7] =  -1;
				}
				$newxnact[8] = $xnlinktab->query([$s, $k],8) || 0;
				$newxnact[9] = $xnlinktab->query([$s, $k],9) || 0;
				push @sortedxnactivities, [ @newxnact  ];
			}
		}
	}
	unless ($self->config_entry("MULTIPERIOD")) {
		print STDERR  "Error for external crop growth model: not the same number of activities for all soil types!!!\n" and exit(1) unless ( min (@soilcount) == max (@soilcount) );
		$self->{NumCropActPerSoil} = max (@soilcount);
	}
	$self->{NumCropActTotal} = $NumCropActTotal;
	#register shared variables
	$self->set_global("Sorted_Xn_Activities",\@sortedxnactivities);
	unless ($self->config_entry("MULTIPERIOD")) {
		$self->set_global("Sorted_Xn_Activities_Per_Soil",\@sortedxnactivitiespersoil);
	}
	#$self->set_global("Xn_Activity_Lookup",\%xnactivity_lookup);
	
	$self->set_global("Sorted_Xn_Activity_Objects", \@sorted_xn_activity_objects);
	$self->set_global("xnactivityRequiredAssetTab", \@xnactivityRequiredAssetTab);
}
sub prepare_special {
		my $self = shift;

		my $sortedxnactivities = $self->get_global("Sorted_Xn_Activities");

		my $xntop = MpmasInputBlock->new(2,10);
		$xntop->add_comment_row("External CROP COEFFICIENTS");
		$xntop->add_comment_row("This links the external crop growth model to the LP tableau");
		$xntop->add_empty_rows(3);

		$xntop->add_row("1","CROP ACTIVITIES");	
		$xntop->add_row("1.1","INFO ABOUT THE CROP ACTIVITIES");
		if ($self->config_entry("MULTIPERIOD")) {
			$xntop->add_row("", "Number of crop activities (total over all soil types)", $self->{NumCropActTotal});				
			$xntop->add_row("", "Number of cropping seasons per period", $self->config_entry("XNSEASONS"));				
			$xntop->add_row("", "Number of additional crop activity characteristics",	scalar( @{[$self->get_cfg_list_empty_ok("EXTRA_CROPACT_CHARACTERISTICS")]} ));	
			$xntop->add_row("", "Does the crop model want CropManagementID instead of CropActivityID?", $self->config_entry("CROP_MODEL_WANTS_MANAGEMENT_ID"));				
		}
		else { 
			$xntop->add_row("", "Number of crop activities per soil type", $self->{NumCropActPerSoil});
			$xntop->add_row("", "Number of crop activities (total over all soil types)", $self->{NumCropActTotal});		
			$xntop->add_row("", "Number of cropping seasons per year",	$self->config_entry("XNSEASONS") );	
		}
		$xntop->add_empty_rows(3);
		$self->add_block($xntop);

		my $xnact = MpmasInputBlock->new(3,30);
		if ($self->config_entry("MULTIPERIOD")) {
			my $soiltransheader = "";
			$soiltransheader = "New soil type after harvest" if($self->config_entry("DYNAMIC_SOIL_TRANSITIONS")); 
			my $header_unowned_land = "";
			 $header_unowned_land ="Activity for unowned land" if ($self->config_entry("DYNAMIC_SOIL_TRANSITIONS")
					|| $self->config_entry("BIOVERSION") == 5
					|| $self->config_entry("BIOVERSION") == 6
					||	$self->config_entry("BIOVERSION") == 7
					|| $self->config_entry("BIOVERSION") == 8);
					
			$xnact->add_comment_row("","","Crop activities", " Link ID", "CropManagementID", "Soil  type", "Initial crop yield", "Initial stover yield", 
							$soiltransheader , $header_unowned_land, $self->get_cfg_list_empty_ok("EXTRA_CROPACT_CHARACTERISTICS") );
		}
		else {	
			$xnact->add_comment_row("","","Crop activities (sorted by soil type)", " Link ID", "Soil  type", "Crop type", "Priority", "Output ID", "Initial crop yield", "Initial stover yield");
		}
		$xnact->add_table($sortedxnactivities);
		$xnact->add_empty_rows(3);
		$self->add_block($xnact);

		if ($self->config_entry("MULTIPERIOD")) {
			
			my $xnactivityRequiredAssetTab = $self->get_global("xnactivityRequiredAssetTab");
			
			my $xnpersoil = MpmasInputBlock->new(3,40);
			$xnpersoil->add_row("2","REQUIRED ASSETS");
			$xnpersoil->add_row("","2.1","Number of entries", scalar @$xnactivityRequiredAssetTab);
			
			$xnpersoil->add_comment_row("","","","CropActivityID",	"ObjID",	"MinAge","MaxAge"); 	
			$xnpersoil->add_table($xnactivityRequiredAssetTab);
			$xnpersoil->add_empty_rows(3);
			$self->add_block($xnpersoil);		
		}
		else  {
			my $sortedxnactivitiespersoil = $self->get_global("Sorted_Xn_Activities_Per_Soil");
			my $xnpersoil = MpmasInputBlock->new(3,40);
			$xnpersoil->add_row("2","NUTRIENT RESPONSE UNITS");
			$xnpersoil->add_row("2.1","LP COLUMN AND ROW INDICES");
			if ($self->config_entry("STATICNRU") == 1 and $self->config_entry("EXTERNALCGFORMAT0") ) {
				$xnpersoil->add_comment_row("","Link ID","Soil Type",	"LP index of activity",	"LP index of crop yield constraint","LP index of stover yield constraint"); 
				$xnpersoil->add_row("","Note: number of rows per soil type must equal maximum number of activites per soil type");	
			}			
			elsif ($self->config_entry("STATICNRU") == 0 || $self->config_entry("STATICNRU") >= 2 || not $self->config_entry("EXTERNALCGFORMAT0") ) {
				$xnpersoil->add_row("","Note: number of rows per NRU must equal maximum number of activites per soil type");	
				$xnpersoil->add_comment_row("","Slot","NRU",	"LP index of activity",	"LP index of crop yield constraint","LP index of stover yield constraint"); 			
				
			}
			$xnpersoil->add_table($sortedxnactivitiespersoil);
			$xnpersoil->add_empty_rows(3);
			$self->add_block($xnpersoil);
		}
}
1;
