use 5.010;
use alienfile;
use Sort::Versions;
use Path::Tiny qw /path/;

my $on_windows = $^O =~ /mswin/i;
my $on_automated_rig
  =  $ENV{PERL_CPAN_REPORTER_DIR}
  || $ENV{PERL_CPAN_REPORTER_CONFIG}
  || $ENV{AUTOMATED_TESTING}
  || $ENV{TRAVIS}
  || $ENV{APPVEYOR}
  || $ENV{CI};


use Cwd;
my $base_dir = getcwd();

use Alien::proj;
use Alien::sqlite;
use Alien::freexl;
use Alien::geos::af;

my @alien_deps = qw( Alien::sqlite Alien::proj Alien::freexl Alien::geos::af );

my $sep_char = $on_windows ? ';' : ':';
$ENV{PATH} .= $sep_char
           . join ($sep_char,
                   Alien::sqlite->bin_dir,
                   Alien::proj->bin_dir,
                   Alien::freexl->bin_dir,
                   Alien::geos::af->bin_dir,
            );

plugin 'Build::SearchDep' => (
  aliens   => [@alien_deps],
  public_I => 1,
  public_l => 1,
);

#  make libtool noisy for debug purposes
#$ENV{LTFLAGS} = "--debug --verbose" if $on_windows;



my $min_target_version = '5.0.0';

plugin 'PkgConfig' => (
    pkg_name => 'spatialite',
    minimum_version => $min_target_version,
);


share {

  my $with_local = '';
  my $with_cpp11 = '';

  start_url 'http://www.gaia-gis.it/gaia-sins/libspatialite-sources/';
  #start_url "file://$base_dir";  #  debug
  plugin Download => (
    filter  => qr/^libspatialite-([0-9\.]+[a-z]?)\.tar\.gz$/,
    version => qr/^libspatialite-([0-9\.]+[a-z]?)\.tar\.gz$/,
  );

  my $lib_version = get_lib_version() // 'not yet defined';
  say "Downloaded spatialite version is $lib_version";
  
  plugin Extract => (format => 'tar.gz');


  plugin 'Build::Autoconf' => ();
  plugin 'PkgConfig::PPWrapper';

  my $build_static = ($^O =~ /mswin/i) ? '' : '--disable-shared';
  #$build_static = '';
  #$build_static = '--enable-static=no';  #  override - needed?  leftover from gdal
  $build_static = '--enable-shared=yes';
  #$build_static = '' if $ENV{FORCE_DYNAMIC};

  # most of these are crude  
  # we can enable librttopo when we have an alien for it
  my $extra_build_args
    = ($on_windows ? '--target=mingw32 ' : '')
      . join ' ', (qw /
      --enable-libxml2=no
      --enable-examples=no
      --enable-minizip=no
      --disable-rttopo
      --disable-gcp
    /);

  #  see if this helps with cirrus bsd builds
  #$ENV{SQLITE3_CFLAGS} = Alien::sqlite->cflags;
  #$ENV{SQLITE3_LIBS} = Alien::sqlite->libs;
  #say "sqlite cflags: " . Alien::sqlite->cflags;
  #say "sqlite libs: " . Alien::sqlite->libs;
  
  if ($^O =~ /bsd/) {
    plugin 'Build::Make' => 'gmake';
    if (-d '/usr/local') {
        $with_local = ' --with-local=/usr/local ';
    }
    if (!-e '/usr/local/include/sqlite3.h' && Alien::sqlite->install_type eq 'system') {
      warn '/usr/local/include/sqlite3.h does not exist, '
         . 'you might need to install the sqlite package for your system, '
         . 'or install a share version of Alien::sqlite';
    }
  }
  elsif ($^O =~ /dragonfly/) {
    #  might need to be combined with bsd check above
    #  but not sure if /usr/local is needed yet
    plugin 'Build::Make' => 'gmake';
  }

  my $make_cmd = '%{make}';
  my $make_inst_cmd = '%{make} install';
  my @make_clean;
  #  try not to exceed the cpan-testers log limits
  if ($on_automated_rig) {
    say "Running under CI or automated testing";
    $make_cmd      .= q/ | perl -ne "BEGIN {$|=1; open our $log, q|>|, q|build.log|};   print qq|\n| if 0 == ($. %% 100); print q|.|; print {$log} $_;" || type build.log/;
    $make_inst_cmd .= q/ | perl -ne "BEGIN {$|=1; open our $log, q|>|, q|install.log|}; print qq|\n| if 0 == ($. %% 100); print q|.|; print {$log} $_;" || type install.log/;
    if (!$on_windows) {
        $make_cmd =~ s/%%/%/;
        $make_cmd =~ s/type/cat/;
        $make_cmd =~ s/"/'/g;
        $make_inst_cmd =~ s/%%/%/;
        $make_inst_cmd =~ s/type/cat/;
        $make_inst_cmd =~ s/"/'/g;
    }
    #  clean up the build dir on cpan testers etc
    plugin 'Cleanse::BuildDir';
  }

  meta->around_hook( build => \&set_compiler_flags );
  meta->around_hook(
    build => sub {
      my ($orig, $build, @args) = @_;
      $build->log("Setting CCACHE_BASEDIR to " . getcwd());
      local $ENV{CCACHE_BASEDIR} = getcwd();
      $orig->($build, @args);
    }
  );

  build [
    "%{configure} $with_local $with_cpp11 $build_static $extra_build_args",
    \&pause,
    $make_cmd,
    \&patch_rpaths,
    \&rename_la_files,
    $make_inst_cmd,
    #@make_clean
  ];

};


sub rename_la_files {
    #  need to return if not share
    return if !$on_windows;
    
    use File::Find::Rule;
    my @la_files
      = File::Find::Rule->file()
                        ->name( '*.la' )
                        ->in( '.' );
    foreach my $file (@la_files) {
        Alien::Build->log("Renaming $file so it will not interfere with gdal compilation");
        rename $file, $file . '.bak';
    }

}

#  should be a gather hook working on the stage dir
sub patch_rpaths {
  my ($build) = @_;
  
  #  only run on unices - incomplete check but
  #  I don't think aliens work on VMS or zOS
  return if ($on_windows or $^O =~ /darwin/i);

  my $h = get_alien_state_hash();
  my $install_path = $h->{install}{prefix};
  return if !defined $install_path;

  my @alien_rpaths;
  for my $alien (@alien_deps) {
    next if not $alien->install_type('share');
    push @alien_rpaths, $alien->dist_dir . '/lib';
  }
  if (!@alien_rpaths) {
    $build->log('No shared alien deps found, not updating rpaths');
    return;
  }

  my $origin_string = $^O =~ /darwin/ ? '@loader_path' : '${ORIGIN}';
  my $alien_rpath_text
    = join ':', (
       (map {$origin_string . '/../' . path ($_)->relative($install_path)->stringify} @alien_rpaths),
       @alien_rpaths
      );

  $build->log ("Prepending rpaths with $alien_rpath_text");
  
  use File::Find::Rule;
  my (@so_files)
    = grep {not -l $_}
      File::Find::Rule
              ->file()
              ->name( qr/^(lib|mod_)spatialite.so.?/ )
              ->in( getcwd() );
  
  eval 'require Alien::patchelf'
    or do {
      warn 'Unable to load Alien::patchelf ($@), cannot update rpaths';
      return;
    };
  my $pe = Alien::patchelf->new;
  foreach my $so_file (@so_files) {
    my ($old_rpath, $result, $stderr, @errors);
    ($old_rpath, $stderr, @errors)
      = $pe->patchelf ('--print-rpath', $so_file);
    $old_rpath //= '';
    #  prepend our paths
    my $rpath = $alien_rpath_text . ($old_rpath ? (':' . $old_rpath) : '');
    $build->log("Updating rpath for $so_file to $rpath, was $old_rpath");
    ($result, $stderr, @errors)
      = $pe->patchelf ('--set-rpath', $rpath, $so_file);
    warn $stderr if $stderr;
  }
  
  return;
}


sub set_compiler_flags {
  my ($orig, $build, @args) = @_;

  local $ENV{CFLAGS}   = "-O2 " . ($ENV{CFLAGS} // '');
  local $ENV{CXXFLAGS} = '-O2 ' . ($ENV{CXXFLAGS} // '');

  $build->log ("Setting compiler flag env vars to -O2");

  $orig->($build, @args);
}

sub pause {
    return;  #  re-enable in case of debug
    return if $on_automated_rig;
    return if !$on_windows;

    say "CONTINUE?";
    my $response = <>;
    while (not $response =~ /yes/) {
        $response = <>;
    }
}


sub get_lib_version {
    my $h = get_alien_state_hash();
    return $h->{runtime}{version};
}

sub get_alien_state_hash {
    use JSON::PP;
    my $root = "$base_dir/_alien";
    my $f = "$root/state.json";
    my $h = {};
    if (-e $f) {
        open my $fh, '<', $f or die $!;
        my $d = do {
            local $/ = undef;
            <$fh>;
        };
        $h = JSON::PP::decode_json($d);
    }
    return $h;
}