package vCard;

use File::Spec::Functions;
use File::Slurp;
use Date::Manip;

# This package contains a class to manage vCards as objects.
# The aim is to have a lot of useful and non-specific code in here!

sub new {
    my $self = {};
    bless($self);

    my $class = shift;
    my %args = @_;
    foreach my $k (keys(%args)) {
        $self->{$k} = $args{$k};
    }

    return $self;
}

my $logoURL='http://www.apache.org/images/asf_logo_simple.png';
my @vCardInfo = (
    [ 'FN', '_name' ],
    [ 'N', '_lname', '_fname' ],
    [ 'EMAIL;TYPE=INTERNET,PREF', '_email' ],
    [ 'PHOTO;VALUE=uri', '_photourl' ],
    [ 'GEO', '_lat', '_long' ],
    [ 'NOTE', 'IRC:_irc', 'Jabber:_jabber', 'AIM:_aim', 'PGP:_pgp' ],
);
my @formFields = (
    [ 'First Name', '_fname', 30, '' ],
    [ 'Last Name', '_lname', 30, '' ],
    [ 'Preferred Email', '_email', 60, '' ],
    [ 'IRC Nickname', '_irc', 20, 'IRC' ],
    [ 'Jabber ID', '_jabber', 20, 'Jabber' ],
    [ 'AIM ID', '_aim', 20, 'AIM' ],
    [ 'PGP Fingerprint', '_pgp', 60, 'PGP' ],
    [ 'Photo URL', '_photourl', 60, ],
    [ '', '_lat' ],
    [ '', '_latdeg' ],
    [ '', '_latmin' ],
    [ '', '_latsec' ],
    [ '', '_long' ],
    [ '',  '_longdeg' ],
    [ '', '_longmin' ],
    [ '', '_longsec' ],
);

sub resetData
{
    my ($self) = shift;

    for my $i (0 .. $#formFields) {
        delete $self->{$formFields[$i]};
    }
    delete $self->{fileName};
}

sub parseFormData
{
    my ($self, $userid, $q) = @_;

    $self->resetData();
    for my $i (0 .. $#formFields) {
        $self->{$formFields[$i][1]} = $q->param($formFields[$i][1]) 
            if $q->param($formFields[$i][1]);
    }        

    $self->{_lat} .= sprintf("%.06f", $q->param('_latdeg') + ($q->param('_latmin') / 60) +
                                     ($q->param('_latsec') / 3600));
    $self->{_long} .= sprintf("%.06f",$q->param('_longdeg') + ($q->param('_longmin') / 60) +
                                      ($q->param('_longsec') / 3600));
    $self->{_name} = $self->{_fname}.' '.$self->{_lname};
    $self->{_user} = $userid;
}

sub findUser
{
    my ($self, $srcUser) = @_;
    my $fn = catfile($self->{Store}, $srcUser.'.vcf');
    return 1 if -e $fn;
    return 0;
}

sub dumpValues
{
    my $self = shift;
    print "<ul>\n";
    foreach my $k (sort(keys(%{$self}))) {
        print "<li>$k: $self->{$k}</li>\n";
    }
    print "</ul>\n";
}

# Given a filename to parse, parse it and fill out the details
# for this object.
sub readParseFile
{
    my ($self, $userid) = @_;
    $self->resetData();

    $self->{_user} = $userid;
    $self->{fileName} = catfile($self->{Store}, $self->{_user}.'.vcf');

    return unless -e $self->{fileName};
    $self->svnUpdate();

    my @lines = read_file($self->{fileName});
    for my $i (0 .. $#lines) {
        for my $j (0 .. $#vCardInfo) {
            next unless $lines[$i] =~ s/^$vCardInfo[$j][0]://;
            chomp($lines[$i]);
            my @arr = @{$vCardInfo[$j]};
            if ($#arr > 0) {
                my @elms = split(/;/, $lines[$i]);
                # If we don't have "optional" items, ie ones with TAG:value, then
                # we simply assign in logical order
                if ($arr[1] =~ /:/) {
                    $self->scanArray(\@arr, \@elms);
                } else {
                    for my $k (1 .. $#arr) {
                        $self->{$arr[$k]} = $elms[$k - 1];
                    }
                }
            } else {
                $self->{$arr[1]} = $lines[$i];
            }

        }
    }
    if ($self->{_lat}) {
        $self->{_latsign} = ($self->{_lat} < 0 ? '-' : '');
        my $num = int($self->{_lat} / (1 / 3600));
        $self->{_latdeg} = int($num / 3600);
        $self->{_latmin} = int(($num - ($self->{_latdeg} * 3600)) / 60);
        $self->{_latsec} = $num - ($self->{_latdeg} * 3600) - ($self->{_latmin} * 60);
    }
    if ($self->{_long}) {
        $self->{_longsign} = ($self->{_long} < 0 ? '-' : '');
        my $num = int(abs($self->{_long}) / (1 / 3600));
        $self->{_longdeg} = int($num / 3600);
        $self->{_longmin} = int(($num - ($self->{_longdeg} * 3600)) / 60);
        $self->{_longsec} = $num - ($self->{_longdeg} * 3600) - ($self->{_longmin} * 60);
    }

#    $self->dumpValues();
}

sub scanArray
{
    my ($self, $arrp, $elmsp) = @_;
    for my $i (0 .. $#$elmsp) {
        for my $j (1 .. $#$arrp) {
            my ($tag, $val) = split(/:/, $arrp->[$j]);
            next unless $elmsp->[$i] =~ /^$tag:/;
            $self->{$val} = $elmsp->[$i];
            last;
        }
    }
}

sub writeForm
{
    my $self = shift;

    for my $i (0 .. $#formFields) {
        next unless $formFields[$i][0];

        my $val = $self->{$formFields[$i][1]};
        $val =~ s/$formFields[$i][3]://g if $formFields[$i][3];

        print <<EOF;
            <tr>
              <td>$formFields[$i][0]</td>
              <td>
                <input type="text" name="$formFields[$i][1]" size="$formFields[$i][2]"
                       value="$val"/>
              </td>
            </tr>
EOF
    }
    my @geoData = ( 
        [ 'Latitude', '_lat', 'N', 'S', '_latsign' ],
        [ 'Longitude', '_long', 'E', 'W', '_longsign' ],
    );
    my @geoSelector;
    for my $i (0 .. $#geoData) {
        $geoSelector[$i] = "$geoData[$i][0]: <select name=\"$geoData[$i][1]\">";
        $geoSelector[$i] .= sprintf("<option value=\"\" %s>%s</option>",
                                  ($geoData[$i][2] eq $self->{$geoData[$i][4]} ? "selected" : ""),
                                  $geoData[$i][2]);
        $geoSelector[$i] .= sprintf("<option value=\"-\" %s>%s</option>",
                                  ($geoData[$i][3] eq $self->{$geoData[$i][4]} ? "selected" : ""),
                                  $geoData[$i][3]);
        $geoSelector[$i] .= "</option>";
    }

    print <<EOF;
            <tr>
              <td>Geo Data</td>
              <td>$geoSelector[0]
                            <input type="text" name="_latdeg" size="3" maxchars="2" 
                                   value="$self->{_latdeg}">
                            <sup>o</sup>
                            <input type="text" name="_latmin" size="3" maxchars="2"
                                   value="$self->{_latmin}">' 
                            <input type="text" name="_latsec" size="3" maxchars="2"
                                   value="$self->{_latsec}">" 
                   </br>
                   $geoSelector[1]
                            <input type="text" name="_longdeg" size="3" maxchars="2"
                                   value="$self->{_longdeg}">
                            <sup>o</sup>
                            <input type="text" name="_longmin" size="3" maxchars="2"
                                   value="$self->{_longmin}">' 
                            <input type="text" name="_longsec" size="3" maxchars="2"
                                   value="$self->{_longsec}">" 
              </td>
            </tr>
EOF

}

sub writeHTMLInfo
{
    my $self = shift;

    for my $i (0 .. $#formFields) {
        next unless $formFields[$i][0];

        my $val = $self->{$formFields[$i][1]};
        $val =~ s/$formFields[$i][3]://g if $formFields[$i][3];

        print <<EOF;
            <tr>
              <td>$formFields[$i][0]</td>
              <td>
                $val
              </td>
            </tr>
EOF
    }
    
    my $lat = ($self->{_lat} eq '-' ? 'S' : 'N');
    my $long = ($self->{_long} eq '-' ? 'E' : 'W');

    print <<EOF;
            <tr>
              <td>Geo Data</td>
              <td>$lat $self->{_latdeg}<sup>o</sup>
                       $self->{_latmin}'
                       $self->{_latsec}"
                  $long $self->{_longdeg}<sup>o</sup>
                        $self->{_longmin}' 
                        $self->{_longsec}"
              </td>
            </tr>
EOF

}

sub makeCard
{
    my $self = shift;
    return unless $self->{_user};
    my $revdt = UnixDate("today", "%Y-%M-%dT%TZ");
    my $txt = <<END_OF_HEADER;
BEGIN:VCARD
VERSION:3.0
CLASS:PUBLIC
EMAIL;TYPE=INTERNET:$ENV{'SSL_CLIENT_S_DN_Email'}
ORG:The Apache Software Foundation
ROLE:Committer
LOGO;VALUE=uri:$logoURL
END_OF_HEADER

    for my $i (0 .. $#vCardInfo) {
        my @array = @{$vCardInfo[$i]};
        my $linetxt = $array[0].':';
        my $added = 0;
        for my $j (1 .. $#array) {
            if ($array[$j] =~ /(.*):(.*)/ && $self->{$2}) {
                $linetxt .= ";" unless $j == 1;
                $linetxt .= "$1:$self->{$2}";
                $added++;
            } elsif ($self->{$array[$j]}) {
                $linetxt .= ";" unless $j == 1;
                $linetxt .= $self->{$array[$j]};
                $added++;
            }
        }
        $txt .= "$linetxt\n" if $added;
    }

    $txt .= <<END_OF_FOOTER;
REV:$revdt
END:VCARD
END_OF_FOOTER

    return $txt;
}

sub writeFile
{
    my $self = shift;
    return 1 unless ($self->{_user} && $self->{Store});
    $self->{fileName} = catfile($self->{Store}, $self->{_user}.'.vcf');
    $self->{new} = 1 unless -e $self->{fileName};
    my $txt = $self->makeCard();
    write_file($self->{fileName}, $txt);
    print "<p>Current vCard for $self->{_name} ($self->{_user}.vcf)</p>\n<pre>$txt</pre>\n";
}

sub svnUpdate
{
    my $self = shift;
    return unless ($self->{fileName} && -e $self->{fileName});
    chdir($self->{Store}) || print "Unable to chdir($self->{Store}), $!";
    `svn update $self->{fileName}`;
}

sub svnCommit
{
    my $self = shift;
    chdir($self->{Store}) || print "Unable to chdir($self->{Store}), $!";
    my $msg = "Update committer details for $self->{_user}";
    if ($self->{new}) {
        `svn add $self->{fileName}`;
        return unless $? == 0;
    }
    `svn commit -m '$msg' $self->{fileName}`;
}

sub printLink
{
    my $self = shift;
    print <<EOF;
<p>
  <a href="/cgi-bin/commaction.cgi?action=info&userid=$self->{_user}">
    $self->{_fname} $self->{_lname}
  </a>&nbsp;
  <a href="/cgi-bin/commaction.cgi?action=vcard&userid=$self->{_user}">
    vCard
  </a>
</p>
EOF
}

1;
