#!/usr/bin/perl -w
use strict;
use SVK;
use SVK::XD;
use SVN::Repos;
use SVK::Util 'traverse_history';
use Getopt::Long;

my $revspec;

die unless GetOptions ("r|revision=s@" => \$revspec);

# XXX: requires patch to svn::dump 0.01
use SVN::Dump;

my $repospath = shift or die "repospath required.\n";
my $path      = shift or die "path required.\n";

my $repos = SVN::Repos::open($repospath) or die $!;
my $depot = SVK::Depot->new({ depotname => '', repos => $repos, repospath => $repospath});
my $t  = SVK::Path->real_new({ depot => $depot, path => $path })
    ->refresh_revision;

my $r = $revspec ? (bless { revspec => $revspec}, 'SVK::Command')->parse_revlist($t) : [0];
$r->[1] ||= $t->revision;

my ( $m, $mpath ) = $t->is_mirrored;
die "only whole repository mirrors are supported.\n" if length($mpath);
$t->revision($r->[1]);
$t->normalize;

my @revs;

traverse_history(
    root     => $t->root,
    path     => $t->path,
    cross    => 0,
    callback => sub {
        my ( $path, $rev ) = @_;
	return 0 if $rev < $r->[0];
        unshift @revs, $rev;
        1;
    }
);

autoflush STDERR 1;
my $i    = 0;
my $pool = SVN::Pool->new_default;

my $prefix = substr( $m->path, 1 );

print bless(
    {   headers => bless(
            { 'SVN-fs-dump-format-version' => 3 },
            'SVN::Dump::Headers'
        )
    },
    'SVN::Dump::Record'
)->as_string;
print bless(
    {   headers =>
            bless( { 'UUID' => $m->source_uuid }, 'SVN::Dump::Headers' )
    },
    'SVN::Dump::Record'
)->as_string;

my $prev;
for my $rev (@revs) {
    $pool->clear;

    my $rrev = $m->find_remote_rev($rev) or next;

    my $r = $t->mclone( revision => $rev );
    my $scalar;
    open my $buf, '+>', \$scalar;
    SVN::Repos::dump_fs2( $repos, $buf, undef, $rev, $rev, 1, 1, undef,
        undef );
    seek $buf, 0, 0;
    my $dump = SVN::Dump->new( { fh => $buf } );
    while ( my $record = $dump->next_record() ) {
        next if exists $record->{headers}{'SVN-fs-dump-format-version'};
        next if exists $record->{headers}{'UUID'};
	# padding
	if ($prev) {
	    for my $pad ($prev+1 .. $rrev-1) {
		print pad_rev($pad)->as_string;
		++$prev;
	    }
	}

        my $translate = sub {
            my $rec = shift;
            $rec->{headers}{'Revision-number'} = $rrev
                if exists $rec->{headers}{'Revision-number'};

            $rec->{headers}{'Node-copyfrom-rev'}
                = $m->find_remote_rev( $rec->{headers}{'Node-copyfrom-rev'} )
                if exists $rec->{headers}{'Node-copyfrom-rev'};

            $rec->{headers}{'Node-copyfrom-path'} =~ s{^\Q$prefix\E/?}{}
                if exists $rec->{headers}{'Node-copyfrom-path'};

            if ( exists $rec->{property} ) {
                my $prop = $rec->{property};
                delete $prop->{hash}{'svm:headrev'};
                $prop->{keys}
                    = [ grep { $_ ne 'svm:headrev' } @{ $prop->{keys} } ];
            }

            if ( $rec->{headers}{'Node-path'} ) {
                $rec->{headers}{'Node-path'} =~ s{^\Q$prefix\E/?}{}
                    or die $rec->{headers}{'Node-path'};
            }

        };
        $translate->( $record );
	$translate->( $record->{included} ) if $record->{included};

        print $record->as_string;
    }
    $prev = $rrev;

    printf STDERR "%d/%d\r", ++$i, scalar @revs;
}

sub pad_rev {
    my $rev = shift;
    my $pad = SVN::Dump::Record->new;
    $pad->set_headers(SVN::Dump::Headers->new);
    $pad->{headers}{ 'Revision-number' } = $rev;
    return $pad;
}

sub recal_header {
    my $record = shift;
    my ( $proplength, $textlength ) = ( 0, 0 );
    if ( exists $record->{property} ) {
        $record->{headers}{'Prop-content-length'}
            = length( $record->{property}->as_string );
    }
    if ( exists $record->{text} ) {
        $record->{headers}{'Text-content-length'}
            = length( $record->{property}->as_string );
    }
    recal_header($record->{included})
	if exists $record->{included};

    if ( $record->{headers}{'Content-length'} ) {
        $record->{headers}{'Content-length'} = $textlength + $proplength;
    }
}
