# Set the state manually, over-ride with a command-line option if present:
$state = "sa";
if ($#ARGV == 0) {
	$state = $ARGV[0];
}

# The input file is the formal preferences by polling place.
# The AEC gives these names like
# SenateStateFirstPrefsByPollingPlaceDownload-20499-NT.csv
# which I've renamed to prefs_nt.csv, prefs_nsw.csv, etc.
$infile_votes = "<pref_files/prefs_" . $state . ".csv";

# CSV file with my party/group abbreviations:
$infile_groups = "<groups/" . $state . "_groups.csv";

# Output file to store the ATL votes with no other information:
$outfile_atl = ">atl_votes_" . $state . ".csv";

# Output files for the graph distances, defined in various ways:
$outfile_graph_max = ">graph_distances/distances_max_" . $state . "_blankmax.csv";
$outfile_graph_avg = ">graph_distances/distances_max_" . $state . "_blankavg.csv";
$outfile_graph_asym = ">graph_distances/distances_max_" . $state . "_blankmax_asym.csv";
$outfile_graph_asym_wtd = ">graph_distances/distances_max_" . $state . "_blankmax_asym_wtd.csv";

# Number of BTL candidates (I could work these out later but easier
# to set them here).
if ($state eq "tas") {
	$num_btl = 58;
} elsif ($state eq "wa") {
	$num_btl = 79;
} elsif ($state eq "sa") {
	$num_btl = 64;
} elsif ($state eq "qld") {
	$num_btl = 122;
} elsif ($state eq "nsw") {
	$num_btl = 151;
} elsif ($state eq "vic") {
	$num_btl = 116;
} elsif ($state eq "act") {
	$num_btl = 22;
} elsif ($state eq "nt") {
	$num_btl = 19;
}

# Read the party abbreviations:
open(in_file_groups, $infile_groups);
$line = <in_file_groups>;
@atl_groups = ();

while (<in_file_groups>) {
	$line = $_;
	chomp($line);
	
	# The CSV file may have commas in party names, but they
	# don't exist in the first column (abbreviations), so no
	# need for Text::CSV.
	@cells = split(",", $line);
	$this_group = $cells[0];
	$this_group =~ s/\"//g;
	
	push(@atl_groups, $this_group);
}

close(in_file_groups);


# Some useful variables for reading the preference lines:
$num_atl = $#atl_groups + 1;
$num_total = $num_atl + $num_btl;

$atl_i = $num_atl - 1;
$btl_start_i = $num_atl;
$btl_end_i = $num_total - 1;


# Initialise the "matrix" for each distances graph and the number
# of votes: stored as a hash, each element being an array equal to
# a row of the matrix.
%distances_max = {};
%distances_avg = {};
%distance_asym = {};
%distance_asym_wtd = {};

%N_votes = {};
%N_votes_asym = {};
%N_not_preffed = {};

for ($i = 0; $i < $num_atl; $i++) {
	$distances_max{$i} = ();
	$distances_avg{$i} = ();
	$distances_asym{$i} = ();
	$distances_asym_wtd{$i} = ();
	
	$N_votes{$i} = ();
	$N_votes_asym{$i} = ();
	$N_not_preffed{$i} = ();
	
	for ($j = 0; $j < $num_atl; $j++) {
		${distances_max{$i}}[$j] = 0;
		${distances_avg{$i}}[$j] = 0;
		${distances_asym{$i}}[$j] = 0;
		${distances_asym_wtd{$i}}[$j] = 0;
		${N_votes{$i}}[$j] = 0;
		${N_votes_asym{$i}}[$j] = 0;
		${N_not_preffed{$i}}[$j] = 0;
	}
}

# Time to start reading the preferences file.
open(in_file_votes, $infile_votes);
open(out_file_atl, $outfile_atl);

print out_file_atl join(",", @atl_groups) . "\n";

# First line of the file is the column headers, second
# line is a bunch of hyphens, so skip over these.
$line = <in_file_votes>;
$line = <in_file_votes>;

$line_ct = 3;

while(<in_file_votes>) {
	$line = $_;
	chomp($line);
	
	if ($line_ct % 10000 == 0) {
		# Progress update:
		print $line_ct . "\n";
	}
	
	# The preferences are the only part of the line
	# inside quotation marks, so isolate that part:
	$pref_part = $line;
	$pref_part =~ s/.*?\"//;
	$pref_part =~ s/\"//;
	
	# Replace / and * with 1:
	$pref_part =~ s/\//1/g;
	$pref_part =~ s/\*/1/g;
	
	@full_prefs = split(",", $pref_part);
	
	@atl_prefs = @full_prefs[0..$atl_i];
	@btl_prefs = @full_prefs[$btl_start_i..$btl_end_i];
	
	# Check BTL formality.
	$formal_btl = 1;
	for ($i = 1; $i <= 6; $i++) {
		@this_numbers = grep { $_ == $i; } @btl_prefs;
		
		if ($#this_numbers != 0) {
			# Either a duplicated preference or an exhaust by preference 6.
			$formal_btl = 0;
			last;
		}
	}
	
	if ($formal_btl == 0) {
		# Vote is ATL.
		
		# Initialise an array to store the groups
		# in preference order:
		@this_prefs = ();
		
		# @atl_prefs might have duplicated preferences or
		# other irregularities, so initialise a new array
		# to be written to file:
		@atl_to_print = ();
		for ($i = 0; $i < $num_atl; $i++) {
			# Initialise to the last preference...
			# maybe this would be better left blank
			# and processed as needed in R.
			push(@atl_to_print, $num_atl);
		}
		
		for ($i = 1; $i <= $num_atl; $i++) {
			@this_numbers = grep { $atl_prefs[$_] == $i; } 0..$#atl_prefs;
			
			if ($#this_numbers != 0) {
				# Exhaust or duplicate.
				last;
			} else {
				push(@this_prefs, $this_numbers[0]);
				$atl_to_print[$this_numbers[0]] = $i;
			}
		}
		
		print out_file_atl join(",", @atl_to_print) . "\n";
		
		# Add distances.
		$this_prim = $this_prefs[0];
		
		# For all distance types except asym_wtd, first add
		# the "not preferenced" distance to all groups,
		# then afterwards subtract it from the actually
		# preferenecd groups.
		for ($i = 0; $i < $num_atl; $i++) {
			if ($i != $this_prim) {
				$distances_max{$this_prim}[$i] += $num_atl - 1;
				$distances_max{$i}[$this_prim] += $num_atl - 1;
				
				$distances_avg{$this_prim}[$i] += 0.5*($num_atl + $#this_prefs);
				$distances_avg{$i}[$this_prim] += 0.5*($num_atl + $#this_prefs);
				
				$distances_asym{$this_prim}[$i] += $num_atl - 1;
				
				$N_votes{$this_prim}[$i]++;
				$N_votes{$i}[$this_prim]++;
				$N_votes_asym{$this_prim}[$i]++;
				$N_not_preffed{$this_prim}[$i]++;
			}
		}
		
		for ($i = 1; $i <= $#this_prefs; $i++) {
			$this_pref = $this_prefs[$i];
			$distances_max{$this_prim}[$this_pref] += $i - ($num_atl - 1);
			$distances_max{$this_pref}[$this_prim] += $i - ($num_atl - 1);
			
			$distances_avg{$this_prim}[$this_pref] += $i - 0.5*($num_atl + $#this_prefs);
			$distances_avg{$this_pref}[$this_prim] += $i - 0.5*($num_atl + $#this_prefs);
			
			$distances_asym{$this_prim}[$this_pref] += $i - ($num_atl - 1);
			
			# For asym_wtd we don't have to subtract off anything:
			$distances_asym_wtd{$this_prim}[$this_pref] += $i;
			
			$N_not_preffed{$this_prim}[$this_pref]--;
		}
	}
	
	$line_ct++;
}

close(in_file);

$sum_not_preffed = 0;

for ($i = 0; $i < $num_atl; $i++) {
	for ($j = 0; $j < $i; $j++) {
		if ($N_votes{$i}[$j] > 0) {
			$distances_max{$i}[$j] /= $N_votes{$i}[$j];
			$distances_max{$j}[$i] /= $N_votes{$j}[$i];
			
			$distances_avg{$i}[$j] /= $N_votes{$i}[$j];
			$distances_avg{$j}[$i] /= $N_votes{$j}[$i];
			
			$distances_asym{$i}[$j] /= $N_votes_asym{$i}[$j];
			$distances_asym{$j}[$i] /= $N_votes_asym{$j}[$i];
			
			# Re-symmetrise the asymmetric distances:
			$distances_asym{$i}[$j] = 0.5*($distances_asym{$i}[$j] + $distances_asym{$j}[$i]);
			$distances_asym{$j}[$i] = $distances_asym{$i}[$j];
		}
	}
	
	# Preparation for the weighting of the not-preferenced votes:
	for ($j = 0; $j < $num_atl; $j++) {
		if ($i != $j) {
			# Careful with the order of the indices here.
			# In braces is the primary vote party; we
			# want to sum a party's not-preferenced votes
			# over all primary vote possibilities.
			$N_not_preffed{$i}[$i] += $N_not_preffed{$j}[$i];
		}
	}
	
	$sum_not_preffed += $N_not_preffed{$i}[$i];
}

$mean_not_preffed = $sum_not_preffed / $num_atl;

# Add the weighted not-preferenced votes:
for ($i = 0; $i < $num_atl; $i++) {
	for ($j = 0; $j < $num_atl; $j++) {
		if ($i != $j) {
			$distances_asym_wtd{$i}[$j] += ($num_atl - 1) * $N_not_preffed{$i}[$j] * $mean_not_preffed / $N_not_preffed{$j}[$j];
			$distances_asym_wtd{$i}[$j] /= $N_votes_asym{$i}[$j];
		}
	}
}

# Re-symmetrise:
for ($i = 0; $i < $num_atl; $i++) {
	for ($j = 0; $j < $i; $j++) {
		if ($i != $j) {
			$distances_asym_wtd{$i}[$j] = 0.5*($distances_asym_wtd{$i}[$j] + $distances_asym_wtd{$j}[$i]);
			$distances_asym_wtd{$j}[$i] = $distances_asym_wtd{$i}[$j];
		}
	}
}

open(out_file_graph_max, $outfile_graph_max);
open(out_file_graph_avg, $outfile_graph_avg);
open(out_file_graph_asym, $outfile_graph_asym);
open(out_file_graph_asym_wtd, $outfile_graph_asym_wtd);

print out_file_graph_max join(",", @atl_groups) . "\n";
print out_file_graph_avg join(",", @atl_groups) . "\n";
print out_file_graph_asym join(",", @atl_groups) . "\n";
print out_file_graph_asym_wtd join(",", @atl_groups) . "\n";

for ($i = 0; $i < $num_atl; $i++) {
	$out_line_max = "";
	$out_line_avg = "";
	$out_line_asym = "";
	$out_line_asym_wtd = "";
	
	for ($j = 0; $j < $num_atl; $j++) {
		$comma = ",";
		if ($j == $num_atl - 1) { $comma = ""; }
		
		$out_line_max .= $distances_max{$i}[$j] . $comma;
		$out_line_avg .= $distances_avg{$i}[$j] . $comma;
		$out_line_asym .= $distances_asym{$i}[$j] . $comma;
		$out_line_asym_wtd .= $distances_asym_wtd{$i}[$j] . $comma;
	}
	
	print out_file_graph_max $out_line_max . "\n";
	print out_file_graph_avg $out_line_avg . "\n";
	print out_file_graph_asym $out_line_asym . "\n";
	print out_file_graph_asym_wtd $out_line_asym_wtd . "\n";
}


close(out_file_graph_max);
close(out_file_graph_avg);
close(out_file_graph_asym);
close(out_file_graph_asym_wtd);
close(out_file_atl);
