use 5.010;
use alienfile;
use Sort::Versions;
use Path::Tiny;

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();

configure {
  requires 'File::Copy' => 0, 'File::Find::Rule' => 0;
};

my $min_target_version = '3.6.0';

plugin 'Build::MSYS';

probe sub {
    my($build) = @_;  # $build is the Alien::Build instance.
    my $version = `geos-config --version`;
    my $build_type
      = $? == 0
      && (versioncmp ($version, $min_target_version) == 1)
      ? 'system'
      : 'share';
    say "Build type is $build_type, found version " . ($version // 'n/a');
    return $build_type;
};


share {

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

  start_url 'http://download.osgeo.org/geos';
  #start_url "file://$base_dir";  #  debug
  plugin Download => (
    filter  => qr/^geos-([0-9\.]+)\.tar\.bz2$/,
    version => qr/^geos-([0-9\.]+)\.tar\.bz2$/,
  );

  my $geos_version = get_geos_version() // 'not yet defined';
  say "Downloaded version is $geos_version";
  
  plugin Extract => (format => 'tar.bz2');

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

  my $build_static = ($on_windows) ? '' : '--disable-shared';
  $build_static = '';
  $build_static = '--enable-static=no';  #  override - HUGE files if static
  #$build_static = '' if $ENV{FORCE_DYNAMIC};
  
  
  if ($^O =~ /bsd/) {
    plugin 'Build::Make' => 'gmake';
    if (-d '/usr/local') {
        $with_local = ' --with-local=/usr/local ';
    }
  }
  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';
  }
  elsif ($^O eq 'MSWin32') {
    plugin 'Build::Make' => 'gmake';
  }

  my $make_cmd = '%{make}';
  my $make_inst_cmd = '%{make} install';
  my $run_tests = $ENV{ALIEN_GEOS_ENABLE_TESTS};
  my $run_tests_bool_text = $run_tests ? 'ON' : 'OFF';

  #  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';
  }
  my $cmake_cmd = [
    '%{cmake}',
    -G => '%{cmake_generator}',
    '-DCMAKE_MAKE_PROGRAM=%{make}',
    '-DBUILD_DOCUMENTATION=NO',
    "-DGEOS_ENABLE_TESTS=$run_tests_bool_text", #  seems not be in the source tree?
    "-DBUILD_TESTING:BOOL=$run_tests_bool_text",
    #'-DCMAKE_BUILD_TYPE=Release',
    '-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true',
    '-DCMAKE_INSTALL_PREFIX:PATH=%{.install.prefix}',
    '..'
  ];
  
  #say join ' ', @$cmake_cmd;

  build [
    #\&set_rectangle_intersection,  #  disable
    #"%{configure} $with_local $with_cpp11 $build_static",
    #'echo %{make}',
    #\&pause,
    'mkdir _build',
    'cd _build',
    $cmake_cmd,
    $make_cmd,
    ($run_tests ? '%{make} test' : ()),
    #\&pause,
    $make_inst_cmd,
    \&rename_la_files,
  ];

};


gather [
  \&copy_geos_c_dll,
  \&copy_geos_so_on_bsd,
  #\&pause,
  #  get all the geos-config fields
  \&set_runtime_props_from_config,
];


#  a bit brute force
sub copy_geos_c_dll {
  my $build = shift;
  
  return unless $^O eq 'MSWin32';
  
  log 'Searching for libgeos_c.dll';

  my @files
    = File::Find::Rule->file()
                      ->name( 'libgeos_c.dll' )
                      ->in( '.' );

  foreach my $from (@files) {
    my $to = Path::Tiny->new($from)->parent->child('libgeos_c-1.dll');
    if (!$to->exists) {
      log "copying $from to $to";
      eval {File::Copy::copy ("$from", "$to")};
      log $@ if $@;
    }
  }
}

#  also a bit brute force
#  and numbering is volatile
sub copy_geos_so_on_bsd {
  my $build = shift;
  
  return unless $^O =~ /bsd/i;
  
  log 'Searching for libgeos so files to rename';

  my @renames = (
    ['libgeos_c.so.1'   => 'libgeos_c.so.17'],
    ['libgeos_c.so.1'   => 'libgeos_c.so'],
    ['libgeos.so.3.9.0' => 'libgeos-3.9.0.so'],
    ['libgeos.so.3.9.0' => 'libgeos-3.so'],
  );

  
  foreach my $names (@renames) {
    my ($from_name, $to_name) = @$names;

    my @files
      = File::Find::Rule->file()
                        ->name( $from_name )
                        ->in( '.' );
  
    foreach my $from (@files) {
      my $to = Path::Tiny->new($from)->parent->child($to_name);
      if (!$to->exists) {
        log "copying $from to $to";
        eval {File::Copy::copy ("$from", "$to")};
        log $@ if $@;
      }
    }
  }
}


sub set_rectangle_intersection {
    my ($build) = @_;
    
    #  disable as we get test failures in Biodiverse,
    #  which is probably for the same reasons this is
    #  disabled in GEOS in the first place
    return;  
    
    return if $build->install_type ne 'share';

    say 'set_rectangle_intersection: Currently in ' . getcwd();
    
    use File::Find::Rule;
    use Path::Tiny qw /path/;
    my (@files)
      = File::Find::Rule
                ->file()
                ->name( 'Geometry.cpp' )
                ->in( $base_dir );
    #  loop is brute force
    foreach my $file (@files) {
        say "Modifying file $file";
        path($file)->edit (
            sub {
              s{^//(#define USE_RECTANGLE_INTERSECTION 1)}{$1}ms;
            }
        );
    }
}

sub set_runtime_props_from_config {
    my ($build) = @_;
    
    say 'set_runtime_props_from_config: Currently in ' . getcwd();
    use File::Find::Rule;
    my ($geos_config)
      = $build->install_type eq 'share'
          ? File::Find::Rule
                ->file()
                ->name( 'geos-config' )
                ->in( getcwd() )
          : 'geos-config';
    
    my $bin_sh = '';
    if ($on_windows) {
        eval 'require Alien::MSYS';
        #$ENV{PATH} .= ';' . Alien::MSYS->msys_path;
        #say $ENV{PATH};
        $bin_sh = Alien::MSYS->msys_path . '/sh';
    }
    #else {
    #    $bin_sh = `which sh`;
    #}
    
    foreach my $flag (qw /cflags libs
                          clibs cclibs
                          ldflags includes
                          static-clibs static-cclibs/) {
        say "Calling: $bin_sh $geos_config --$flag";
        $build->runtime_prop->{$flag} = `$bin_sh $geos_config --$flag`;
        $build->runtime_prop->{$flag} =~ s/[\r\n]+$//;  #  generic chomp
        if ($on_windows) {
            #  windowsify the paths
            $build->runtime_prop->{$flag} =~ s|(?<=-[IL])/C/|C:/|i;
        }
        if ($^O =~ /bsd/i) {
            #  maybe will help?
            $build->runtime_prop->{$flag} =~ s/-\d+$//;
        }
        say "Runtime prop $flag is " . $build->runtime_prop->{$flag};
    }
}

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

}

sub pause {
    #return;  #  disable
    return if !$on_windows || $on_automated_rig;

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

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

sub get_stage_dir {
    my $h = get_alien_state_hash();
    return $h->{install}{stage};
}

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