###########################
#      Match::GNS.pm      #
#      Version 1.01       #
#    (c) Ed Egan 2009     # 
###########################

package GNS;
no warnings 'redefine';
@ISA= qw (Common);
use strict;
use warnings;

#use Math::Complex qw(asin);
use Math::Trig qw(deg2rad pi great_circle_distance asin acos);

sub new {
    my $class = shift;
    my $self = bless {}, $class;
    $self->{GLOBALS}->{ISO9660}=uc(shift);
    $self->{DIR}=shift;
    $self->{GLOBALS}->{SOURCEFILE}="GNS-".$self->{GLOBALS}->{ISO9660}.".txt"; #"GNS-GB.txt";
    $self->Load($self->{DIR});
    return $self;
}

sub Load {
    my $self=shift;
    my $Dir=shift;
    my $reffile=$self->{GLOBALS}->{SOURCEFILE};
    my $reffilewithpath;
    if ($Dir) {$reffilewithpath="$Dir/$reffile";}
    else {$reffilewithpath=$reffile;}
    open (SOURCE,"$reffilewithpath") || die "Can not find the source file - Is it the correct dir?".$!;
    #The Load is robust to changes into the order of the columns and inclusion of new columns, but not to renamings
    undef my @header;
    my $headercol={};
    while (<SOURCE>) {
        chomp $_;
        if (!@header) {
            @header=split("\t",$_);
            for (my $i=0;$i<=$#header;$i++) {$headercol->{$header[$i]}=$i;}
            next;
        }
        my @temp=split("\t",$_);
        #Ignore anything but NT = A,P,L
        if (!($temp[$headercol->{FC}] eq "A" || $temp[$headercol->{FC}] eq "P")) {next;}  #|| $temp[$headercol->{FC}] eq "L" #L is dropped for now
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{RAWDATA}=\@temp;
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{LAT}=$temp[$headercol->{LAT}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{LONG}=$temp[$headercol->{LONG}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{FC}=$temp[$headercol->{FC}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{NT}=$temp[$headercol->{NT}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{DSG}=$temp[$headercol->{DSG}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{SHORT_FORM}=$temp[$headercol->{SHORT_FORM}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME_RAW}=$temp[$headercol->{FULL_NAME}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME_ND_RAW}=$temp[$headercol->{FULL_NAME_ND}];
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME}=$self->CleanString($self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME_RAW});
        $self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME_ND}=$self->CleanString($self->{DATA}->{$temp[$headercol->{UNI}]}->{FULL_NAME_ND_RAW});
    }
    $self->{COUNT_DATA}=scalar(keys(%{$self->{DATA}}));
    close (SOURCE);
    return $self;
}

sub GetCount {
    my $self=shift;
    my $var=shift;
    $var="COUNT_".$var;
    if (defined($self->{$var})) {return $self->{$var};}
    else {return "ERROR";}
}

sub Index {
    my $self=shift;
    foreach my $UNI (keys (%{$self->{DATA}})) {
        my $data=$self->{DATA}->{$UNI};
        if (defined($data->{SHORT_FORM}) && $data->{SHORT_FORM} ne "") {$self->{INDEX_ALL}->{$data->{SHORT_FORM}}->{$UNI}=$data;}
        $self->{INDEX_ALL}->{$data->{FULL_NAME}}->{$UNI}=$data;
        $self->{INDEX_ALL}->{$data->{FULL_NAME_ND}}->{$UNI}=$data;
        if ($data->{FC} eq "A") {
            if (defined($data->{SHORT_FORM}) && $data->{SHORT_FORM} ne "") {$self->{INDEX_A}->{$data->{SHORT_FORM}}->{$UNI}=$data;}
            $self->{INDEX_A}->{$data->{FULL_NAME}}->{$UNI}=$data;
            $self->{INDEX_A}->{$data->{FULL_NAME_ND}}->{$UNI}=$data;
        }
        elsif ($data->{FC} eq "P") {
            if (defined($data->{SHORT_FORM}) && $data->{SHORT_FORM} ne "") {$self->{INDEX_P}->{$data->{SHORT_FORM}}->{$UNI}=$data;}
            $self->{INDEX_P}->{$data->{FULL_NAME}}->{$UNI}=$data;
            $self->{INDEX_P}->{$data->{FULL_NAME_ND}}->{$UNI}=$data;
        }
        #elsif ($data->{FC} eq "L") {
        #    if (defined($data->{SHORT_FORM}) && $data->{SHORT_FORM} ne "") {$self->{INDEX_L}->{$data->{SHORT_FORM}}->{$UNI}=$data;}
        #    $self->{INDEX_L}->{$data->{FULL_NAME}}->{$UNI}=$data;
        #    $self->{INDEX_L}->{$data->{FULL_NAME_ND}}->{$UNI}=$data;
        #}
    }
    my @temp=keys(%{$self->{INDEX_ALL}});
    foreach (@temp) {$self->{NAME}->{$_}=1;}
    return $self;
}

sub GetIndexKeys {
    my $self=shift;
    my $IndexType="INDEX_".shift;
    return (keys(%{$self->{$IndexType}}));
}

sub GetLong {
    my $self=shift;
    my $PlaceName=shift;
    my $Letter=shift;
    my $Long=undef;
    if (!defined($self->{"INDEX_".$Letter}->{$PlaceName})) {
        return undef;
    }    
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) >=1) {
        my $UNI=$self->GetUNI($PlaceName,$Letter);
        $Long=$self->{"INDEX_".$Letter}->{$PlaceName}->{$UNI}->{LONG};
        return $Long;
    }
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) ==0) {
        return undef;
    }
}

sub GetLat {
    my $self=shift;
    my $PlaceName=shift;
    my $Letter=shift;
    my $Lat=undef;
    if (!defined($self->{"INDEX_".$Letter}->{$PlaceName})) {
        return undef;
    }    
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) >=1) {
        my $UNI=$self->GetUNI($PlaceName,$Letter);
        $Lat=$self->{"INDEX_".$Letter}->{$PlaceName}->{$UNI}->{LAT};
        return $Lat;
    }
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) ==0) {
        return undef;
    }
}

sub GetUNI {
    my $self=shift;
    my $PlaceName=shift;
    my $Letter=shift;
    if (!defined($self->{"INDEX_".$Letter}->{$PlaceName})) {return undef;}    
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) >=1) {
        my @UNIs=keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}});
        return $UNIs[0];
    }
    elsif (scalar(keys(%{$self->{"INDEX_".$Letter}->{$PlaceName}})) ==0) {return undef;}
}

sub GetDist {
    my $self=shift;
    my ($Lat1,$Long1,$Lat2,$Long2)=@_;
    if (!defined($Lat1) || !defined($Long1) || !defined($Lat2) || !defined($Long2)) {return 99999999;} #A very big number
    my $Dist=0; 
    my $EarthRadiusKm=6367.45; #Note: This is the Geometric mean of the radius - see http://en.wikipedia.org/wiki/Haversine_formula
    my $pi=3.1415926535897932384626433832795; #From http://en.wikipedia.org/wiki/Pi
    my $RadsInADegree=$pi/180;
    my $HalfDiffLong=(($Long1*$RadsInADegree)-($Long2*$RadsInADegree))/2;
    my $HalfDiffLat=(($Lat1*$RadsInADegree)-($Lat2*$RadsInADegree))/2;
    my $RadLat1=($Lat1*$RadsInADegree); my $RadLat2=($Lat2*$RadsInADegree);
    my $Part=sin($HalfDiffLat)**2 + cos($RadLat1)*cos($RadLat2)*sin($HalfDiffLong)**2;
    my $Haversine=2*asin(sqrt($Part));
    $Dist=$Haversine*$EarthRadiusKm;
    return $Dist; 
}


1;

