package SDCommon;

use base qw/ Exporter /;
our @EXPORT = qw(writeCfg withecho withechoNoPrompt needs_upsTagUrl
needs_upsCurrentUrl needs_tagsUrl url insvn long_path oldSvnDirsCheck load_dirs set_statusref);
#### common ####
#

use Cwd;
use SVN::Client;
#use strict;

my $version;
# to be replaced during the package creation
$version=0.5;

# workaround for the old piece of Perl shit in Woody
sub long_path {
   if( ($] cmp "5.008") >= 0) {
      return Cwd::abs_path($_[0]);
   }
   use File::Basename;
   return $_[0] if (! -r $_[0]);
   return Cwd::abs_path(dirname($_[0]))."/".basename($_[0]); # would break on symlinks but get a newer perl if you wanna fix it
   #   ( (-l $_[0]) ? readlink($_[0]) : $_[0]);
}

$cfgFile=long_path(".svn/deb-layout");
$defCfgFile="debian/svn-deblayout";

sub exwerror {
   print STDERR $_[0]."\n";
   exit 1;
}

$msgmax=150;

##
# Prints the command, executes it, on errors also jumps to a control prompt
# returns 1 on success
#
sub withecho {
   if ($opt_noninteractive) {
      return withechoNoPrompt(@_);
   }
   # weed undefs out
   for($i=0; $i<=$#_; $i++) {splice(@_, $i, 1) if(!defined($_[$i]));}
   $cmd=join(' ',@_);
   if(length($cmd) > $msgmax && !$opt_verbose) {
      $cmd="";
      $i=0;
      while(length($cmd) < $msgmax) {$cmd.=" ".$_[$i];$i++;}
      $rest=$#_-$i+1;
      $cmd.=" <$rest more argument".($rest>1?"s>":">") if $rest;
   }
   retry:
   print STDERR "$cmd\n" if(!$opt_quiet);
   if(system(@_)) {
      print STDERR "Command $cmd failed in ".Cwd::getcwd.", how to continue now? [Qri?]: ";
      prompt:
      $ans = <STDIN>;
      if($ans =~ /^q$/i or $ans =~ /^$/) {
         exwerror("Aborting.\n");
      }
      elsif($ans =~ /^i$/i) {
         return 1;
      }
      elsif($ans =~ /^r$/i) {
         goto retry;
      }
      else {
         print STDERR "Invalid selection! " if $ans !~ /^\?/;
         print STDERR "The choices are: Quit (q), Retry (r), Ignore & continue (i).\n";
         print STDERR "[Qri?]: ";
         goto prompt;
      }
   }
   return 1;
}

## 
# Same as withecho but does not show the control prompt on errors
# returns on success
sub withechoNoPrompt {
   # weed undefs out
   for($i=0; $i<=$#_; $i++) {splice(@_, $i, 1) if(!defined($_[$i]));}
   $cmd=join(' ',@_);
   if(length($cmd) > $msgmax && !$opt_verbose) {
      $cmd="";
      $i=0;
      while(length($cmd) < $msgmax) {$cmd.=" ".$_[$i];$i++;}
      $rest=$#_-$i+1;
      $cmd.=" <$rest more argument".($rest>1?"s>":">") if $rest;
   }
   print STDERR "$cmd\n" if(!$opt_quiet);
   return (!system(@_));
}

sub oldSvnDirsCheck {
   my @svndirs;
   $dir = $_[0];
   @svndirs=`find $dir -name .svn`;
   if(@svndirs) {
      print "Found conflicting .svn directories in the upstream source:\n";
      if($opt_verbose) {
         print @svndirs;
      }
      else {
         print "use -v to display the files\n";
      }
      print "Hint: use the uclean program to fix upstream source tarball\n";
      exit 1;
   }
}
         

# gets the url of specified local (checkout) directory
sub url {
   my $info;
   return undef if !defined $_[0];
   # bloated with caching, maybe reduce to returning URL again
   my $URL;
   my $PATH;
   $testpath=long_path($_[0]);
   return $cacheUrl{$testpath} if($cacheUrl{$testpath});
   open($info, "svn info $testpath 2>/dev/null |");
   while(<$info>) { 
      $URL=$1 if(/^Url\s*:\W*(.+)\n/i); 
      $PATH=$1 if(/^Path\s*:\s*(.+)\n/i);
   };
   if($URL && close($info)) {
      $cacheUrl{$testpath}=$URL;
      $cacheUrl{$PATH}=$URl;
      return $URL;
   }
   return undef;
}

sub insvn {
   $testurl = $_[0];
   my $url=$testurl;
   $url =~ /(.*:\/\/)(.*)/;
   $proto=$1;
   $url=$2;
   while( $url =~ /\.\./) { 
      $url =~ s/\/\//\//g;
      $url =~ s/[^\/]+\/\.\.//g;
      $url =~ s/\/\//\//g;
   }
   $url =~ s/\/$//g;
   $url=$proto.$url;
   return undef if !defined $testurl;
   return  $inSvn{$testurl} if $inSvn{$testurl};
   print "Repository lookup, probing $url...\n";
   open($info, "svn ls $url 2>/dev/null |");
   @junk=<$info>;
   if (close $info) {
      $inSvn{$testurl}=$url;
      return $url;
   }
   return undef;
}

# helper to automate lookup for variables
# does not change defined var, but looks for useful path if not defined
sub such {
   
   my @testloc;
   my @testsvn;
   our($pre, $suf, $var, $basedir) = @_;
#   print "
#WOOT:
#   $pre
#   $suf
#   $var
#   $basedir
#";
   # if basedir contains :// -> svn lookup;
   # args: 
   # prefix: like branches
   # suffix: like upstream
   # var: name of the variable we work on in %c config hash
   # basedir: starting directory. If ommited, `pwd` is used

   return if (defined($c{$var}));
   
   $basedir=Cwd::getcwd if(!$basedir);
   if($basedir=~/:\/\//) { # huch, URL was specified as $basedir?
      print "W: $var not specified anywhere, looking in the local repository...\n";
      @testsvn=("$basedir/../$pre/$suf",
      "$basedir/../../$pre/$package/$suf");
   }
   else {
      if($opt_verbose) {
         print "I: trying blind lookup for ressource directories in SVN repository.\n";
         print "D: $pre, $suf, $var, $basedir.\n";
      }
      @testloc=(long_path("$basedir/../$pre/$suf"),
      long_path("$basedir/../../$pre/$package/$suf"));
   }
   foreach $dir (@testloc) {
      if ($dir && -d $dir) { $c{$var}=$dir; return;}
   }
   for (@testsvn) {
     print "Looking in SVN for: @testsvn\n";
      if ($_ && insvn($_)) { 
         print "I: adding the URLs to the $cfgFile to skip the check later.\n";
         $c{$var}=insvn($_);
         return;
      }
      else {
         print "Failed, assuming non-existant directory...";
      }
   }
}

# if still undefined, try blind search in the repository
sub needs_upsTagUrl {
   such("branches","upstream", "upsTagUrl", url("."));
   exwerror "upsTagUrl setting is required, but could not be found in $cfgFile or repository tree." if (!defined $c{"upsTagUrl"});
}
sub needs_upsCurrentUrl {
   such("branches","upstream/current", "upsCurrentUrl", url("."));
   exwerror "upsCurrentUrl setting is required, but could not be found in $cfgFile or repository tree." if (!defined $c{"upsCurrentUrl"});
}
sub needs_tagsUrl {
    my $hereurl=url(".");
    such("tags", "", "tagsUrl", $hereurl);
    if( !defined $c{"tagsUrl"} && $hereurl=~/(.*)\/branches\// ) { # oh, crap...
        print "Branch directory detected in URL, testing more possible locations\n";
        such("tags", "", "tagsUrl", "$1/$package");
    }
    exwerror "tagsUrl setting is required, but could not be found in $cfgFile or repository tree." if (!defined $c{"tagsUrl"});
}
sub writeCfg {
   if($_[0]){$cfgFile = $_[0];}
   open($cfg, ">$cfgFile") || die "Could not open $cfgFile for writing.\n";
   foreach (sort(keys %c)) {
      print $cfg "$_=".$c{$_}."\n" if defined $c{$_};
   }
   print $cfg @cfgRest;
   close($cfg);
}

sub sd_exit {
   if(!$nosave) {
      print "Writing config: $cfgFile\n\n\n" if $opt_verbose;
      $ret=$_[0];
      &writeCfg();
   }
   # print STDERR "Return-Code: $ret";
   exit $ret;
}

sub init {


   open($infoC, "svn info debian/changelog |");  @junk=<$infoC>;
   if(!close($infoC)) {
      exwerror "Not started from the Trunk directory or not a valid SVN repository. Aborting.\n";
   }

#   `head -n1 debian/changelog` =~ /^(\S+)\s*\((.+)\)/;
#   $package=$1 if(!defined($package));
#   $upVersion=$2;
#   $tagVersion=$upVersion;
#   if(!$force_debian) {
#      $upVersion=~s/^.*://;
#      $upVersion=~s/-.*//;
#   }

   if(  (-f "debian/changelog" &&
         `head -n1 debian/changelog` =~ /^(\S+)\s*\(((\d+):)?(.+)\)/
      )
      || 
      (-f "../debian/changelog" && chdir ".." &&
         `head -n1 debian/changelog` =~ /^(\S+)\s*\(((\d+):)?(.+)\)/
      )
   )
      {
      $package=$1;
      $upVersion=$4;
      $epoch=$3;
      $tagVersion=$upVersion;
      if(!$force_debian) {
         $upVersion=~s/^.*://;
         $upVersion=~s/(.*)-([^-]+)/$1/;
      }
      print STDERR "I: Got package name and version from debian/changelog.\n" if $opt_verbose;
   }
   else { 
      exwerror "E: Not started from the trunk/PACKAGE directory (debian/changelog garbled?).\n";
   }
   print "
   Package name: $package
   Current upstream version: $upVersion
   Debian tag: $tagVersion

   " if $opt_verbose;

}

sub configure {

   &init if(!$tagVersion);
   # keep the list of known vars here
   # @cfgVars=("upsCurrentDir", "upsTagDir", "tagsDir", "origDir", "origUrl",
   # "upsCurrentUrl","upsTagUrl", "tagsUrl", "trunkUrl", "trunkDir");

   my $fromFile=$cfgFile;
   if(!-e $cfgFile) {
      if(-e $defCfgFile) {
         print "$cfgFile not found, importing defaults from $defCfgFile\n";
         $fromFile=$defCfgFile;
      }
      else {
         print "$cfgFile not found, importing settings via Subversion properties... \n";
         foreach ( $prop, `svn proplist debian | grep 'svn-bp:'` ) {
            # import every svn-bp:* property as a cfg
            if(/\s*svn-bp:(\S+)\s*/) {
               $val=`svn propget svn-bp:$1 debian | head -n1 | tr -d '\n'`;
               $val=~ s/\ ~/\ $ENV{"HOME"}/;
               $c{$1}=$val;
               print "\t$1: $val\n";
            }
         }
         print "Autodetecting remaining properties... \n";
      }
   }
      
   if(-r $fromFile) {
      open($cfg, "<$fromFile");
      while(<$cfg>) {
         if(/(\S+)\s*=\s*(.+)(\n|\r)*/) {
            $val=$2;
            $val=~ s/\ ~/\ $ENV{"HOME"}/;
            $c{$1}=$val;
            print "\t$1: $val\n";
         }
         else {
            push(@cfgRest,$_);
         }
      }
      close($cfg);
   }

   # always redetect them; keep them in the same config base just for the
   # record
   $c{"trunkUrl"}=url(".");
   if($c{"trunkUrl"}) {
      $c{"trunkDir"}=getcwd;
   }
   else {
      exwerror "We are not in a working copy of SVN trunk directory";
   }

   foreach(values(%c)) {
      exwerror "\nThe directory $_ does not exist!

Create this directory or fix the setting in .svn/deb-layout or remove that
line and let svn-descripts redetect the value. Also check the associated URL.

" if($_=~/^\// && ! -e $_);
   }
   # lookup for local locations and get URLs from them if needed
   such("build-area", "", "buildArea");
   such("tags", "", "tagsDir");
   $c{"tagsUrl"}=url($c{"tagsDir"}) if($c{"tagsDir"} && !defined $c{"tagsUrl"});
   if($upVersion ne $tagVersion) {
      such("branches","upstream", "upsTagDir");
      $c{"upsTagUrl"}=url($c{"upsTagDir"}) if($c{"upsTagDir"} && !defined $c{"upsTagUrl"});
      such("branches","upstream/current", "upsCurrentDir");
      $c{"upsCurrentUrl"}=url($c{"upsCurrentDir"}) if($c{"upsCurrentDir"} && !defined $c{"upsCurrentUrl"});
      such("tarballs", "", "origDir");
      $c{"origUrl"}=url($c{"origDir"}) if($c{"origDir"} && !defined $c{"origUrl"});
   }

   &writeCfg;
   #
   #foreach("branches", "tarballs", "trunk", "tags") {
   #   if(length($pkgDir)) {
   #      exwerror "E: Weird directory structure. Where am I? Missing $startdir/$_/$package/\n"
   #      if(! -d $_."/$package");
   #   }
   #   else {
   #      mkdir $_;
   ##      withecho "svn $quiet add $_" if(`svn status -N $_`=~/^\?/);
   #   }
   #}
   #
   ## sanity check first
}

sub check_uncommited {
   open($svn, "svn status |");
   @statuslist = <$svn>;
   for(@statuslist) {
       if(/^\s*M+\s+(.*)/) {
           # FIXME: rewrite to run svn propget in one command with a list if
           # somebody complains about performance issues
           push(@conflicts,$_) if not `svn propget deb:ignoreM "$1"`;
       }
       else {
           push(@conflicts, $_);
       }
   }
   if (@conflicts) {
      print "E: found unresolved issues: \n\n@conflicts\n";
      exwerror "E: Resolve them manually before continueing\n";
   }
   close($svn);
}

my $statusref;
sub set_statusref {
    $statusref=shift;
}

sub collect_name {
   (my $file, my $status) = @_;
   chomp($file);
   $$statusref{$file}=$status->text_status;
   #print "Status: $file -- ".$status->text_status."\n";
}

#sub get_listing {
#    my $dir = shift;
#    my $curdir=Cwd::getcwd;
#    chdir $dir;
#    my %tmp;
#    $statusref=\%tmp;
#    my $ctx = new SVN::Client;
#    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);
#    chdir $curdir;
#    return (keys %tmp);
#}

# embedd new contents into an URL-referenced directory, using a specified
# work directory. The resulting directory is not commited, check and commit
# yourself with appropriate message.
sub load_dirs {
    my $url=shift;
    my $tmpdir=Cwd::abs_path(shift);
    my @src;
    while(@_) { push(@src, Cwd::abs_path(shift)); }

    # simple replacement for svn_load_dirs, just purge the files that
    # disappeared and add new stuff, then commit
    if( ! withechoNoPrompt("svn", "co", $url, $tmpdir) ) {
        # svn sucks here, import does create subdirs, but mkdir has no -p switch
        withecho "mkdir", "-p", $tmpdir;
        withecho("svn", "-m", "Creating trunk directory", "import", $tmpdir, $url);
        unlink $tmpdir;
        withecho("svn", "co", $url, $tmpdir);
    }
    my $curdir=Cwd::getcwd;

    chdir $tmpdir;
    
    my %tmp;
    my $ctx = new SVN::Client;
    $statusref=\%tmp;
    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);

    for(@src) {
        chdir $_;
        for(`find`) {
            chomp; 
            substr($_,0,2,""); # make it svn-like paths, just trailing / is missing
            delete $tmp{$_};
            delete $tmp{"$_/"};
        };
    }
    # remains in %tmp are not covered by the new stuff and shall be deleted
    chdir $tmpdir;
    @junk = keys %tmp;
    $ctx->delete(\@junk, 1);
    for(@src) {
        withecho("cp", "-a", "$_/.", "$tmpdir/.");
    }
    %tmp=();
    $statusref=\%tmp;
    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);
    for(keys %tmp) {
        if($tmp{$_} == 2) {
            $ctx->add($_, 1);
        }
   }
    chdir $curdir;
}

1;
##
##### /common ###

