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

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 Env qw( @PATH @PKG_CONFIG_PATH );

use Alien::sqlite;
unshift @PATH, Alien::sqlite->bin_dir;
my @dep_aliens = ('Alien::sqlite');
my %have;
my $lib = 'Alien::curl';
$have{$lib} = eval "require $lib";
if ($have{$lib}) {
    unshift @PATH, $lib->dist_dir . '/dynamic';
}
$lib = 'Alien::libtiff';
$have{$lib} = eval "require $lib";
if ($have{$lib}) {
    #say 'Adding libtiff dependency';
    unshift @PATH, $lib->bin_dir;
    push @dep_aliens, $lib;
    my $p;
    if ($on_windows && $lib->install_type eq 'system') {
      #  dirty hack for strawberry perl
      $p = path ($^X)->parent->parent->parent . '/c/lib/pkgconfig';
    }
    elsif ($lib->install_type eq 'share') {
      $p = path ($lib->dist_dir, 'lib', 'pkgconfig');
    }
    if ($p && -e $p) {
      push @PKG_CONFIG_PATH, $p;
      say join ' ', @PKG_CONFIG_PATH;
    }
}

say "Alien::sqlite has sqlite version " . Alien::sqlite->version;

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

my $min_target_version = '6.1';

plugin 'PkgConfig' => (
    pkg_name => 'proj',
    minimum_version => $min_target_version,
);
#plugin 'Probe::CommandLine' => (
#  command  => 'projinfo',
#  secondary => 1,  #  secondary to pkgconf probe
#);


share {

  #  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 ($have{'Alien::libtiff'}) {
    my $p = path ('Alien::libtiff'->dist_dir, 'lib');
    $ENV{TIFF_LIBS}
      ||= (-e $p
            ? "-L$p "
            : ''
          )
          . 'Alien::libtiff'->libs;
    say "libtiff libs: $ENV{TIFF_LIBS}";
  }

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

  if ($on_windows) {
    #  there are issues with strawberry perl's gcc
    plugin 'Prefer::BadVersion' => '7.1.0';
  }
  
  start_url 'http://download.osgeo.org/proj/';
  #start_url "file://$base_dir";  #  debug
  plugin Download => (
    filter  => qr/^proj-([0-9\.]+)\.tar\.gz$/,
    version => qr/^proj-([0-9\.]+)\.tar\.gz$/,
  );

  my $proj_version = get_proj_version() // 'not yet defined';
  say "Downloaded proj version is $proj_version";
  
  die "Downloaded proj version $proj_version is too low "
      . "(should be >= $min_target_version).\n"
      . "Please update your Alien::Build::Fetch::Cache if using one."
    if   defined $proj_version
      && versioncmp ($proj_version, $min_target_version) < 0;
  
  plugin Extract => (format => 'tar.gz');


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

  my $build_static = $on_windows ? '' : '--disable-shared';
  $build_static = '';
  $build_static = '--enable-static=no';  #  override - needed?  leftover from gdal
  $build_static = '' if $ENV{FORCE_DYNAMIC};
  
  
  if ($^O =~ /bsd|dragonfly/) {
    plugin 'Build::Make' => 'gmake';
    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';
    }
  }

  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;
    }
    #if (! ($ENV{TRAVIS} || $ENV{APPVEYOR})) {
    #    push @make_clean, '%{make} clean';
    #}
    #  clean up the build dir on cpan testers etc
    #  but not github workflows
    if (!$ENV{CI}) {
      plugin 'Cleanse::BuildDir';
    }
  }
  
  my $config_args = $ENV{ALIEN_PROJ_CONFIG_ARGS} // '';
  $config_args =~ s/[^-\s\w,=]//g;  #  overkill?
  my $with_tiff = '';  #  only needed for proj 7+
  my $with_curl = '';  #  only needed for proj 7+
  if ($proj_version ge 7) {
    if (!$have{'Alien::libtiff'}) {
      $with_tiff = '--disable-tiff';
      say 'Disabling TIFF support';
    }
    else {
      $with_tiff = '--enable-tiff';
      say 'Enabling TIFF support';
    }
    if (!$have{'Alien::curl'}) {
      $with_curl = '--without-curl';
    }
    else {
      my $dynamic_config = path ('Alien::curl'->dist_dir . '/dynamic/curl-config');
      if (-e $dynamic_config) {
        say 'Adding curl support';
        $with_curl = "--with-curl=$dynamic_config";
      }
      elsif ('Alien::curl'->install_type eq 'system') {
        if ('Alien::curl'->cflags =~ /STATIC_LIB/) {
          $with_curl = '--without-curl';
          say 'Disabling curl support.  You have Alien::curl, but it lacks a dynamic curl-config.';
        }
        else {
          say 'Adding curl support';
          $with_curl = "--with-curl";
        }
      }
      else {
        say 'Disabling curl support.  You have Alien::curl, but it lacks a dynamic curl-config.';
        #  until we depend on an Alien::libcurl that provides a dynamic curl-config
        $with_curl = '--without-curl';  
      }
    }
  }
  $config_args = "$with_local $with_cpp11 $with_tiff $with_curl $build_static $config_args";

  #meta->around_hook( build => \&_pkgconf_wrapper );
  plugin 'PkgConfig::PPWrapper';
  meta->around_hook( build => \&remove_gitfw_from_path );

  say 'PKG_CONFIG_PATH:' . join ' ', @PKG_CONFIG_PATH;
  
  build [
    #\&pause,
    "%{configure} $config_args",
    $make_cmd,
    $make_inst_cmd,
    \&rename_la_files,
  ];

};


#  git for windows clashes with MSYS
#  if its /usr/bin dir is in the path
sub remove_gitfw_from_path {
  my ($orig, $build, @args) = @_;

  return $orig->($build, @args)
    if !$on_windows;

  local $ENV{PATH} = $ENV{PATH};

  my $msys_path = eval {
    path('Alien::MSYS'->msys_path())
  };
  return if !defined $msys_path;
  my $count = @PATH;

  @PATH
    = grep {path($_)->stringify =~ m|/usr/bin$| && path($_) ne $msys_path ? () : $_}
      @PATH;

  my $removed = $count - @PATH;
  if ($removed) {
    $build->log ("$removed additional .../usr/bin dirs were removed from the path for compilation");
  }

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


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

  return $orig->($build, @args)
    if !$on_windows;

  my $pk = File::Which::which ($ENV{PKG_CONFIG})
        || File::Which::which ('ppkg-config')
        || File::Which::which ('pkg-config');

  if (!defined $pk) {
    $build->log ("Could not locate ppkg-config or pkg-config in your path:\n");
    return $orig->($build, @args);
  }

  $pk =~ s/\.bat$//i;
  if (!(-e $pk && -e "$pk.bat")) {
    $build->log ("$pk unlikely to be pure perl");
    return $orig->($build, @args);
  }

  my $perl = $^X;
  $perl =~ s/\.exe$//i;
  foreach my $path ($perl, $pk) {
    $path =~ s{\\}{/}g;
    $path =~ s{^([a-z]):/}{/$1/}i;
    $path =~ s{\s}{\\ }g;
  }
  my $args = '$' . join ' $', (1..9);
    
  my $wrapper = <<"EOWRAPPER"
#/bin/sh

$perl $pk $args
EOWRAPPER
  ;
  $build->log ("Pure perl pkg-config detected on windows.\n");
  $build->log ("Wrapping $pk in shell script to cope with MSYS perl and paths.\n");
  my $fname = Path::Tiny->new(File::Temp::tempdir( CLEANUP => 1 ))->child('pkg-config');
  open my $fh, '>', $fname
    or die "Unable to open pkg-config wrapper $fname, $!";
  print {$fh} $wrapper;
  close ($fh);
  $build->log ("Setting \$ENV{PKG_CONFIG} to point to $fname\n");
  
  local $ENV{PKG_CONFIG} = $fname;
  
  return $orig->($build, @args);
}


sub update_pkg_conf_path {
  return;
    return if !$on_windows;
    #  should be a before or around hook
    use Env qw /@PKG_CONFIG_PATH/;
    say 'Modifying drive paths in PKG_CONFIG_PATH';
    say $ENV{PKG_CONFIG_PATH};
    #  msys-ificate drive paths
    @PKG_CONFIG_PATH = map {s{^([a-z]):}{/$1}ri} @PKG_CONFIG_PATH;
    #  make sure we get the dynamic libcurl
    #  (although the proj configure script does not currently use it)
    @PKG_CONFIG_PATH
      = map {s{Alien-curl[/\\]lib[/\\]pkgconfig}{Alien-curl/dynamic/pkgconfig}ri}
        @PKG_CONFIG_PATH;
    $ENV{PKG_CONFIG_PATH} = join ':', @PKG_CONFIG_PATH;
    say $ENV{PKG_CONFIG_PATH};
    return;
}

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( $base_dir );
    foreach my $file (@la_files) {
        say "Renaming $file so it will not interfere with gdal compilation";
        rename $file, $file . '.bak';
    }

}


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_proj_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;
}

