package MpmasClusterSet;

use strict;
use warnings;
use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use SimpleObjectRegistry;
use MpmasInputBlock;
use SimpleObject;
use IndexedTable;
use CtUtilities qw(queryhash extqueryhash strip_sql);
our @ISA = qw(MpmasInputfileSet);
use MpmasCluster;
use MpmasAssets;
use MpmasAssetsAndAges;
use MpmasMembersAndAges;
use MpmasInitialAgentCharacteristics;
use MpmasMapDummies;

sub class_of_files{
	return "MpmasCluster";
 }
sub filename {
	return "ClusterSet";
}
sub determine_files {
	my $self = shift;
	for my $p (0..$self->config_entry("POPULATIONS") - 1) {
		for my $c (0..$self->config_entry("CLUSTERS") - 1) {
			my $fname = "$p"."_"."$c";
			
			$self->add_file($fname,$fname);
			$self->add_file($fname,$fname, "MpmasAssets") if ($self->config_entry("ASSETDISTTYPE") == 1);
			$self->add_file($fname,$fname, "MpmasAssetsAndAges") if ($self->config_entry("ASSETDISTTYPE") == 2);
			$self->add_file($fname,$fname, "MpmasMembersAndAges") if ($self->config_entry("ASSETDISTTYPE") == 2);
			$self->add_file($fname,$fname, "MpmasInitialAgentCharacteristics") if ($self->get_cfg_list_empty_ok("EXTRA_AGENT_ATTRIBUTES")) ;
		}
	}
	$self->add_file("","", "MpmasMapDummies") if ($self->config_entry("GENERATE_MAPS") > 0);
}

sub prepare_shared_files {
	my $self = shift;
	my $dbh = $self->get_dbh;
	my $sexagedef;
	if ($self->config_entry("ASSETDISTTYPE") < 2) {
		print STDERR "Error: Missing HOUSEHOLD_MEMBER_TYPES [MPMAS TABLES]\n" and exit(1) unless (defined $self->config_entry("HOUSEHOLD_MEMBER_TYPES"));
		$sexagedef = $self->config_entry("HOUSEHOLD_MEMBER_TYPES")->transform($dbh, 1);
	}
	my $householdhash;
	my $assethash;
	my $agentinfo;
	my $agentsinclu;
	if ($self->config_entry("ASSETDISTTYPE") == 0) {
			
		  print STDERR "\nError: Missing HOUSHOLD_CDF [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 0 (random allocation of assets within MPMAS)\n"  and exit(1) unless (defined $self->config_entry("HOUSEHOLD_CDF"));
		  $householdhash = $self->config_entry("HOUSEHOLD_CDF")->transform($dbh, 4);
		  print STDERR "\nError: Missing ASSET_CDF [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 0 (random allocation of assets within MPMAS)\n"  and exit(1) unless (defined $self->config_entry("ASSET_CDF"));
		  $assethash = $self->config_entry("ASSET_CDF")->transform($dbh,4);
	}
	elsif ($self->config_entry("ASSETDISTTYPE") == 1) {
		
		  print STDERR "\nError: Missing HOUSEHOLD_COMPOSITION [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 1 (prepared asset distribution provided)\n"  and exit(1) unless (defined $self->config_entry("HOUSEHOLD_COMPOSITION"));
		  print STDERR "\nError: Missing ASSET_ENDOWMENTS [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 1 (prepared asset distribution provided)\n"  and exit(1) unless (defined $self->config_entry("ASSET_ENDOWMENTS"));
		  $householdhash = $self->config_entry("HOUSEHOLD_COMPOSITION")->transform($dbh,2);
		  $assethash = $self->config_entry("ASSET_ENDOWMENTS")->transform($dbh,2);
		  #$assethash->printme_with_header_sorted();
		  if ($self->config_entry("AGENT_INFO_FROM_MAPS")) {
			    my $mapdir = "gis/";
			    $mapdir = $self->config_entry("MAPS") if (defined $self->config_entry("MAPS"));
			    $mapdir = $self->outdir ."../". $mapdir;
				$mapdir = $mapdir ."/" unless ($mapdir =~/\/$/);

				use EnhancedGrid;
				my $fstmap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Farm.txt");
				my $clumap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Clu.txt");
				my $popmap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Pop.txt");
				my $clutab = IndexedTable->create_from_hash($clumap->tabulate_values_by_grid($fstmap), 2, "clutab");
				my $poptab = IndexedTable->create_from_hash($popmap->tabulate_values_by_grid($fstmap), 2, "poptab");

				my %infohash;
				for my $i ($clutab->query([],0)) {
					my @p = $poptab->query([$i],0);
					my @c = $clutab->query([$i],0);
					$infohash{$i} = [( $p[0], $c[0], -1,-1,-1,-1)] if( $i != -1);
				}
				$agentinfo = IndexedTable->create_from_hash(\%infohash,1,"agentinfo");
				#$agentinfo->printme();
				$self->set_config_entry("MAP_X_EXTENT", $fstmap->ncols() );
				$self->set_config_entry("MAP_Y_EXTENT", $fstmap->nrows() );
		  }
		  else {
		      print STDERR "\nError: Missing AGENT_INFO [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 1 (prepared asset distribution provided)\n"  and exit(1) unless (defined $self->config_entry("AGENT_INFO"));

		      $agentinfo = $self->config_entry("AGENT_INFO")->transform($dbh,1);	
		  }
		  print STDERR "\nError: Query for AGENT_INFO returned an empty result.\n" and exit(1) if ($agentinfo->is_empty);
		  $agentsinclu = $agentinfo->regrouped_copy([2,3,1]);
	}
	elsif ($self->config_entry("ASSETDISTTYPE") == 2) {

		  ##First we need a list of all agents to be included. Typically it should come from the maps that also MPMAS will read
		  ##However, for simple models where there are no maps provided by the user and they need to be generated by mpmasql
		  ##We read it from any information about agents that has been provided:

		  ## First we check whether we should read them from the maps.
		  if ($self->config_entry("AGENT_INFO_FROM_MAPS")  && not $self->config_entry("GENERATE_MAPS") ) {
			    my $mapdir = "gis/";
			    $mapdir = $self->config_entry("MAPS") if (defined $self->config_entry("MAPS"));
			    $mapdir = $self->outdir ."../". $mapdir;
			     $mapdir = $mapdir ."/" unless ($mapdir =~/\/$/);
			     use EnhancedGrid;
			      my $fstmap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Farm.txt");
			      my $clumap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Clu.txt");
			      my $popmap = EnhancedGrid->create_from_ascii($mapdir."CatchMap00Pop.txt");
			      my $clutab = IndexedTable->create_from_hash($clumap->tabulate_values_by_grid($fstmap), 2, "clutab");
			      my $poptab = IndexedTable->create_from_hash($popmap->tabulate_values_by_grid($fstmap), 2, "poptab");

			      my %infohash;
			      for my $i ($clutab->query([],0)) {
				    my @p = $poptab->query([$i],0);
				    my @c = $clutab->query([$i],0);
				    $infohash{$i} = [( $p[0], $c[0], -1,-1,-1,-1)] if( $i != -1);
			      }
			      $agentinfo = IndexedTable->create_from_hash(\%infohash,1,"agentinfo");
					#$agentinfo->printme();
			      $self->set_config_entry("MAP_X_EXTENT", $fstmap->ncols() );
			      $self->set_config_entry("MAP_Y_EXTENT", $fstmap->nrows() );

 	      		      $agentsinclu = $agentinfo->regrouped_copy([2,3,1]);
		  }
		  ##then we check whether the user has provided an explicit AGENT_INFO list
		  if($self->config_entry("AGENT_INFO")) {
		  	$agentinfo = $self->config_entry("AGENT_INFO")->transform($dbh,1);
		  	my @columnsToAdd = ();
		  	my @columnValuesToAdd = ();
		  	#$agentinfo->printme_with_header();
		  	if (! $agentinfo->exists_columnname("ClusterID")) {
		  		if ($self->config_entry("CLUSTERS") > 1) {
		  			print STDERR "Error: CLUSTERS > 1 and missing ClusterID in AGENT_INFO";
		  			exit(1);
		  		}
				push @columnsToAdd, "ClusterID";
				push @columnValuesToAdd, 0;
		  	}
		  	if (! $agentinfo->exists_columnname("PopulationID")) {
		  		if ($self->config_entry("POPULATIONS") > 1) {
		  			print STDERR "Error: POPULATIONS > 1 and missing PopulationID in AGENT_INFO";
		  			exit(1);
		  		}
				push @columnsToAdd, "PopulationID";
				push @columnValuesToAdd, 0;
		  	}
		  	if (! $agentinfo->exists_columnname("NetworkID")) {
		  		if ($self->config_entry("NETWORKS") > 1) {
		  			print STDERR "Error: NETWORKS > 1 and missing NetworkID in AGENT_INFO";
		  			exit(1);
		  		}
				push @columnsToAdd, "NetworkID";
				push @columnValuesToAdd, 0;
		  	}
		  	if (! $agentinfo->exists_columnname("CatchmentID")) {

				push @columnsToAdd, "CatchmentID";
				push @columnValuesToAdd, -1;
		  	}
		  	if (! $agentinfo->exists_columnname("SectorID")) {
		
				push @columnsToAdd, "SectorID";
				push @columnValuesToAdd, -1;
		  	}
		  	if (! $agentinfo->exists_columnname("NumberOfPlots")) {

				push @columnsToAdd, "NumberOfPlots";
				push @columnValuesToAdd, 1;
		  	}
		  	$agentinfo->add_columns(\@columnsToAdd, \@columnValuesToAdd);
		  	#$agentinfo->printme_with_header();
		  	$agentsinclu = $agentinfo->regrouped_copy_by_colnames(["PopulationID","ClusterID","FarmsteadID"]);
		  }
		  ##then we check for a HOUSEHOLD_MEMBERS table
		  elsif ( defined $self->config_entry("HOUSEHOLD_MEMBERS")) {

				my $charhash = $self->config_entry("HOUSEHOLD_MEMBERS")->transform($dbh,4);
				my @agentlist = $charhash->query([],0);

				my %infohash;
				for my $i (@agentlist) {
					$infohash{$i} = [( 0, 0, 0,-1,0,1,0)] if( $i != -1);
				}
				$agentinfo = IndexedTable->create_from_hash(\%infohash,1,"agentinfo",["FarmsteadID", "PopulationID", "ClusterID", "CatchmentID", "AgentID", "SectorID", "NumberOfPlots", "NetworkID"]);
				$agentsinclu = $agentinfo->regrouped_copy([2,3,1]);

		  }
		  ##then we check for an AGENT_ATTRIBUTES table
		  elsif ($self->get_cfg_list_empty_ok("EXTRA_AGENT_ATTRIBUTES") and defined $self->config_entry("AGENT_ATTRIBUTES")) {

				my $charhash = $self->config_entry("AGENT_ATTRIBUTES")->transform($dbh,2);
				my @agentlist = $charhash->query([],0);

				my %infohash;
				for my $i (@agentlist) {
					$infohash{$i} = [( 0, 0, 0,-1,0,1,0)] if( $i != -1);
				}
				$agentinfo = IndexedTable->create_from_hash(\%infohash,1,"agentinfo",["FarmsteadID", "PopulationID", "ClusterID", "CatchmentID", "AgentID", "SectorID", "NumberOfPlots", "NetworkID"]);
				$agentsinclu = $agentinfo->regrouped_copy([2,3,1]);


		  }
		  ##then we check for an ASSET_OWNERSHIP table
		  elsif ( defined $self->config_entry("ASSET_OWNERSHIP")) {

				my $charhash = $self->config_entry("ASSET_OWNERSHIP")->transform($dbh,3);
				my @agentlist = $charhash->query([],0);

				my %infohash;
				for my $i (@agentlist) {
					$infohash{$i} = [( 0, 0, 0,-1,0,1,0)] if( $i != -1);
				}
				$agentinfo = IndexedTable->create_from_hash(\%infohash,1,"agentinfo",["FarmsteadID", "PopulationID", "ClusterID", "CatchmentID", "AgentID", "SectorID", "NumberOfPlots", "NetworkID"]);
				$agentsinclu = $agentinfo->regrouped_copy([2,3,1]);

		  }

		  else {
		      print STDERR "\nError: Missing AGENT_INFO [MPMAS TABLES] and cfg entry AGENT_INFO_FROM_MAPS == 0\n",
						   "and also no HOUSEHOLD_MEMBERS, ASSET_OWNERSHIP, or AGENT_ATTRIBUTES table.\n",
						   "=> No list of agents to be included in the model."
							and exit(1) unless (defined $self->config_entry("AGENT_INFO"));

		      $agentinfo = $self->config_entry("AGENT_INFO")->transform($dbh,1);	
      		  $agentsinclu = $agentinfo->regrouped_copy([2,3,1]);
		  }
		  print STDERR "\nError: Agent list is empty.\n" and exit(1) if ($agentinfo->is_empty);

		  
		  
		  #print STDERR "\nError: Missing HOUSEHOLD_MEMBERS [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 2 (prepared asset and age distribution provided)\n"  and exit(1) unless (defined $self->config_entry("HOUSEHOLD_MEMBERS"));

		  #print STDERR "\nError: Missing ASSET_OWNERSHIP [MPMAS TABLES].\nThis is required because ASSETDISTTYPE has been set to 2 (prepared asset and age distribution provided)\n"  and exit(2) unless (defined $self->config_entry("ASSET_OWNERSHIP"));

		  if (defined $self->config_entry("HOUSEHOLD_MEMBERS")) {
			$householdhash = $self->config_entry("HOUSEHOLD_MEMBERS")->transform($dbh,4); ## no problem if it was already transformed earlier when looking for agent info. Will then automoatically use the already transformed one.
		  }
		  else {
			print STDERR "\nInfo: HOUSEHOLD_MEMBERS [MPMAS TABLES] table not provided. Using default assignment\n";

			my @agentlist = $agentinfo->query([],0);

			my %mhash;
			for my $i (@agentlist) {
				$mhash{$i}{"worker"}{30}{0} = [ 1];

			}

			$householdhash = IndexedTable->create_from_hash(
				\%mhash,
				 4, "HOUSEHOLD_MEMBERS" ,
				[ "farmstead_id", "career", "age", "married", "quantity"]
			);
		  }

		  if (defined $self->config_entry("ASSET_OWNERSHIP")) {
			$assethash = $self->config_entry("ASSET_OWNERSHIP")->transform($dbh,3); ## no problem if it was already transformed earlier when looking for agent info. Will then automoatically use the already transformed one.
		  }
		  else {
			print STDERR "\nInfo: ASSET_OWNERSHIP [MPMAS TABLES] table not provided. No assets assigned to agents\n";
			my @agentlist = $agentinfo->query([],0);

			my %infohash;
			for my $i (@agentlist) {
				$infohash{$i}{"liquidity"}{-1} = 1  if( $i != -1);
			}
			$assethash = IndexedTable->create_from_hash(\%infohash,3,"ASSET_OWNERSHIP", [("fstID", "asset", "age", "quantity")]);

		  }
		  
	}
	else {
	    print STDERR "\nError in cfg: Invalid value [". $self->config_entry("ASSETDISTTYPE") ."] for ASSETDISTTYPE" and exit(1);
	}	
	my $charhash;
	if ($self->get_cfg_list_empty_ok("EXTRA_AGENT_ATTRIBUTES"))	{
		  print STDERR "\nError: Missing AGENT_ATTRIBUTES [MPMAS TABLES].\nThis is required because you have included EXTRA_AGENT_ATTRIBUTES\n"  and exit(2) unless (defined $self->config_entry("AGENT_ATTRIBUTES"));
		  
		  $charhash = $self->config_entry("AGENT_ATTRIBUTES")->transform($dbh,2);
	}
	
	
	for my $f ($self->files) {
		$f->prepare_shared($sexagedef, $householdhash, $assethash, $agentsinclu);
	}	
	$self->set_sexagedef($sexagedef);
	$self->set_householdhash($householdhash);
	$self->set_assethash($assethash);
	$self->set_agentinfo($agentinfo);
	$self->set_charhash($charhash);
	
	


}


	
	


sub prepare_special_files {
    my $self = shift;
    my $dbh = $self->get_dbh;
    my $fixcosthash = $self->config_entry("FIXCOSTS")->transform($dbh,3) if ($self->config_entry("FIXCOSTS"));
    for my $f ($self->files) {
	  $f->prepare_special($self->sexagedef, $self->householdhash, $fixcosthash, $self->assethash, $self->agentinfo, $self, $self->charhash);
    }
}

sub sexagedef {
    my $self = shift;
    return $self->{sexagedef};
}
sub set_sexagedef {
    my $self = shift;
    $self->{sexagedef}  = shift;
}
sub householdhash {
    my $self = shift;
    return $self->{housholdhash};
}
sub set_householdhash {
    my $self = shift;
    $self->{housholdhash}  = shift;
}
sub assethash {
    my $self = shift;
    return $self->{assethash};
}
sub set_assethash {
    my $self = shift;
    $self->{assethash}  = shift;
}
sub charhash {
    my $self = shift;
    return $self->{charhash};
}
sub set_charhash {
    my $self = shift;
    $self->{charhash}  = shift;
}
1;
