#!/usr/bin/env perl

use strict;

use Cwd qw( realpath );
use FindBin;
use File::Basename;
use File::Copy;
use File::Spec::Functions;
use Data::Dumper;

BEGIN {
    # Add the standard places we expect to find the support libs. 
    # We add these regardless as having them on the search path won't do any harm
    # and new directories are pushed on the front, so if there is an environment
    # setting for us to use it'll end up prior to our guesses.
    # NB at present we don't have any *standard* locations,
    #    so this should be adjusted when we do have!
    my @stdlibdirs = ( '../support', '/usr/local/asf_support' );
    foreach my $d (@stdlibdirs) {
        my $dir = $d;
        $dir = catdir(realpath(dirname($0)), $d) if $d =~ /^\./;
        next unless -d $dir;
        $dir = catdir(realpath($dir), 'perl');
        next unless -d $dir;
        unshift(@INC, $dir);
    }
    unshift(@INC, catdir($ENV{ASF_SITE_LIBS}, 'perl')) if $ENV{ASF_SITE_LIBS};
}

# These modules are in the support/perl repository.
use httpGet;
use xsltProc;
use File::Slurp;
use Date::Manip;
use XML::dspp;

my %conf;
$conf{directories}{base} = realpath(catdir($FindBin::Bin, '..'));

my $cfgFn = $#ARGV == -1 ? 'iis.cfg' : $ARGV[0];
$cfgFn = catfile($conf{directories}{base}, 'iis.cfg') unless osUtils::isAbsolute($cfgFn);
print "Configuration file: $cfgFn\n";

# Read the config file, die if we can't...
readConfig($cfgFn);

processSection('committer');
processSection('member');

sub readConfig
{
    my $fn = shift;
    my $xml2 = XML::dspp->new();
    if (! $xml2->parseFile($fn)) {
        print "Error reading '$fn', no configuration!\n";
        die;
    }
    my $root = $xml2->getRoot();
    if (! $root || $root->Name() ne 'iisConfig') {
        print "'$fn' doesn't appear to be a IIS configuration file\n";
        exit;
    }

    my @branches = $root->getChildren();
    foreach my $b (@branches) {
        processAll($b, $b->Name(), $b->Attribute('prefix'));
    }
    foreach my $b (@branches) {
        postProcessConfig($b->Name());
        postProcessConfig($b->Name());
    }
    foreach my $d (sort(keys(%{$conf{directories}}))) {
        checkDirectoryRecurse($conf{directories}{$d});
    }
}

sub processAll
{
    my ($elm, $branch, $prefix) = @_;
    my @ch = $elm->getChildren();
    foreach my $e (@ch) {
        my $n = $e->Name();
        if ($n eq 'action') {
            push(@{$conf{$branch}{actions}}, $e);
        } elsif ($n eq 'index') {
            push(@{$conf{$branch}{indexes}}, $e);
        } elsif ($n eq 'multiIndex') {
            push(@{$conf{$branch}{multis}}, $e);
        } else {
            my $c = $e->Data();
            $c = $n unless $c;
            $c = $prefix.$c if $prefix;
            $conf{$branch}{$n} = $c;
        }
    }
}

sub postProcessConfig
{
    my $branch = shift;
    foreach my $e (keys(%{$conf{$branch}})) {
        $conf{$branch}{$e} = parseSettingString($conf{$branch}{$e}, $branch);
    }
}

sub parseSettingString
{
    my ($str, $branch) = @_;
    return unless $str;
    $branch = '' unless $branch;
    for my $i (0 .. 1) {
        return $str unless $str =~ /\{/;
        my @subs = $str =~ /\{([a-zA-Z:]*)\}/g;
        foreach my $s (@subs) {
            my $v = getConfSetting($s, $branch);
            $str =~ s/\{$s\}/$v/ if $v;
        }
    }
    return $str;
}

sub getConfSetting
{
    my ($setting, $dBranch) = @_;
    my $v = undef;
    $v = $conf{$setting} if $conf{$setting};
    $v = $conf{$dBranch}{$setting} if $conf{$dBranch}{$setting};
    $v = $conf{$1}{$2} if $setting =~ /(.*):(.*)/;
    return $v;
}

sub checkDirectoryRecurse
{
    my $dir = shift;

    if (! -d $dir) {
        my $parent = dirname($dir);
        return 2 if $parent eq '';
        return 0 unless checkDirectoryRecurse(dirname($dir)) == 1;
        return 0 unless checkDirectory($dir, 1);
    }
    return 1;
}

sub checkDirectory
{
    my ($d, $create) = @_;
    return 1 if (-d $d);
    return 0 unless $create;
    return 0 unless mkdir($d);
    print "Created directory $d\n";
}

sub processSection
{
    my $sect = shift;

    print "Processing section: $sect\n";
    if (!$conf{$sect}) {
        print "\tNo instructions found! Skipping\n";
        return 1;
    }
    $conf{$sect}{indexData} = catfile($conf{directories}{storage}, 
                                      "index_$sect.xml"); 
    if ($conf{$sect}{repoPath}) {
        my $idxFn;
        return 0 unless checkDirectoryRecurse($conf{$sect}{storagePath});
        print "\tUpdating repository...";
        if (! osUtils::updateSVNRepository($conf{$sect}{repoPath},
                                           $conf{$sect}{storagePath})) {
            print "failed\n";
            return 0;
        }



        if ($conf{$sect}{actions} || $conf{$sect}{multis}) {
            write_file($conf{$sect}{indexData}, 
                       "<?xml version=\"1.0\"?>\n<indexData>\n");

            foreach my $actp (@{$conf{$sect}{actions}}) {
                if (! prepareAction($actp, $sect)) {
                    print "Action preperation failed!\n";
                }
            }
            foreach my $actp (@{$conf{$sect}{multis}}) {
                if (! prepareAction($actp, $sect)) {
                    print "Action preperation failed!\n";
                }
            }
        }
        print "OK\n\tCreating XDOCS2 files...\n";
        opendir(my $dh, $conf{$sect}{storagePath}) || return 0;
        return 0 unless $dh;
        while (my $f = readdir($dh)) {
            next unless $f =~ /\.xml$/;
            my $fn = catfile($conf{$sect}{storagePath}, $f);
            print "\t\t$f\n";
            foreach my $actp (@{$conf{$sect}{actions}}) {
                if (! runAction($actp, $fn, $sect)) {
                    print "Action failed!\n";
                }
            }
        }
        closedir($dh);

        if ($conf{$sect}{actions} || $conf{$sect}{multis}) {
            append_file($conf{$sect}{indexData}, "</indexData>\n");
            foreach my $actp (@{$conf{$sect}{actions}}) {
                if (! finishAction($actp, $sect)) {
                    print "Action preperation failed!\n";
                }
            }
        }

        print "\t\tdone\n";
    }
    print "\tComplete\n";

    print "\tMaking index pages...";
    foreach my $idxp (@{$conf{$sect}{indexes}}) {
        if (! createIndex($idxp, $sect)) {
            print "failed!\n";
            return 0;
        }
    }
    print "OK\n\tComplete\n";
    print "\tMaking Multi page indexes...";
    foreach my $mp (@{$conf{$sect}{multis}}) {
        if (! createMulti($mp, $sect) ||
            ! finishAction($mp, $sect)) {
            print "failed!\n";
            return 0;
        }
        
    }

    return 1;
}

sub prepareAction
{
    my ($actp, $sect) = @_;

    my $outputFn = parseSettingString($actp->getChildData('fileName'));
    unlink($outputFn) if -f $outputFn;

    my $fT = parseSettingString($actp->getChildData('fileTemplate'));
    return 1 unless $fT;

    if ($fT =~ /^http/) {
        my $fetch = new httpGet(url => $fT, output => $outputFn);
        return 0 unless $fetch->Get();
    } elsif (osUtils::isAbsolute($fT)) {
        return 0 unless copy($fT, $outputFn);
    } else {
        print "write the code for this type of fileTemplate you lazy bum!\n";
    }
    return 1;
}

sub finishAction
{
    my ($actp, $sect) = @_;

    my $eT = parseSettingString($actp->getChildData('endTemplate'));
    return 1 unless $eT;

    my $outputFn = parseSettingString($actp->getChildData('fileName'));
    if ($eT =~ /^http/) {
#        my $fetch = new httpGet(url => $eT, output => $outputFn);
#        return 0 unless $fetch->Get();
print "figure out how to append...\n";
    } elsif (osUtils::isAbsolute($eT)) {
        return 0 unless my $txt = read_file($eT);
        return 0 unless append_file($outputFn, $txt);
    } else {
        print "write the code for this type of endTemplate you lazy bum!\n";
    }
    return 1;
}

sub runAction
{
    my ($actp, $fn, $sect) = @_;
    my $xml = XML::dspp->new();
    return 0 unless $xml;
    return 0 unless $xml->parseFile($fn);
    my $root = $xml->getRoot();
    return 0 unless ($root && $root->Name() eq $sect);

    my $id = $root->getChildData('asfid');
    my $outputFn = parseSettingString($actp->getChildData('fileName'));
    return 0 unless $outputFn;

    $outputFn =~ s/\{id\}/$id/;
    $outputFn =~ s/\{sect\}/$sect/;

    if (! checkDirectoryRecurse(dirname($outputFn))) {
        print "Unable to use output directory selected\n";
        print "Directory '$outputFn' doesn't exist\n";
        return 0;
    }

    my $xsl = xsltProc->new();
    $xsl->processFile(parseSettingString($actp->getChildData('template')),
                      $fn);
    return 0 unless $xsl->OK();
    my $type = $actp->Attribute('type');
    if ($type eq 'create') {
        write_file($outputFn, $xsl->output());
    } elsif ($type eq 'append') {
        append_file($outputFn, $xsl->output());
    } else {
        print "Unknown type '$type'\n";
    }
    return 1;
}

sub createIndex
{
    my ($actp, $sect) = @_;
    my $outputFn = parseSettingString($actp->getChildData('fileName'));
    return 0 unless ($outputFn || ! -f $conf{$sect}{indexData});

    $outputFn =~ s/\{sect\}/$sect/;

    if (! checkDirectory(dirname($outputFn))) {
        print "Unable to use output directory selected\n";
        return 0;
    }

    my $xsl = xsltProc->new();
    $xsl->addParam('section', $sect);
    $xsl->processFile(parseSettingString($actp->getChildData('template')),
                      $conf{$sect}{indexData});
    return 0 unless $xsl->OK();
    write_file($outputFn, $xsl->output());
    return 1;
}

sub createMulti
{
    my ($actp, $sect) = @_;
    my $outputFn = parseSettingString($actp->getChildData('fileName'));
    my $templ = parseSettingString($actp->getChildData('template'));
    my $regexp = findRegexp($actp);
    my $templIdx = parseSettingString($actp->getChildData('indexTemplate'));
    my $idxFn = parseSettingString($actp->getChildData('indexFilename'));
    return 0 unless ($outputFn || ! -f $conf{$sect}{indexData});

    $outputFn =~ s/\{sect\}/$sect/g;
    if (! checkDirectory(dirname($outputFn))) {
        print "Unable to use output directory selected\n";
        return 0;
    }

    my $xsl = xsltProc->new();
    $xsl->addParam('section', $sect);
    if ($actp->Attribute('type') eq 'alphabetical') {
        foreach my $l ('A' .. 'Z') {
            $xsl->addParam('index', $l);
            my $fn = $idxFn;
            $fn =~ s/\{index\}/$l/g;
            $xsl->processFile($templIdx, $conf{$sect}{indexData});
            return 0 unless $xsl->OK();
            return 0 unless write_file($fn, $xsl->output());
            if ($templ) {
                $xsl->processFile($templ, $conf{$sect}{indexData});
                return 0 unless $xsl->OK();
                return 0 unless append_file($outputFn, $xsl->output);
            }
            if ($regexp) {
                my $txt = $regexp;
                $txt =~ s/\{sect\}/$sect/g;
                $txt =~ s/\{index\}/$l/g;
                return 0 unless append_file($outputFn, $txt."\n");
            }

        }
    }
    return 1;
}

sub findRegexp
{
    my $xmlp = shift;
    my @arr = $xmlp->getChildren('regexp');
    return unless @arr;
    return $arr[0]->asString();
}
