package Committer;

use strict;

use File::Spec::Functions;
use File::Basename;

use File::Slurp;
use XML::Mini::Document;

my %dataMapping = (
    asfid => '_asfid',
    name => { first => '_fname', last => '_lname' },
    email => { address => '_email' },
    latitude => { degrees => '_latdeg' , minutes => '_latmin', seconds => '_latsec' },
    longitude => { degrees => '_longdeg' , minutes => '_longmin', seconds => '_longsec' },
    aim => '_aim',
    irc => { identity => { channel => '_ircChannel', nickname => '_ircNickname' } },
    homepage => '_homepage',
    blog => '_blog',
    pgp => '_pgp',
);

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

    my $class = shift;
    my %args = @_;
    foreach my $k (keys(%args)) {
        $self->{$k} = $args{$k};
    }
    $self->{xml} = XML::Mini::Document->new();
    $self->readFile() if $self->{fileName};


    return $self;
}

sub dumpData
{
    my $self = shift;
    print "Data:\n";
    foreach my $k (sort(keys(%{$self}))) {
        printf("\t%-30s %s\n", $k, $self->{$k});
        next unless ref($self->{$k}) eq 'HASH';
        foreach my $hk (sort(keys(%{$self->{$k}}))) {
            printf("\t%-30s :: %s\n", '', $hk);
        }
    }
}

sub findElement # private
{
    my ($root, $elem) = @_;
    return $root->getAllChildren($elem);
}

sub readFile
{
    my ($self, $myfn) = @_;

    $self->resetData() if ($self->{fileName} && $myfn && $self->{fileName} ne $myfn);
    $self->{fileName} = $myfn if $myfn;
    return 1 unless $self->{xml}->fromFile($self->{fileName});
    my $root = $self->{xml}->getElementByPath('committer');

    foreach my $k (sort(keys(%dataMapping))) {

        my $elmp = findElement($root, $k);
        if (ref($dataMapping{$k}) eq 'HASH') {
            my %arr = %{$dataMapping{$k}};
            foreach my $kk (sort(keys(%arr))) {
                my $elmpp = findElement($elmp->[0], $kk);
                if ($#$elmpp == 0) {
                    $self->{$arr{$kk}} = $elmpp->[0]->getValue();
                } else {
                    for my $j (0 .. $#$elmpp) {
                        $self->{$arr{$kk}}{$elmpp->[$j]->getValue()}++;
                    }
                }
            }
        } else {
            $self->{$dataMapping{$k}} = $elmp->[0]->getValue();
        }
    }
#    $self->dumpData();       
}

sub makeElement  # private
{
    my ($root, $elem) = @_;
    my $retElem;
#    print "makeElement($root, $elem)\n";
    if ($elem =~ /\//) {
        my @bits = split('/', $elem);
        $retElem = makeElement($root, $bits[0]);
        for my $i (1 .. $#bits) {
            $retElem = makeElement($retElem, $bits[$i]);
        }
    } else {
        $retElem = $root->createChild($elem);
    }
    return $retElem;
}

sub addElement # private
{
    my ($root, $key, $value) = @_;
#    print "addElement($root, $key, $value)\n";
    my $node = makeElement($root, $key);
    $node->text($value);
}

sub writeFile
{
    my ($self, $fn) = @_;
    $self->{xml} = XML::Mini::Document->new();
    $self->{fileName} = $fn;
    my $xmlRoot = $self->{xml}->getRoot();
    my $docRoot = $xmlRoot->createChild('committer');

    foreach my $k (sort(keys(%dataMapping))) {
        if (ref($dataMapping{$k}) eq 'HASH') {
            my %arr = %{$dataMapping{$k}};
            my $root = makeElement($docRoot, $k);
            foreach my $kk (sort(keys(%arr))) {
                next unless defined($self->{$arr{$kk}});
                if (ref($self->{$arr{$kk}}) eq 'HASH') {
                    my %multi = %{$self->{$arr{$kk}}};
                    foreach my $val (sort(keys(%multi))) {
                        addElement($root, $kk, $val);
                    }
                } else {
                    next unless defined($self->{$arr{$kk}});
                    addElement($root, $kk, $self->{$arr{$kk}});
                }
            }
        } else {
            next unless defined($self->{$dataMapping{$k}});
            addElement($docRoot, $k, $self->{$dataMapping{$k}});
        }
    }
    write_file($fn, "<?xml version=\"1.0\"?>\n".
               $self->{xml}->toString($XML::Mini::NoWhiteSpaces));
print "Content-type: text/html\n\n<pre>".$self->{xml}->toString()."</pre>\n";
}

sub setField  #private
{
    my ($self, $q, $tag) = @_;
    my $n = 1;
    # Some fields are numbered, so test initially for plain old field,
    # then test for numbered fields as well. We append the number when we
    # create the hash entry.
    if (my $val = $q->param($tag)) {
        $self->{$tag} = $val;
        return;
    }
print "Content-type: text/html\n\n";
    while (my $val = $q->param($tag.$n)) {
print "<pre>found $tag$n</pre>\n";
        $self->{$tag}{$val}++;
        $n++;
    }    
}

sub parseHash
{
    my ($self, $arrp, $q) = @_;
    foreach my $k (keys(%$arrp)) {
        if (ref($arrp->{$k}) eq 'HASH') {
            $self->parseHash(\%{$arrp->{$k}}, $q);
        } else {
            $self->setField($q, $arrp->{$k});
        }
    }
}

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

    $self->resetData() if ($self->{_asfid} && $self->{_asfid} ne $userid);

    foreach my $k (keys(%dataMapping)) {
        if (ref($dataMapping{$k}) eq 'HASH') {
            $self->parseHash(\%{$dataMapping{$k}}, $q);
        } else {
            $self->setField($q, $dataMapping{$k});
        }
    }

    $self->{_asfid} = $userid;
    $self->{_name} = $self->{_fname}.' '.$self->{_lname};

#    $self->dumpData();
}

sub resetData
{
    my $self = shift;
print "ref(self) = ".ref($self)."\n";
    foreach my $k (keys(%dataMapping)) {
        delete $self->{$dataMapping{$k}};
    }
}

sub applyTemplate
{
    my ($self, $tempfn) = @_;
    return '' unless (-e $self->{fileName} && -e $tempfn);
    my $txt = `xsltproc $tempfn $self->{fileName}`;
    return '' unless $? == 0;
    chomp($txt);
    return $txt;
}

1;
