package MpmasCluster;

use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use CtUtilities qw(queryhash extqueryhash);
use MpmasInputfile;
use MpmasInputBlock;
our @ISA = qw(MpmasInputfile);

sub filename {
	my $self = shift;
	my $temp = $self->file_suffix;
	return "Clu$temp";
}

sub required {
	return 1;
}
sub assets {
    return $_[0]->{assetlist};
}
sub oids {
    return $_[0]->{oidlist};
}
sub sexagecats {
    return $_[0]->{sexagecatlist};
}
sub prepare_shared {
	my $self = shift;	
	my $sexagedef = shift;	
	my $householdhash = shift;	
	my $assethash = shift;	
	my $agentsinclu = shift;
	$self->{numagents} = 1000;
	$self->{lablotsegs} = 0;   

	$self->{assettabsegs} = 0;
	$self->{assetlist} = [()];

	##List all sex-age-categories so we are sure they are mentioned; probably needed in order to make ASSETENDOW from old lt2 possible
	if ($self->config_entry("ASSETDISTTYPE") < 2) {
		$self->{sexagecatlist} =  [( sort {$a cmp $b}  $sexagedef->query([],0) )];
	}
	$self->argument =~ /(\d+)_(\d+)/;
	my $curPop = $1;
	my $curClu = $2;
	if ($self->config_entry("ASSETDISTTYPE") == 0) {
		#determine number of lablotsegs by finding the category with the largest number of segments
		for my $curCat ($householdhash->query([$curPop,$curClu],0)){
			my @tmp = $householdhash->query([$curPop,$curClu, $curCat],0);
			my $x = @tmp;
			$self->{lablotsegs} = $x if $x > $self->{lablotsegs};
		}
		## list only those sex-age-categories which are actually used
		##$self->{sexagecatlist} = [ (sort {$a cmp $b}  $householdhash->query([$curPop,$curClu],0) ) ];

		#determine number of assettabsegss by finding the asset with the largest number of segments
		for my $curAss ($assethash->query([$curPop,$curClu],0)){
			my @tmp = $assethash->query([$curPop,$curClu, $curAss],0);
			my $x = @tmp;
			$self->{assettabsegs} = $x if $x > $self->{assettabsegs};
		}
		
		my @SortedAssets = sort {$a cmp $b} $assethash->query([$curPop,$curClu],0) ;
		$self->{assetlist} = \@SortedAssets;
	}
	elsif ($self->config_entry("ASSETDISTTYPE") == 1) {
	
		##Assets
		my %collect;
		for my $ag ($agentsinclu->query([$curPop, $curClu],0) ) {
		   foreach  ( $assethash->query([$ag],0) ) {
				print "Note: no assets found for agent $ag [$curPop, $curClu]\n" and next unless (defined $_); 
				$collect{$_} = 1; 
		    } 
		}
		my @SortedAssets = sort {$a cmp $b} keys %collect ;
		my %specobjindex = %{$self->get_specobj_indices()};
		my %specobjcheck;
		my @oids ; 
		my @assets;
		foreach  my $i (@SortedAssets) {
			if (exists $specobjindex{$i}) {
			    push @oids, $self->config_entry("SPECOBJS")->[$specobjindex{$i}] ;
			    push @assets,  $i;
			    $specobjcheck{$i}  = 1;
			} 
			elsif (defined $self->networkobject($i) )  {
			      push @oids, $self->networkobject($i)->oid;
			      push @assets,  $i;
			}
			else {
			    print "Note from Asset Assignment [$curPop, $curClu]: Asset $i is not part of the current model!\n";
			}
		}
		foreach my $t (keys %specobjindex) {
			unless (exists $specobjcheck{$t}) {
				print STDERR "Error: None of the agents in Population $curPop, Cluster $curClu is being assigned a value for $t and you did not set a global value for ".uc($t)."in the cfg file either!!\n" and exit(1) unless (defined $self->config_entry(uc($t)));
				push @assets, $t ;
				push @oids, $self->config_entry("SPECOBJS")->[$specobjindex{$t}] ;
			}
		}	
		$self->{oidlist} = \@oids;
		$self->{assetlist} = \@assets;
	}
	elsif ($self->config_entry("ASSETDISTTYPE") == 2) {
	
		##Assets
		my %collect;
		for my $ag ($agentsinclu->query([$curPop, $curClu],0) ) {
		   foreach  ( $assethash->query([$ag],0) ) {
				print "Note: no assets found for agent $ag [$curPop, $curClu]\n" and next unless (defined $_); 
				$collect{$_} = 1; 
		    } 
		}
		my @SortedAssets = sort {$a cmp $b} keys %collect ;
		my %specobjindex = %{$self->get_specobj_indices()};
		my %specobjcheck;
		my @oids ; 
		my @assets;
		foreach  my $i (@SortedAssets) {
			if (exists $specobjindex{$i}) {
			    push @oids, $self->config_entry("SPECOBJS")->[$specobjindex{$i}] ;
			    push @assets,  $i;
			    $specobjcheck{$i}  = 1;
			} 
			elsif (defined $self->networkobject($i) )  {
			      push @oids, $self->networkobject($i)->oid;
			      push @assets,  $i;
			}
			else {
			    print "Note from Asset Assignment [$curPop, $curClu]: Asset $i is not part of the current model!\n";
			}
		}
		foreach my $t (keys %specobjindex) {
			unless (exists $specobjcheck{$t}) {
				print STDERR "Error: None of the agents in Population $curPop, Cluster $curClu is being assigned a value for $t and you did not set a global value for ".uc($t)."in the cfg file either!!\n" and exit(1) unless (defined $self->config_entry(uc($t)));
				push @assets, $t ;
				push @oids, $self->config_entry("SPECOBJS")->[$specobjindex{$t}] ;
			}
		}	
		$self->{assetlist} = \@assets;
	}
}

sub prepare_special {
	my $self = shift;	
	my $sexagedef = shift;	
	my $householdhash = shift;	
	my $fixcosthash = shift;	
	my $assethash = shift;	
	my $agentinfo = shift;
	my $parent = shift;
	
	$self->argument =~ /(\d+)_(\d+)/;
	my $curPop = $1;
	my $curClu = $2;

	my $lablotsegs = $self->{lablotsegs};
	
		
	my $poplabtab = MpmasInputBlock->new(2,30);
	if ($self->config_entry("ASSETDISTTYPE") < 2) {

		for my $curCat ( @{ $self->{sexagecatlist} } ) {
			
			
			my @entry;
			
			$entry[1] = $curCat;
			push @entry , "";
			my $kind = $sexagedef->query([$curCat], 2);
			#$kind =~ s/^(\d*)$/x$1/;
			my $s = $self->config_entry("CAREERS")->{$kind};
			print "Warning!!!!: There is no entry in the household dynamics table for category/career '$curCat' / '$kind'\n" and $s = "???" unless (defined $self->config_entry("CAREERS")->{$kind});
			
			push @entry, $s;
			
			push @entry, $sexagedef->query([$curCat],3);
			push @entry, $sexagedef->query([$curCat],4);
			push @entry, $sexagedef->query([$curCat],5);
			if ($self->config_entry("ASSETDISTTYPE") == 0 ) {
				  #put segments into the right order and check consistency
				  my @segbounds = $householdhash->query([$curPop,$curClu, $curCat],0);

				  if (defined $segbounds[0]) { #don't go in here if no entry in householdhash, captured in #elsif1
					 @segbounds = sort{$a  <=> $b } @segbounds;
					my $highestsegbound = $#segbounds;
					die "Error in HOUSEHOLDCDF: Highest upper bound in Pop: $curPop, Clu: $curClu,Sex-Age-Category $curCat is $segbounds[$highestsegbound], but should be 100!!\n" unless ($segbounds[$highestsegbound] == 100);
				  }
				  for my $i (0..($lablotsegs -1)){						
					  my $cB;
					  my $cV;
					  if (defined $segbounds[$i]){
						  
						  $cB = $segbounds[$i];
						  $cV = $householdhash->query([$curPop,$curClu, $curCat, $cB],5);
						  die "Error: undefined value returned from HOUSEHOLDCDF for  $curPop,$curClu, $curCat, $cB\n" unless (defined $cV);
					  }elsif (not defined $segbounds[$i] and $i == 0){ #if no entry in householdhash ##elsif1
						  $cB = 100;
						  $cV = 0;
					  }else{
						  $cB = 0;
						  $cV = 0;
						  
					  }
					  push @entry, $cB;
					  push @entry, $cV;
				  }
			}
			$poplabtab->add_row(@entry);
			
		}
		$self->add_block($poplabtab);
	}
	
	#prepare head for population file
	my $pophead = MpmasInputBlock->new(3,10);
	$pophead->add_row("THE CLUSTER FILE");
	$pophead->add_row("POPULATION: $curPop","CLUSTER: $curClu");
	$pophead->add_row("1","AGENT SEX AND AGE COMPOSITION");
	$pophead->add_row("1.1", "Number of agents in cluster", "",$self->{numagents});
	if ($self->config_entry("ASSETDISTTYPE") < 2) {
		$pophead->add_row("1.2", "Number of sex age categories", "",  scalar @{ $self->{sexagecatlist} });
	}
	else {	
		$pophead->add_row("1.2", "Number of sex age categories", "",  0);
	}
	$pophead->add_row("1.3", "Number of lottery segments", "","$lablotsegs");
	$pophead->add_empty_rows(2);
	
	my @poplabtabdes;
	@poplabtabdes[0..6] = ("","Sex-Age-Category","","CareerId","Sex","Lower Age", "Upper Age");
	if ($self->config_entry("ASSETDISTTYPE") == 0 ) {
	    for my $i (1..$lablotsegs){
		push @poplabtabdes, "Upper Bound, Seg. $i";
		push @poplabtabdes, "Value, Seg. $i";
	    }
	}
	$pophead->add_comment_row(@poplabtabdes);
	$self->add_block($pophead);

	#prepare table for asset distribution
	my $assettabsegs = $self->{assettabsegs};
	
	if ($self->config_entry("ASSETDISTTYPE") < 2 ) {
		my $popassettab = MpmasInputBlock->new(3,100);
	
		my %specobjname = %{$self->get_specobj_names()};
		my %specobjtype = %{$self->get_specobj_types()};
		my %specobjcheck;
	
	
		
		for my $curAss (@{ $self->{assetlist} }){
			my @entry;
			$entry[1] = $curAss;
			
			
			if (exists $specobjname{lc($curAss)}) {
				my $co = -1 * $specobjtype{lc($curAss)};
				push @entry, $specobjname{lc($curAss)};
				push @entry, $self->config_entry("SPECOBJS")->[$co];
				push @entry, $specobjtype{lc($curAss)};
				push @entry, "";
				push @entry, "0";
				$specobjcheck{lc($curAss)} = 1;
			}
			else {	
				unless (defined $self->networkobject($curAss)) {
					  print STDERR "\nError in asset assignment!\n";
					  print STDERR "The following assets have been defined:\n";
					  foreach ($self->networkobjects) {
						 print $_->id ."\n";
					  }
					  print STDERR "Error: You try to assign an asset '$curAss', which has not been defined anywhere in the model structure.\n";
					  exit(1) ;
				}
				my $k = $self->networkobject($curAss);
				push @entry,  $k->get_attribute("name");
				push @entry, $k->oid;
				push @entry, $k->type;
				push @entry , "";
				my $landreq = 0;
				$landreq = $k->get_attribute("landreq") if ($self->config_entry("ASSETDISTTYPE") == 0);
				push @entry, $landreq;
			}
			if ($self->config_entry("ASSETDISTTYPE") == 0 ) {
				#put segments into the right order and check consistency
				my @segbounds = $assethash->query([$curPop,$curClu, $curAss],0);
				for my $i (0..$#segbounds) {
					$segbounds[$i] =~ s/^x//;
				}
				@segbounds = sort{$a  <=> $b } @segbounds;
				my $highestsegbound = $#segbounds;
				die "Error in ASSETCDF: Highest upper bound in Pop: $curPop, Clu: $curClu, Asset '$curAss' is $segbounds[$highestsegbound], but should be 100!!\n" unless ($segbounds[$highestsegbound] == 100);
				
				for my $i (0..($assettabsegs -1)){						
					my $cB;
					my $cV;
					if (defined $segbounds[$i]){
						
						$cB = $segbounds[$i];
						$cV = $assethash->query([$curPop,$curClu,$curAss,$cB],5);
						die "Error: undefined value returned from ASSETCDF for  $curPop,$curClu, $curAss, $cB\n" unless (defined $cV);
					}elsif (not defined $segbounds[$i] and $i == 0){ #should never actually happen, but for security
						$cB = 100;
						$cV = 0;
					}else{
						$cB = 0;
						$cV = 0;
						
					}
					push @entry, $cB;
					push @entry, $cV;
				}
			
			}
			
			$popassettab->add_row(@entry);
			
		}
		my $numassets = scalar @{ $self->{assetlist} };
		# Check if values for all special objects have been supplied
		foreach my $t (keys %specobjname) {
			unless (exists $specobjcheck{$t}) {
				my @entry;
				$entry[1] = $t;
				my $co = -1 * $specobjtype{lc($t)};
				push @entry, $specobjname{lc($t)};
				push @entry, $self->config_entry("SPECOBJS")->[$co];
				push @entry, $specobjtype{lc($t)};
				push @entry, "";
				push @entry, "0";
				for my $i (0..($assettabsegs -1)){						
					if ($i == 0) {
						push @entry, 100;
						push @entry, $self->config_entry(uc($t));
					}
					else {
						push @entry, 0;
						push @entry, 0;
					}
				}	
		
				$popassettab->add_row(@entry);
				$numassets++;
		
			}		
		}
		$self->add_block($popassettab);
		#prepare head of asset distribution table
		my $popassettabhead = MpmasInputBlock->new(3,90);
		$popassettabhead->add_row("2", "AGENT ASSET COMPOSITION");
		$popassettabhead->add_row("2.1", "Number of assets to be allocated", "","$numassets");
		$popassettabhead->add_row("2.2","Number of lottery segments", "","$assettabsegs");

		my @popassettabdes = ("","Asset","Name","ObjectId","ObjectType","", "Land Requirement");
		if ($self->config_entry("ASSETDISTTYPE") == 0 ) {
			 for my $i (1..$assettabsegs){
			  push @popassettabdes, "Upper Bound, Seg. $i";
			  push @popassettabdes, "Value, Seg. $i";
			}
		}

		$popassettabhead->add_comment_row(@popassettabdes);

		$self->add_block($popassettabhead);
	}
	#prepare fixcosttable
	my $numfixcosts = 0;
	my $fixcosttab = MpmasInputBlock->new(3,200);
	if (defined $fixcosthash) {
		if (defined $fixcosthash->query([$curPop,$curClu],0)) {
			my @SortedFC = $fixcosthash->query([$curPop,$curClu],0);
			@SortedFC = sort {$a cmp $b} @SortedFC;
			for my $i(@SortedFC) {
				my $cc = $i;
				$cc =~ s/x//;
				$fixcosttab->add_row("","","","$cc",$fixcosthash->query([$curPop,$curClu,$i],4), $fixcosthash->query([$curPop,$curClu,$i],5));
				$numfixcosts += 1;
			}
			
		}
	}
	my $fixcosthead = MpmasInputBlock->new(3,190);
	$fixcosthead->add_empty_rows(2);
	$fixcosthead->add_row("3","Fix Cost Matrix");
	$fixcosthead->add_row("3.1","Number of Fix Cost Size Classes","","$numfixcosts");
	
	$fixcosthead->add_comment_row("","","","Upper Bound", "Fix", "Var");
	$self->add_block($fixcosthead);
	$self->add_block($fixcosttab);

}
1;
