use 5.010;
use alienfile;
use Sort::Versions;
use Env qw /@PATH @LD_LIBRARY_PATH/;
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};

my @aliens_l_and_I = qw(
        Alien::geos::af
        Alien::proj
        Alien::sqlite
        Alien::libtiff
  );

#  make sure we get GEOS, PROJ and other support
foreach my $alien (@aliens_l_and_I) {
  eval "require $alien";
  if ($alien->install_type eq 'share') {
    log "Prepending $alien bin dir to path";
    unshift @PATH, $alien->bin_dir;
  }
}
my $have_spatialite = eval 'require
  Alien::spatialite';
if ($have_spatialite && Alien::spatialite->version ge 5) {
  log "Prepending Alien::spatialite bin dir to path";
  unshift @PATH, $have_spatialite->bin_dir;
  push @aliens_l_and_I, $have_spatialite;
}
else {
  $have_spatialite = undef;
}


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

#  configure script does not seem to detect the proj lib distributed with Strawberry perl
#use FFI::CheckLib;
#my $proj_lib = FFI::CheckLib::find_lib (lib => 'proj');
#say "PROJ_LIB FILE IS $proj_lib";


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


use Cwd;
my $base_dir = getcwd();
my $patch_file_isnan
  = "$base_dir/patch_isnan_gcc.patch";
my $patch_file_configure
  = "$base_dir/0001-Enable-shared-build-on-freebsd-10.patch";

my $min_target_version = '3.1.0';

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


share {

  #  add existing alien files to path etc
  #  not very elegant...
  #  disable for now - config tests fail
  #  it looks to be already noted in https://github.com/Perl5-Alien/Alien-Build/issues/12
  #  might also be able to access via old hash via https://metacpan.org/pod/Alien::Build#install_prop
  my $have_alien_gdal = eval 'require Alien::gdal';
  my $copy_from_dir;
  if ($ENV{ALIEN_SHARE_RECYCLE} && $have_alien_gdal && Alien::gdal->install_type eq 'share') {
      my $ag_version = Alien::gdal->version;
      say "Found existing gdal via alien ($ag_version) in " .  Alien::gdal->dist_dir;
      #  append the relevant path
      if (versioncmp($ag_version, $min_target_version) >= 0) {
        $copy_from_dir = Alien::gdal->dist_dir;
      }
  }

  if ($copy_from_dir) {
    #  files are copied in the extract hook
    meta->register_hook(download => sub {
      Path::Tiny->new ('fauxdownload.txt')->touch;
    });

    if($^O eq 'MSWin32') {
      meta->register_hook(extract => sub {
        my($build) = @_;
        $copy_from_dir =~ s|/|\\|g;  #  it seems that xcopy needs backslashes in paths
        $build->system("xcopy $copy_from_dir . /E /Q");
      });
    }
    else {
      meta->register_hook(extract => sub {
        my ($build) = @_;
        "cp -aR $copy_from_dir .";
      });
    }

    meta->after_hook( extract => sub {
      my($build) = @_;
      $build->log('CURRENTLY IN ' . cwd());
      File::Path::rmtree '_alien';
    });

    plugin 'Build::Copy';

  }
  else {
    my $with_local = '';
    my $with_cpp11 = '';
    
    #  ensure we are compatible with the installed proj version
    my $start_url = Alien::proj->atleast_version ('6.0.0')
      ? 'http://download.osgeo.org/gdal/CURRENT'
      : 'http://download.osgeo.org/gdal/2.4.2';
  
    say "Proj library version is " . Alien::proj->version;
  
    start_url $start_url;
    #start_url "file://$base_dir";  #  debug
    plugin Download => (
      filter  => qr/^gdal-([0-9\.]+)\.tar\.gz$/,
      version => qr/^gdal-([0-9\.]+)\.tar\.gz$/,
    );
  
    my $gdal_version = get_gdal_version() // 'not yet defined';
    say "Downloaded gdal version is $gdal_version";
    
    plugin Extract => (format => 'tar.gz');
  
    if (versioncmp($gdal_version, '2.3.0') == -1) {
      if ($on_windows) {
        #  fix known issue in 2.2.3, harmless in 2.2.4.
        patch [
          '%{patch} port/cpl_port.h < ' . $patch_file_isnan,
        ];
      }
      elsif ($^O =~ /freebsd/i) {
        #  make sure we run a shared install on freebsd by default
        #  (before libtool files were updated for gdal 2.3.0)
        patch [
          '%{patch} configure < ' . $patch_file_configure,
        ];
        $with_cpp11 = ' --without-cpp11 ';  #  issues with cad lib pre 2.3.0
      }
    }
  
    plugin 'Build::Autoconf' => ();
  
    my $build_static = ($^O =~ /mswin/i) ? '' : '--disable-shared';
    $build_static = '';
    $build_static = '--enable-static=no';  #  override - HUGE files if static
    $build_static = '' if $ENV{FORCE_DYNAMIC};
    
    
    if ($^O =~ /bsd|dragonfly/) {
      plugin 'Build::Make' => 'gmake';
      if (-d '/usr/local') {
          $with_local = '--with-local=/usr/local';
      }
    }
  
  
    my $make_cmd = '%{make}';
    my $make_inst_cmd = '%{make} install';  
    my @automated_rig_config_args;
    
    #  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} $_;" /;
      $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} $_;" /;
      if (!$on_windows) {
          $make_cmd =~ s/%%/%/;
          $make_cmd =~ s/"/'/g;
          $make_cmd .= ' || (E=$? && cat build.log && exit $E)';
          $make_inst_cmd =~ s/%%/%/;
          $make_inst_cmd =~ s/"/'/g;
          $make_inst_cmd .= ' || (E=$? && cat install.log && exit $E)';
      }
      else {
          #  fake exit codes on failure, not sure how to get originals
          $make_cmd      .= q/ || perl -e"system ('type', 'build.log'); exit 1"/;
          $make_inst_cmd .= q/ || perl -e"system ('type', 'install.log'); exit 1"/;
      }
      if (! ($ENV{TRAVIS} || $ENV{APPVEYOR} || $ENV{CI})) {
          #  make build slightly faster and smaller on cpan testers
          push @automated_rig_config_args, '--with-gnm=no';
      }
      #  debug
      push @automated_rig_config_args,
        (qw/--with-openjpeg=no --with-jasper=no/);
  
      #  clean up the build dir on cpan testers etc
      plugin 'Cleanse::BuildDir';
    }
    
    my @with_args = (
      #'--with-libtiff=internal',
      $with_cpp11,
      $with_local,
      @automated_rig_config_args,
      #'--without-pg',
      '--without-ogdi',
      '--with-mongocxxv3=no',
      $^O eq 'MSWin32' ? '--without-hdf5' : (),  #  issues with strawberry perl hdf5 dlls
    );
    my @ld_flags;
    my @ld_lib_path;
    #  system installs won't necessarily have dist dirs
    if (Alien::proj->install_type eq 'share') {
      push @with_args, '--with-proj=' . Alien::proj->dist_dir;
    }
    if (Alien::libtiff->install_type eq 'share') {
      push @with_args, '--with-libtiff=' . Alien::libtiff->dist_dir;
    }
    if ($have_spatialite && $have_spatialite->install_type eq 'share') {
      push @with_args, '--with-spatialite=' . $have_spatialite->dist_dir;
    }
    #  get dynamic proj extra deps, one day will not be needed
    push @with_args,
      q{--with-proj-extra-lib-for-test="} . Alien::proj->libs . q{"};
    if (Alien::sqlite->install_type eq 'share') {
      push @with_args, '--with-sqlite3=' . Alien::sqlite->dist_dir;
      #push @ld_flags, 'LDFLAGS=' . Alien::sqlite->libs;
      #'-L/home/libraries/intel/sqlite/lib -lsqlite3'
    }
    if (Alien::geos::af->install_type eq 'share') {
      push @with_args,
          '--with-geos='
        . Alien::geos::af->bin_dir
        . '/geos-config';
      #  build weirdness on linux with geos under cmake
      if (!$on_windows) {
        push @ld_lib_path, Alien::geos::af->dist_dir . '/lib';
      }
    }
    #  an attempt to get hdf5 support under strawberry,
    #  but which did not work so now it is turned off
    #if ($^O eq 'MSWin32' && !$ENV{HDF5_LIBS}) {
    #  my $c_dir = path ($^X)->parent->parent->parent->child('c');
    #  if ($c_dir->exists) {
    #    #$ENV{HDF5_LIBS} = "-L$c_dir/lib -lhdf5";
    #    #warn 'adding HDF5_LIBS var: ' . $ENV{HDF5_LIBS};
    #    push @with_args, '--without-hdf5';
    #    #push @ld_flags, "-L$c_dir/lib -lhdf5";
    #  }
    #}
    if ($^O eq 'MSWin32') {
      unshift @with_args, '--with-hide-internal-symbols=yes';
      push @with_args, '--without-gnm';
      #  should add an env flag for this
      push @with_args, '--with-curl=no';
    }    
    
    
    my $config_args = $ENV{ALIEN_GDAL_CONFIG_ARGS} // '';
    $config_args =~ s/[^-\s\w,=+]//g;  #  overkill?
    $config_args = join (' ', @with_args) . " $config_args";
  
    if (versioncmp ($gdal_version, '3.1.1') < 0) {
      meta->before_hook( build => \&patch_makefile_long_line );
    }
    
    meta->around_hook( build => \&remove_gitfw_from_path );
    plugin 'PkgConfig::PPWrapper';

    #meta->before_hook( build => \&cleanup_ldflags );
    
    my $drivers_to_disable = join ' ', get_drivers_to_disable();
    ## --with-curl=no
    #  not sure this is needed, or if it is to deal with a cirrus quirk 
    #local $ENV{LDFLAGS} = join ' ', @ld_flags;
    #  hack
    push @LD_LIBRARY_PATH, @ld_lib_path;
    build [
      #\&update_pkg_conf_path,
      \&cleanup_ldflags,
      "%{configure} $config_args $drivers_to_disable $build_static",
      \&pause,
      $make_cmd,
      \&patch_pkgconfig,
      $make_inst_cmd,
    ];
  }
};

#  we can get dangling -L values
#  bandaid until we find the source
sub cleanup_ldflags {
    my ($build, @args) = @_;

    if ($ENV{LDFLAGS} =~ /\s*-L\s*$/) {
        $build->log("Trimming trailing -L from $ENV{LDFLAGS}");
        $ENV{LDFLAGS} =~ s/\s*-L\s*$//;
    }

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


sub update_makefile {
    return;  #  should not be needed now
    return if !$on_windows;
    system ($^X, '-p', '-i.bak', q{-e"s/\$\(GDAL_ROOT\)/\./"}, "GNUmakefile");
}

sub update_pkg_conf_path {
    return if !$on_windows;
    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;
    say $ENV{PKG_CONFIG_PATH};
    return;
}


sub patch_makefile_long_line {
    my ($build) = @_;

    #return if !$on_windows;
    
    my $target = "GNUmakefile";
    $build->log ("Patching $target to cope with long lines");
    #my $target_line = quotemeta q{$(LD) $(LDFLAGS) $(LIBS) -o $@ $(sort $(wildcard $(GDAL_OBJ:.o=.lo))) \ };
    
    my $text_target1 = '$(LIBGDAL):	$(GDAL_OBJ:.o=.lo)';
    my $x = quotemeta $text_target1;
    my $qr_target1 = qr /^$x/;
    my $new_contents1 = <<'NEW_CONTENTS'
SORTED  := $(sort $(wildcard $(GDAL_OBJ:.o=.lo)))
NSORTED := $(words $(SORTED))
#  mid left and right indices
MIDL := $(shell echo $$(( $(NSORTED) / 2 )) )
MIDR := $(shell echo $$(( $(MIDL) + 1 )) )
NEW_CONTENTS
  ;
    my $qr_target2 = qr /sort .+wildcard/;
    my $new_contents2 = << 'NEW_CONTENTS'
$(LD) $(LDFLAGS) $(LIBS) -o $@ \
$(wordlist 1,$(MIDL),$(SORTED)) \
$(wordlist $(MIDR),$(NSORTED),$(SORTED)) \
NEW_CONTENTS
  ;
    $new_contents2 =~ s/^/\t/gms;
    open my $fh , '<', $target
      or die "Could not open GNUmakefile for reading, $!";
    my $file_contents;
    
    while (my $line = <$fh>) {
        if ($line =~ $qr_target1) {
            $file_contents .= $new_contents1;
        }
        elsif ($line =~ $qr_target2) {
            $line = $new_contents2;
        };
        $file_contents .= $line;
    }
    $fh->close;
    rename $target, "$target.bak";
    open my $ofh, '>', $target
      or die "Could not open $target for writing, $!";
    print {$ofh} $file_contents;
    return;
}

#  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 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 patch_pkgconfig {
    #my $gdal_config_file = 'bin/gdal-config';
    #my $pkg_config_file  = 'lib/pkgconfig/gdal.pc';
    use File::Find::Rule;
    my @gdal_configs
      = File::Find::Rule->file()
                        ->name( 'gdal-config' )
                        ->in( '.' );
    my @pkg_configs
      = File::Find::Rule->file()
                        ->name( 'gdal.pc' )
                        ->in( '.' );
    say 'gdal-configs: ' . join ' ', @gdal_configs;
    say 'pkg-configs:  ' . join ' ', @pkg_configs;
    
    return if !@gdal_configs || !@pkg_configs;
    
    open my $gc_fh, '<', $gdal_configs[0] or die $!;
    my $dep_libs = '';
    while (defined (my $line = <$gc_fh>)) {
        if ($line =~ /CONFIG_DEP_LIBS=(.+)$/) {
            $dep_libs = $1;
            last;
        }
    }
    close $gc_fh;

    #  trim quotes (could do in prev check, but...)
    $dep_libs =~ s/^\"//;
    $dep_libs =~ s/\"$//;
    
    open my $pk_fh, '<', $pkg_configs[0] or die $!;
    my @pkg_conf;
    while (defined (my $line = <$pk_fh>)) {
        push @pkg_conf, $line;
    }
    close $pk_fh;

    #  change all (we should be more nuanced and do only the one that matters)
    foreach my $pkg_config_file (@pkg_configs) {
        say "Adding gdal dep_libs to $pkg_config_file";
        #  now add the dep libs to the pkg_conf file
        open $pk_fh, '>', $pkg_config_file or die $!;
        foreach my $line (@pkg_conf) {
            if ($line =~ /^CONFIG_INST_LIBS/) {
                chomp $line;
                $line .= " $dep_libs\n";
            }
            print {$pk_fh} $line;
        }
        close $pk_fh;
    }
}

sub get_gdal_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;
}


sub get_drivers_to_disable {
  #  some of these are already disabled in recent versions of GDAL
  my @drivers = qw /
    e00grid  jdem     xpm       grib
    zmap     adrg     arg       blx
    bt       cals     ceos      coasp
    cosar    ctg      dimap     elas
    fit      gff      gsg       gxf
    hf2      iris     lan       leveller
    mrf      msgn     northwood prf
    rmf      rs2      sgi       sigdem
    stacta   terragen til       rik
    ozi      webp
    
    aeronavfaa arcgen   bna    couchdb
    geomedia   gtm      htf    openair
    cloudant   rec      sdts   sua
    walk       tiger    edigeo
    geoconcept georss   gmt    jml
    s57        wasp     vdv
  /;
  my @args = map {"--disable-driver-$_"} @drivers;
  return wantarray ? @args : \@args;
}