use strict; use warnings;
use PDL;
use PDL::IO::GD; # for write_gif_anim, bigger file but nicer looking
use PDL::Demos;
use File::Path qw(mkpath);
use File::Spec::Functions qw(catdir catfile splitpath updir);

my $html_header = <<'EOF';
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
</head>
<body>
EOF
my $html_footer = <<'EOF';
</body>
</html>
EOF
my $index_header = <<'EOF';
<!-- Generated with pdl/Demos/harness -->
<h1 class='title'>Demos and Examples</h1>
<p>On the following pages you'll find some examples of how to use PDL
for basic computations and plotting purposes. Several of the examples
are available as demos within <tt>perldl</tt>. For more details try:</p>
<pre>
 <b>perldl></b> demo
</pre>
<ul>
EOF
my $index_footer = <<'EOF';
<li><a href="?page=demos/plot2D">2D plotting with PGPLOT</a></li>
<li><a href="?page=screenshots/index">3D plots</a></li>
</ul>
EOF
my ($name_pat, $name_glob) = qw(output-%d.png output-*.png);

my $destroot = shift;
die "Usage: $0 destroot [singledemo]" unless defined $destroot && -d $destroot;

my $single_demo = shift;
my @infos = map [PDL::Demos->info($_)],
  'pdl', sort grep $_ ne 'pdl', $single_demo || PDL::Demos->keywords;
@infos = grep $_->[0] eq 'pdl' || $_->[1] =~ /Simple|GSL/, @infos;

my @this_output;
sub do_output { push @this_output, map "$_", @_; }
my @titles;
for (@infos) {
  my ($kw, $blurb, $mod) = @$_;
  my $outdir = catdir($destroot, updir, updir, qw(images demos), $kw);
  print " $kw -> $outdir\n";
  $ENV{PDL_SIMPLE_ENGINE} = 'gnuplot';
  $ENV{PDL_SIMPLE_OUTPUT} = catfile($outdir, $name_pat);
  mkpath($outdir) or die "$outdir: $!" if !-d $outdir;
  unlink($_) or die "unlink $_: $!"
    for grep -f, glob catfile($outdir, $name_glob);
  PDL::Demos->init($kw);
  my ($vidcounter, @outframes, %seen_img) = 0;
  for my $frame (PDL::Demos->demo($kw)) {
    my ($cmd, $txt) = @$frame;
    my @lines = split /\n/, $txt;
    shift @lines until $lines[0] =~ /\S/;
    pop @lines until $lines[-1] =~ /\S/;
    die "No non-blank lines found in a frame of $kw, text '$txt'" if !@lines;
    if ($cmd eq 'comment') {
      my $final = join "\n", @lines;
      $final =~ s#\n\n+#\n<p/>\n#g;
      push @outframes, [hyperlink($final)];
      next;
    }
    my ($state, $chunk, @to_execute, @thisframe) = ($lines[0] =~ /^\s*#/ ? 'c' : 'w', '');
    for (@lines) {
      if (/^\s*#+\s*(.*?)\s*#*\s*$/) { # words
        if ($state eq 'c') {
          chomp $chunk;
          push @thisframe, "<pre>\n$chunk\n</pre>" if $chunk;
          $chunk = '';
        }
        $state = 'w';
        $chunk .= $1 ? "$1\n" : "<p/>\n";
      } else {
        if ($state eq 'w') {
          chomp $chunk;
          push @thisframe, $chunk if $chunk;
          $chunk = '';
        }
        $state = 'c';
        $chunk .= "$_\n" if /\S/;
        push @to_execute, $_;
      }
    }
    chomp($chunk), push @thisframe, $state eq 'c' ? "<pre>\n$chunk\n</pre>" : $chunk
      if $chunk;
    if (@to_execute) {
      @this_output = ();
      s#^(\s*)print\b#do_output +#g for @to_execute;
      s#^(\s*)printf\b#do_output sprintf#g for @to_execute;
      my $exec_text = join "\n", "package $mod; *do_output=\\&main::do_output; sub do_output; no strict; use PDL;", @to_execute;
      eval $exec_text;
      die if $@;
      my $o = join('', @this_output)."\n";
      $o =~ s/\A\n+|\n+\z//g;
      $o = "<pre>\n$o\n</pre>" if $o;
      my @this_imgs = map $_->[1], sort {$a->[0]<=>$b->[0]} map [/(\d+)/, $_],
        grep !$seen_img{$_}++, glob catfile($outdir, $name_glob);
      if (@this_imgs) {
        if (@this_imgs > 1) {
          my $multiframe = cat(map rpic($_), @this_imgs);
          my $vidfile = catfile($outdir, "vid-".++$vidcounter.".gif");
          $multiframe->write_gif_anim($vidfile, 0, 10);
          unlink @this_imgs;
          delete @seen_img{@this_imgs}; # may reappear with new content
          @this_imgs = $vidfile;
        }
        $o .= sprintf qq{\n<img src="images/demos/%s/%s"/>}, $kw, (splitpath $this_imgs[0])[2];
      }
      push @thisframe, "<h4>Output</h4>\n$o" if $o;
    }
    $_ = hyperlink($_) for @thisframe;
    push @outframes, \@thisframe;
  }
  PDL::Demos->done($kw);
  rmdir $outdir if !glob catfile($outdir, $name_glob);
  open my $fh, ">", catfile($destroot, "$kw.html");
  $blurb =~ s#\s*\(.*##;
  push @titles, [$kw, my $title = "$kw - $blurb"];
  print $fh sprintf($html_header, $title),
    "<h1>$title</h1>\n\n",
    join("\n\n<hr/>\n", map join("\n", @$_), @outframes), "\n",
    $html_footer;
}
if (!$single_demo) {
  open my $fh, ">", catfile($destroot, "index.html");
  print $fh
    $index_header,
    (map qq{<li><a href="?page=demos/$_->[0]">$_->[1]</a></li>\n}, @titles),
    $index_footer;
}

sub hyperlink {
  my ($text) = @_;
  $text =~ s#PDL::[a-zA-Z0-9_:]+#<a href="https://metacpan.org/pod/$&">$&</a>#g;
  $text =~ s#([^"])(https?:\S+)#$1<a href="$2">$2</a>#g;
  $text;
}