#!/usr/bin/perl -w
use strict;
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
use File::Basename qw(basename);

my $VERSION = '0.01';

use Data::Dumper;
my %opts;
GetOptions(\%opts, "list", "save=s", "delete=s", "force", "keep", "restore=s", "help", "menu");
pod2usage(1) if $opts{help};
pod2usage(1) if not $opts{list} and not $opts{save} and not $opts{delete} and not $opts{restore} and not $opts{menu};
#print Dumper \%opts;
#exit;


my $patch      = "/usr/bin/patch";
my $svn        = "/usr/bin/svn";
my $subversion = "$ENV{HOME}/.subversion/switch";
my @list;
read_list();

if (not -d $subversion) {
	mkdir $subversion or die "Cannot create switch directory '$subversion'\n";
}

if ($opts{list}) {
	list_patches();
	exit;
}

if ($opts{save}) {
	my $file = check_name($opts{save});
	if (-e $file and not $opts{force}) {
		die "Change '$opts{save}' already exists. Use --force to overwrite\n" 
	}
	system "$svn diff > $file";
	if (not $opts{keep}) {
		system "$svn revert --recursive .";
	}
	exit;
}

if ($opts{delete}) {
	my $file = check_name($opts{delete});
	if (not -e $file) {
		die "This patch does not exists in $subversion\n";
	}
	if ($opts{force}) {
		unlink $file or die $!;
		exit;
	}
	print "Do you really want to remove '$opts{delete}' [yN]?";
	chomp(my $resp = <STDIN>);
	if (grep {$resp eq $_} qw(y Y Yes)) {
		unlink $file or die $!;
	} else {
		print "OK, we keep $opts{delete}\n";
	}
	exit;
}

if ($opts{restore}) {
	restore_patch($opts{restore});
}

sub restore_patch {
	my ($param) = @_;

	if ($param =~ /^\d+$/) {
		die "Invalid number '$param'\n" if not defined $list[$param-1];
		$param = $list[$param-1]; 
	}

	my $file = check_name($param);
	if (not -e $file) {
		die "Could not find $param\n";
	}
	my $diff = `$svn diff`;
	if ($diff and not $opts{force}) {
		die "There is a change in the workspace, use --force to remove it\n";
	}
	if ($diff) {
		system "$svn revert --recursive .";
	}
	system "$patch -p0 < $file";
	exit;
}

if ($opts{menu}) {
	while (1) {
		my ($sel, $param) = menu_selection();
		exit                  if $sel eq 'X';
		list_patches()        if $sel eq "L";
		restore_patch($param) if $sel eq "R";
	}
}


sub menu_selection {
	print "(L) List\n";
	print "(D) Delete       (D n)\n";
	print "(S) Save         (S n) or (S name for new)\n";
	print "(R) Restore      (R n)\n";
	print "(X) Exit\n";
	while (1) {
		chomp(my $selection = <STDIN>);
		next if $selection eq "";
		my ($sel, $param) = $selection =~ /^(.)\s*(.*?)\s*$/;
		print "$param\n";
		if (grep {uc($sel) eq $_} qw(L X D S R)) {
			print "----------------------------\n";
			return	(uc $sel, $param);
		} else {
			print "Invalid selection. Try again.\n";
		}
	}
}

sub read_list {
	my @patches = <$subversion/*.diff>;
	@list = map  {$_ = basename $_; s/.diff$//; $_} @patches;
	
}

sub list_patches {
	#read_list();
	my $i = 0;
	if (@list) {
		print map  {$i++; "$i) $_\n"} @list;
	} else {
		print "We could not find any saved workspace in $subversion\n";
	}
	print "\n";
}



sub check_name {
	my ($name) = @_;
	return "$subversion/$name.diff" if $name =~ /^\w[\w-]*$/;
	die "Invalid change name. It must only use a-z characters\n";
}




=head1 SYNOPSIS

 svnswitch --list      
 svnswitch --delete  NAME [--force]
                                          delete an existing patch from the switch directory
                                          if --force then won't even ask questions
 svnswitch --save    NAME [--force] [--keep]
                                          save the current change as NAME and revert ws to original status
                                          --force will overwrite an existing saved patch with the same name
                                          --keep will keep the workspace as it is (no revert) 
 svnswitch --restore NAME                
                                          restore another change (patch the WS with NAME.diff)

 svnswitch --menu                         interactive operations

=head1 OTHER IDEA

  When starting to work on a chane it is an anonymous change
  User can give a name to the current change
  If there is a name of the current change user can change to another name.
  The current change will be saved under the given name
     if the new name is an existing change it will be restored
     if not then a new empty change will be started (but one that already has a name)

=head1 TODO

 Deal with Windows and other OSes (location of svn, patch is missing, etc.)
 Should we check if we are in the top-most directory of a repository ?
 Should we save the place in the tree where the diff was created so later 
   we will not try to apply the diff in another location ?

=head1 AUTHOR

Gabor Szabo <szabgab@gmail.com>

based on comments on the users mailing list from 
  Norbert Unterberg <nunterberg @ gmail.com>
  David Faure <faure @ kde.org>


=cut

