#!/usr/bin/env perl
package _TMP_SCRIPT;
use strict;
use warnings;
use HTTP::Cookies;
use LWP::UserAgent;
use Data::Dumper;
use File::Temp;

$|++;

my $VERBOSE = 0;
my $BaseURL = 'http://intranet.some-domain.com:8080/wiki';
my $CookieDomain = '.some-domain.com';

my $RCDirectory = "$ENV{HOME}/.jspwiki";
my $CookieFile = "$RCDirectory/cookie-jar";
my $ParamsFile = "$RCDirectory/params.ser";
my $UserAgent;
my %Params;

my %Ligatures =
  (
   '&quot;' => '"',
   '&gt;' => '>',
   '&lt;' => '>',
   '&amp;' => '&',
  );

################################################################################

unless (-d $RCDirectory) {
  system(qq{mkdir -p $RCDirectory});
}

END {
  writeFile($ParamsFile,Data::Dumper::Dumper(\%Params));
}

sub setJSPWikiUserProfile {
  # prime the UA with any session cookies, etc.
  my $resp = $UserAgent->get("$BaseURL/UserPreferences.jsp");
  $resp = $UserAgent->post("$BaseURL/UserPreferences.jsp",
                   {
                    username=>$Params{JSPWikiUserProfile},
                    ok=>'Set my preferences!',
                    action=>'save',
                   });
}

sub readFile {
  my($file) = @_;
  my $fh;
  unless (open $fh, "<", $file ) {
    die "Error opening file for reading: $file : $!\n";
  }
  my $data;
  { local $/ = undef;
    $data = <$fh>; }
  close $fh;
  return $data;
}

sub getUserParam {
  my($paramName) = @_;
  unless ($Params{$paramName}) {
    print "$paramName: ";
    my $answer = <STDIN>;
    chomp $answer;
    $Params{$paramName} = $answer;
  }
  return $Params{$paramName};
}

sub getUserAgent {
  my $cookies = HTTP::Cookies->new(file=>$CookieFile,autosave=>1);
  my $ua = LWP::UserAgent->new;
  $ua->cookie_jar($cookies);
  return $ua;
}

sub writeFile {
  my($file,@content) = @_;
  my $fh;
  unless (open $fh, ">", $file) {
    die "Error opening file for writing: $file : $!\n";
  }
  print $fh @content;
  close $fh;
  return 1;
}


sub fetchTopicPage {
  my($topic,$ver) = @_;
  my $uri = "$BaseURL/Edit.jsp?page=$topic";
  $uri .= "&version=$ver" if $ver;
  my $resp = $UserAgent->get($uri);
  return $resp->content;
}

sub topicToFile {
  my($topic) = @_;
  return "$Params{WorkingDirectory}/$topic.txt";
}

sub diffTopic {
  my($topic,$ver1,$ver2) = @_;

  my $localFile = topicToFile($topic);
  unless (-r $localFile) {
    die "Error: local topic file does not exist ($localFile), can not diff until you have a local copy.\n";
  }

  if ($ver2) {
    system("bash","-c","diff -U 2 <($ $0 get-content $topic $ver1) <($ $0 get-content $topic $ver2)");
    return 1;
  }

  $ver1 ||= '';
  `bash -c 'diff -U 2 <("$" "$0" get-content "$topic" "$ver1") "$localFile"'`;
}

sub diffAllTopics {
  foreach my $topic (getLocalTopics()) {
    my $diff = diffTopic($topic);
    next unless $diff;
    print "Topic: $topic\n";
    print "  Local File: ",topicToFile($topic),"\n";
    print "  Wiki URL: $BaseURL/wiki.jsp?page=$topic\n";
    print $diff;
  }
}

sub makeParser {
  my($data) = @_;
  my $pos = 0;
  my $setData = sub { $data = $_[0]; $pos = 0; };
  my $start   = sub { $pos = 0 };
  my $fwd     = sub { return -1 if $pos == -1; $pos += $_[0]; $pos = -1 if $pos >= length($data); $pos };
  my $bck     = sub { return -1 if $pos == -1; $pos -= $_[0]; $pos = -1 if $pos < 0;              $pos };
  my $bckTo   = sub { return -1 if $pos == -1; $pos = rindex $data, $_[0], $pos; };
  my $fwdTo   = sub { return -1 if $pos == -1; $pos =  index $data, $_[0], $pos; };
  my $fwdPast = sub {
    return $pos if $pos == -1;
    $pos = index $data, $_[0], $pos;
    return $pos if $pos == -1;
    $pos += length($_[0]);
    $pos >= length($data) ? $pos = -1 : $pos;
  };
  my $btwn = sub {
    return -1 if $pos == -1;
    my $s = $fwdPast->($_[0]);
    return undef if $s == -1;
    my $e = $fwdTo->($_[1]);
    return undef if $e == -1;
    my $item = substr $data, $s, $e - $s;
    return $item;
  };

  my $all = sub {
    my @all;
    while (-1 != $pos) {
      my $item = $btwn->(@_);
      last unless $item;
      push @all, $item;
    }
    return @all;
  };

  return ($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all);
}

sub getPageInfo {
  my($topic) = @_;
  my $data = $UserAgent->get("$BaseURL/PageInfo.jsp?page=$topic")->content;
  #print "$data\n";
  my($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($data);
  my $table = $btwn->('Version','</table>');

  ($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($table);
  my $s = join("\t",qw(Version Date Author Size Changes from Previous))."\n";
  $s .= dumpRow($_) for $all->('<tr>','</tr>');
  return $s;
}

sub dumpRow {
  my($row) = @_;
  my($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($row);
  return join("\t",map { simpleStrip($_) } $all->('<td>','</td>'))."\n";
}

sub simpleStrip {
  my($text) = @_;
  $text =~ s|<[^>]+>||msg;
  $text =~ s|\s+| |msg;
  $text =~ s|^\s+||;
  $text =~ s|\s+$||;
  $text;
}

sub fetchTopicContent {
  my($topic,$ver) = @_;
  my $content = fetchTopicPage($topic,$ver);
  $VERBOSE and print "CONTENT: $content\n";
  $content =~ m|<textarea[^\]]*>(.+?)</textarea>|ims;
  my $text = $1;
  $VERBOSE and print "GOT BACK: $text\n";
  unless ($text) {
    $text = '';
  }
  $text =~ s/$_/$Ligatures{$_}/eg for( keys %Ligatures );
  return $text;
}

sub fetchTopic {
  my($topic) = @_;
  my $content = fetchTopicContent($topic);
  $content = '' unless defined $content;
  writeFile(topicToFile($topic),$content);
}

sub getTopicEditTime {
  my($topic) = @_;
  my $prev = fetchTopicPage($topic);
  $prev =~ m|name="edittime"\s+value="(.+?)">|ims;
  my $edittime = $1;
  return $edittime;
}

sub postTopic {
  my($topic) = @_;
  my $edittime = getTopicEditTime($topic);
  my $content = readFile(topicToFile($topic));
  my $result = $UserAgent->post
    (
       "$BaseURL/Edit.jsp?page=$topic",
     {
      page=>$topic,
      action=>'save',
      edittime=>$edittime,
      text=>$content,
      ok => 'Save',
     }
    );
  print "Response Code: ",$result->code,"\n";
  print $result->content;
}


sub postAllTopics {
  foreach my $topic (getLocalTopics()) {
    if (diffTopic($topic)) {
      print "Posting: $topic\n";
      postTopic($topic);
    }
  }
}

sub chip ($) {
  my($s) = @_;
  substr($s,0,1) = '';
  $s;
}

sub getTopicsFromContent {
  my($content) = @_;
  $content =~ s|\{{3}.+?\}{3}||gms;
  grep /^[A-Z]/, map {
    chop $_;
    $_ = chip $_;
    my @parts = split('\|',$_,2);
    $parts[-1];
  } ( $content =~ m|\[[^\[\]]+?\]|msg );
}

sub listTopicsFromPage {
  my($topic) = @_;
  getTopicsFromContent(fetchTopicContent($topic));
}

sub getLocalTopics {
  map { s|/([^/]+).txt$||; $1; } glob "$Params{WorkingDirectory}/*.txt";
}

{my %seen;
sub dumpWiki {
  my($startTopic) = @_;
  return if $seen{$startTopic}++;
  print "DUMP: $startTopic:\n";
  fetchTopic($startTopic);
  foreach my $topic (listTopicsFromPage($startTopic)) {
    unless ($topic) {
      warn "Huh, got a zero-len topic from $startTopic\n";
      next;
    }
    dumpWiki($topic);
  }
}}

sub displayHelp {
  exec('perldoc',$0);
}

sub editTopic {
  my($topic) = @_;
  if (-e topicToFile($topic) && diffTopic($topic)) {
    print "Error, you have a local copy of $topic and it is different\n";
    print diffTopic($topic),"\n";
    print "Please fix this situation before editing the topic in this way.\n";
    print "Shall I update it now? (Throwing away local diffs) [y/n]: ";
    my $answer = <STDIN>;
    chomp $answer;
    return undef unless 0 == index($answer,'y');
    print "Overwring local copy with fresh copy from wiki\n";
    fetchTopic($topic);
  }

  fetchTopic($topic) unless -e topicToFile($topic);

  # fix for Keith }:-)
  if ($ENV{EDITOR} =~ m|/?gvim$| ) {
    system($ENV{EDITOR},'-f',topicToFile($topic));
  }
  else {
    system($ENV{EDITOR}||'vi',topicToFile($topic));
  }

  if (my $diff = diffTopic($topic)) {
    print $diff,"\n";
    print "Commit back to the wiki [y/n]? ";
    my $answer = <STDIN>;
    chomp $answer;
    return undef unless 0 == index($answer,'y');
    postTopic($topic);

  }
}

################################################################################

if (-e $ParamsFile) {
  my $params = readFile($ParamsFile);
  $VERBOSE and print "PARMS: $params\n";
  my $VAR1; # for Dumper support
  %Params = %{ eval $params };
}

my $UserNameWasSet = exists $Params{JSPWikiUserProfile};
getUserParam($_) for qw(JSPWikiUserProfile WorkingDirectory);
$UserAgent = getUserAgent();
setJSPWikiUserProfile() unless $UserNameWasSet;

my %dispatch =
  (
   'get'         => sub { fetchTopic(@_) },
   'put'         => sub { postTopic(@_) },
   'get-raw'     => sub { print "$_[0]:\n",fetchTopicPage(@_),"\n"; },
   'get-content' => sub { print fetchTopicContent(@_); },
   'diff'        => sub { print diffTopic(@_) },
   'page-info'   => sub { print getPageInfo(@_) },
   'list-topics' => sub { print join("\n",listTopicsFromPage(@_)),"\n"; },
   'get-topics'  => sub { fetchTopic($_) for listTopicsFromPage(@_); },
   'dump-wiki'   => sub { dumpWiki($_[0]||'Main') },
   'edit'        => sub { editTopic(@_) },
  );

my %noTopicDispatch =
  (
   'diff'        => \&diffAllTopics,
   'put'         => \&postAllTopics,
   'dump-wiki'   => sub { dumpWiki($_[0]||'Main') },
   'help'        => sub { displayHelp() },
  );

my %allCommands = map { $_=>1 } (keys(%noTopicDispatch),keys(%dispatch));
my $usage = "$0 <command> topic\n  Commands: (".join('|',sort(keys %allCommands)).")\n";
my($cmd,$topic,@args) = @ARGV;

unless ($cmd) {
  die "Error: you must supply a command on the command line: $usage\n";
}

if (not($topic) && exists $noTopicDispatch{$cmd}) {
  $noTopicDispatch{$cmd}->(@args);
  exit 0;
}

unless ($topic) {
  print "Error: you must supply a topic on the command line: $usage\n";
  print "Your currently downloaded topics are:\n";
  print "  $_\n" for getLocalTopics();
  exit 1;
}

unless (exists $dispatch{$cmd}) {
  die "Error: you supplied an invalid command: '$cmd': $usage\n";
}

$dispatch{$cmd}->($topic,@args);

exit 0;


__DATA__

=head1 NAME

jspwiki - Wiki Interaction and Editing Tool

=head1 SYNOPSIS

  jspwiki edit TopicName
  jspwiki get TopicName
  jspwiki put TopicName
  jspwiki get-raw TopicName
  jspwiki get-content TopicName
  jspwiki diff TopicName
  jspwiki diff TopicName ver1
  jspwiki diff TopicName ver1 ver2
  jspwiki page-info TopicName
  jspwiki list-topics TopicName
  jspwiki get-topics TopicName
  jspwiki dump-wiki
  jspwiki dump-wiki TopicName
  jspwiki help


=head1 DESCRIPTION

This tool interacts with the HMS JSPWiki instance at
http://intranet.some-domain.com:8080/wiki.  It works similarly to CVS.
It can check out a Wiki topic, check back in a Wiki topic, diff and
spider the Wiki (dumping it to the local file system).

=head1 COMMANDS

edit topic-name

Fetch the topic content from the Wiki, execute your editor, then push
the changed file (if it changed) back to the Wiki.

get topic-name

Retreives a topic to a local file.

put topic-name
put

Pushes a topic back to the wiki from a local file.  With no topic name
as an argument it puts all the topics from your local working
directory back to the wiki (if they are different).

get-raw topic-name

Prints the raw HTML for a topic page.

get-content topic-name

Prints the content for a topic.

diff topic-name
diff topic-name ver1
diff topic-name ver1 ver2

With no arguments, this shows a diff for each topic you have in your local working copy.

With a single topic name as an argument it shows a diff between the
wiki and your local working copy.

With version arguments after the topic name it shows the diff between
those versions.  If the second version is left blank, it diffs between
the specified version and the local copy.

page-info topic-name

Dumps the version history of the page as tab data.

list-topics topic-name

Lists the topics that are linked to by the given topic.

get-topics topic-name

Fetches the topics linked to by the given topic.

dump-wiki
dump-wiki topic-name

Spiders the wiki from the given topic-name (defaults to Main) to your
local working directory.

help

Displays this document.

=head1 ENVIRONMENT

HOME - the location of the .jspwiki parameters.

EDITOR - the editor invoked when you edit a topic.

=head1 FILES

$HOME/.jspwiki/cookie-jar

This stores cookies for the Wiki, like your JSPWikiUserProfile.

$HOME/.jspwiki/params.ser

This is a store for jspwiki settings, such as the local working directory.

=head1 AUTHORS

=head1 SEE ALSO

cvs

http://www.jspwiki.org/

http://intranet.some-domain.com:8080/wiik

=cut
