#!/usr/bin/perl

# Copyright (C) 2013, 2014, 2015 Thorsten Kukuk
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# in Version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301, USA.

=head1 NAME

geo-challenges - Check which challenges we fullfill

=head1 SYNOPSIS

geo-challenges [options] [<gpx-file> ...]

=head1 DESCRIPTION

geo-challenges reads the given GPX file (which should contain all
founds of the user).

=head1 OPTIONS

  --lab-caches        Provide the number of Lab caches already found
  -q|--quiet            Be quiet and only print error messages and results
  --version             Print version number and exit
  --man                 Display manual page
  --usage               Display usage
  -h|-?|--help          Help

=head1 COPYRIGHT

Copyright (c) 2013 by Thorsten Kukuk.  All rights reserved.

This package is free software; you can redistribute it and/or modify
it under the GPL version <http://gnu.org/licenses/gpl2.html>.
There is NO WARRANTY, to the extent permitted by law.

=cut

use strict;
use warnings;
use XML::Twig;
use Pod::Usage;
use GEO::Coords;
use GEO::GCVote;
use Config::IniFiles;
use Term::ANSIColor;

my $Version = '(geo-tools) 1.23';

#
# process command line arguments
#
use Getopt::Long;
my $help = 0;
my $man = 0;
my $version = 0;
my $dump_config = 0;
my $homedir = '';
my $founds = 0;
my $labcaches = 0;

if ($ENV{HOME}) {
  $homedir = $ENV{HOME};
} elsif ($ENV{HOMEDRIVE} && $ENV{HOMEPATH}) {
  $homedir = $ENV{HOMEDRIVE}."/".$ENV{HOMEPATH};
}
my $cfgname = $homedir."/.geo-tools.cfg";

if (!-r $cfgname) {
  my $cfg = new Config::IniFiles(-default => "Global",
				 -nocase => 1);
  $cfg->AddSection("geo-challenges");
  $cfg->newval("geo-challenges", "convert2utf8", 1);
  $cfg->WriteConfig($cfgname);
}

my $cfg = new Config::IniFiles(-file => $cfgname,
			       -default => "Global");
if (!$cfg->SectionExists("geo-challenges")) {
  $cfg->AddSection("geo-challenges");
  $cfg->newval("geo-challenges", "quiet", 0);
  $cfg->WriteConfig($cfgname);
}
my $quiet = $cfg->val("geo-challenges", "quiet", 0);

GetOptions('dump-config' => \$dump_config,
	   'l|lab-caches=s' => \$labcaches,
           'q:1' => \$quiet,
           'quiet!' => \$quiet,
	   'man' => \$man,
	   'version' => \$version,
	   'help|h|?' => \$help) or pod2usage(2);
pod2usage(0) if $help;
pod2usage(-exitstatus => 0, -verbose => 2) if $man;

if ($version) {
  print "geo-challenges $Version\n";
  exit 0;
}

if ($dump_config) {
  WriteConfig();
  exit 0;
}

my %Caches;
my @InputFiles;

if ($#ARGV >= 0) {
   @InputFiles = (@ARGV);
}
else {
  print "No input files given\n";
  pod2usage(2);
  exit(1);
}

foreach my $InputFileName (@InputFiles) {
  my $Parser =
    new XML::Twig(twig_handlers=>{'gpx/wpt' => \&GetCache,
				  'gpx/wpt/groundspeak:cache/groundspeak:logs/groundspeak:log' => \&GetLog},

		  keep_encoding => 1);

  print "Processing input GPX file $InputFileName:\n" unless $quiet;
  $Parser->parsefile($InputFileName);
}

# Add Lab caches, if provided on commandline
if ($labcaches > 0) {
    $founds+=$labcaches;
    $Caches{"Labs"}->{name} = "Lab Cache";
    $Caches{"Labs"}->{desc} = "Lab Cache";
    $Caches{"Labs"}->{lat} = 0;
    $Caches{"Labs"}->{lon} = 0;
    $Caches{"Labs"}->{type} = "Lab Cache";
    $Caches{"Labs"}->{container} = "None";
    $Caches{"Labs"}->{difficulty} = 0;
    $Caches{"Labs"}->{terrain} = 0;
    $Caches{"Labs"}->{NC} = 0;
    $Caches{"Labs"}->{founds} = $labcaches;
}


print "Founds: $founds                                   \n\n";
Master_of_Mystery();
print "\n\n" unless $quiet;
Icons_2_hoch_8();
print "\n\n" unless $quiet;
Icons_2_hoch_9();
print "\n\n" unless $quiet;
ExponentialChallengeNuernberg();
print "\n\n" unless $quiet;
MicroMonster();
print "\n\n" unless $quiet;
SchoenerCachenInFranken();
print "\n\n" unless $quiet;
NCChallenge();

print "\nDone!\n" unless $quiet;
exit;

sub Icons_2_hoch_8
{
  my %TYPES;
  my $OK=1;

  print "GC4XZPV - Challenge-Cache: Icons 2 hoch 8";
  print "\n" unless $quiet;

  while (my ($k,$v) = each %Caches) {
    my $type = $v->{type};
    if (defined $TYPES{$type}){
      $TYPES{$type}+=$v->{founds};
    } else {
      $TYPES{$type}=$v->{founds};
    }
  }

  my %table = ('0' => 256,
	       '1' => 128,
	       '2' => 64,
	       '3' => 32,
	       '4' => 16,
	       '5' => 8,
	       '6' => 4,
	       '7' => 2);

  my @ORDER = sort { $TYPES{$b} <=> $TYPES{$a} } keys %TYPES;
  my $count = 0;
  print "Cachetype         / Found (Missing)\n" unless $quiet;
  foreach my $k (@ORDER) {
    if ($count < 8) {
      if ($TYPES{$k} < $table{$count}) {
	printf "%17s     %s (%s)\n",
	  $k, colored($TYPES{$k}, 'red'), $table{$count}-$TYPES{$k}
	    unless $quiet;
	$OK=0;
      } else {
	printf "%17s     %s\n", $k, colored($TYPES{$k}, 'green') unless $quiet;
      }
    }
    $count++;
  }
  if (!$quiet) {
    while ($count < 8) {
      print "                      -- ($table{$count})\n";
      $count++;
    }
  } else {
    check_ok ($OK);
  }
}

sub Icons_2_hoch_9
{
  my %TYPES;
  my $OK=1;

  print "GC4058A - Challenge-Cache: Icons 2 hoch 9";
  print "\n" unless $quiet;

  while (my ($k,$v) = each %Caches) {
    my $type = $v->{type};
    if (defined $TYPES{$type}){
      $TYPES{$type}+=$v->{founds};
    } else {
      $TYPES{$type}=$v->{founds};
    }
  }

  my %table = ('0' => 512,
	       '1' => 256,
	       '2' => 128,
	       '3' => 64,
	       '4' => 32,
	       '5' => 16,
	       '6' => 8,
	       '7' => 4,
	       '8' => 2);

  my @ORDER = sort { $TYPES{$b} <=> $TYPES{$a} } keys %TYPES;
  my $count = 0;
  print "Cachetype         / Found (Missing)\n" unless $quiet;
  foreach my $k (@ORDER) {
    if (defined $table{$count} && $TYPES{$k} < $table{$count}) {
      printf "%17s     %s (%s)\n",
	$k, colored($TYPES{$k}, 'red'), $table{$count}-$TYPES{$k}
	  unless $quiet;
      $OK=0;
    } else {
      printf "%17s     %s\n", $k, colored($TYPES{$k}, 'green') unless $quiet;
    }
    $count++;
  }
  if (!$quiet) {
    while ($count < 9) {
      print "                      -- ($table{$count})\n" unless $quiet;
      $count++;
    }
  } else {
    check_ok ($OK);
  }
}


sub ExponentialChallengeNuernberg
{
  my %TYPES;
  my $OK=1;

  print "GC4XC8A - Exponential-Challenge Nürnberg";
  print "\n" unless $quiet;

  while (my ($k,$v) = each %Caches) {
    my $type = $v->{type};
    if (defined $TYPES{$type}){
      $TYPES{$type}+=$v->{founds};
    } else {
      $TYPES{$type}=$v->{founds};
    }
  }

  my %table = ('0' => 1024,
               '1' => 512,
	       '2' => 256,
	       '3' => 128,
	       '4' => 64,
	       '5' => 32,
	       '6' => 16,
	       '7' => 8,
	       '8' => 4,
	       '9' => 2,
	       '10' => 1);

  my @ORDER = sort { $TYPES{$b} <=> $TYPES{$a} } keys %TYPES;
  my $count = 0;
  print "Cachetype         / Found (Missing)\n" unless $quiet;
  foreach my $k (@ORDER) {
    if ($TYPES{$k} < $table{$count}) {
      printf "%17s     %s (%s)\n",
	$k, colored($TYPES{$k}, 'red'), $table{$count}-$TYPES{$k}
	  unless $quiet;
      $OK=0;
    } else {
      printf "%17s     %s\n", $k, colored($TYPES{$k}, 'green') unless $quiet;
    }
    $count++;
  }
  if (!$quiet) {
    while ($count < 11) {
      printf "                      %s ($table{$count})\n", colored("--", 'red') unless $quiet;
      $count++;
    }
  } else {
    check_ok ($OK);
  }
}


sub Master_of_Mystery
{
  my %table;
  my %result = ('1'   => 1000,
		'1.5' => 750,
		'2'   => 750,
		'2.5' => 500,
		'3'   => 250,
		'3.5' => 250,
		'4'   => 100,
		'4.5' => 50,
		'5'   => 25);
  my $OK=1;

  print "GC4AHXP - Master of Mystery";
  print "\n" unless $quiet;

  # Init table
  for my $d (1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5) {
    $table{$d} = 0;
  }

  while (my ($k,$v) = each %Caches) {
    if ($v->{type} eq "Unknown Cache" || $v->{type} eq "Mystery Cache") {
      for my $d (1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5) {
	$table{$d}+=1 if ($v->{difficulty} >= $d);
      }
    }
  }

  print "Difficulty / Founds / Needed / Missing\n" unless $quiet;
  for my $d (1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5) {
    if ($table{$d} >= $result{$d}) {
      printf "%5s%22s%9s\n", $d, colored ($table{$d}, 'green'), $result{$d} unless $quiet;
    } else {
      printf "%5s%22s%9s%18s\n", $d, colored ($table{$d}, 'red'),
	$result{$d},  colored ($result{$d}-$table{$d}, 'red') unless $quiet;
      $OK=0;
    }
  }
  if ($quiet) {
    print "\t\t";
    check_ok ($OK);
  }
}

sub MicroMonster
{
  my $micro = 0;
  my $procent;

  print "GC4CENF - MICROMONSTER";
  print "\n" unless $quiet;

  while (my ($k,$v) = each %Caches) {
    $micro++ if ($v->{container} eq "Micro");
  }
  $procent = $micro/$founds*100;
  if ($quiet) {
    print "\t\t\t";
    check_ok ($procent <= 33.33);
  } else {
    if ($procent <= 33.33) {
      printf "Prozent Micros: %s%%\n", colored ($procent, 'green');
    } else {
      printf "Prozent Micros: %s%% (needed: <=33.33%%)\n", colored ($procent, 'red');
    }
  }
}

sub SchoenerCachenInFranken
{
  my $OK=1;
  my $difficulty=0;
  my $terrain=0;

  print "GC501K0 - Schöner Cachen in Franken";
  print "\n" unless $quiet;


  if ($founds >=801) {
    printf "Funde: %s >= 801\n", colored ($founds, 'green') unless $quiet;
  } else {
    printf "Funde: %s >= 801\n", colored ($founds, 'red') unless $quiet;
    $OK=0;
  }

  while (my ($k,$v) = each %Caches) {
    $difficulty+=$v->{difficulty};
    $terrain+=$v->{terrain};
  }
  $difficulty=$difficulty/$founds;
  $terrain=$terrain/$founds;

  if ($difficulty+$terrain < 4.30) {
    printf "D- + T-Wertung >= 4.30: %s\n",
      colored ($difficulty+$terrain, 'red') unless $quiet;
    $OK=0;
  } else {
    printf "D- + T-Wertung >= 4.30: %s\n",
      colored ($difficulty+$terrain, 'green') unless $quiet;
  }

  if ($quiet) {
    print "\t";
    check_ok ($OK);
  }
}

sub NCChallenge
{
    my $total = 0;
    my $d4 = 0;
    my $t3 = 0;

    print "GC5BMJE - Wir lieben Nachtcaches";
    print "\n" unless $quiet;

    while (my ($k,$v) = each %Caches) {
	if ($v->{NC}) {
	    $total++;
	    $d4++ if ($v->{difficulty} >= 4);
	    $t3++ if ($v->{terrain} >= 3);
	}
    }
    if ($quiet) {
	print "\t";
	check_ok ($total >= 34 && $d4 >= 3 && $t3 >= 4);
    } else {
	if ($total >= 34 && $d4 >= 3 && $t3 >= 4) {
	    printf "Anzahl NCs: %s (D>=4: $d4, T>=3: $t3)\n", colored ($total, 'green');
	} else {
	    printf "Anzahl NCs: %s (needed: >=34)\n", colored ($total, 'red');
	}
	while (my ($k,$v) = each %Caches) {
	    if ($v->{NC}) {
		print "$k: $v->{name} ($v->{type}/D$v->{difficulty}/T$v->{terrain})\n";
	    }
	}
    }
}

sub WriteConfig
{
  $cfg->newval("geo-challenges", "quiet", $quiet);
  $cfg->WriteConfig($cfgname);
}

sub check_ok
{
  my $OK = $_[0];

  if ($OK) {
    printf "\t\t%s\n", colored("OK", 'green');
  } else {
    printf "\t\t%s\n", colored("not ready", 'red');
  }
}

sub GetCache
{
  my ($t, $wpt) = @_;
  my $ID = $wpt->first_child_text('name');
  my $cache = $wpt->first_child('groundspeak:cache');

  return unless $cache;

  print "Parsing ID: $ID       \r" unless $quiet;

  my $name = $cache->first_child_text('groundspeak:name');
  my $desc = $wpt->first_child_text('desc');
  my $lat = $wpt->{'att'}->{'lat'};
  my $lon = $wpt->{'att'}->{'lon'};
  my $type = $cache->first_child_text('groundspeak:type');
  my $container = $cache->first_child_text('groundspeak:container');
  my $difficulty = $cache->first_child_text('groundspeak:difficulty');
  my $terrain = $cache->first_child_text('groundspeak:terrain');

  my $is_NC=0;

  if ($type ne "Traditional Cache" &&
      $type !~ m/Event/ &&
      $name !~ m/Bonus/i &&
      $ID ne "GCWRXW" &&
      $ID ne "GC3NWD0" &&
      $ID ne "GC1PP3N" &&
      $ID ne "GC1REGZ" &&
      $ID ne "GC3TD6B" &&
      $ID ne "GC3P8T0") {

    # Check for names
    if ($name =~ m/NC/ ||
	$name =~ m/Nachtcache/i ||
	$desc =~ m/Nachtcache/i) {
      $is_NC=1;
    }

    # Check attribute for NC
    my $attrs = $cache->first_child('groundspeak:attributes');
    if ($attrs) {
      my $other=0;
      foreach my $attr ($attrs->children('groundspeak:attribute')) {
	if ($attr->{'att'}->{'inc'}) {
	  $is_NC=1 if ($attr->text eq "Night Cache");
	  $other+=1 if ($attr->text eq "Flashlight required");
	  $other+=1 if ($attr->text eq "Recommended at night");
	} else {
	  if ($attr->text eq "Night Cache") {
	    $is_NC=0;
	    $other=-2;
	  }
	}
      }
      $is_NC=1 if ($other == 2);
    }
  }

  #
  # Create a list of known Geocaches
  #
  $Caches{$ID}->{name} = $name;
  $Caches{$ID}->{desc} = $desc;
  $Caches{$ID}->{lat} = $lat;
  $Caches{$ID}->{lon} = $lon;
  $Caches{$ID}->{type} = $type;
  $Caches{$ID}->{container} = $container;
  $Caches{$ID}->{difficulty} = $difficulty;
  $Caches{$ID}->{terrain} = $terrain;
  $Caches{$ID}->{NC} = $is_NC;
}

sub GetLog
{
  my ($t, $log) = @_;
  my $type = $log->first_child_text('groundspeak:type');

  my $parent = $log->parent;
  $parent = $parent->parent;
  $parent = $parent->parent;
  my $ID = $parent->first_child_text('name');

  if ($type eq 'Found it' ||
      $type eq 'Attended' ||
      $type eq 'Webcam Photo Taken') {
    $founds++;
    $Caches{$ID}->{founds}++;
  }
}
