use File::Basename 'basename', 'dirname';
use Carp 'confess';
use File::Temp;
use ExtUtils::ParseXS;
use ExtUtils::CBuilder;
use Config;
use SPVM::Builder::ObjectFileInfo;

use SPVM::Builder::Config::DLL;

my $config = SPVM::Builder::Config::Util::load_base_config(__FILE__);

$config = SPVM::Builder::Config::DLL->new_with_config($config);

$config->init;

if ($^O eq 'MSWin32') {
  my $dll_dir = $Config{bin};
  
  # Extract "perl542" from "libperl542.a"
  my $lib_name = $Config{libperl};
  $lib_name =~ s/^lib//;
  $lib_name =~ s/\.(a|lib)$//;
  
  # Add library directory and library name
  $config->add_lib_dir($dll_dir);
  $config->add_lib($lib_name);
  
  $config->add_lib('bcrypt');
}
elsif ($^O eq 'darwin') {
  $config->add_ldflag("-undefined dynamic_lookup");
}

my $tmp_dir;

$config->global->add_before_link_cb(sub {
  my ($link_info) = @_;
  
  my $config = $link_info->config;
  
  my $bootstrap_xs = <<'EOS';
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

int SPVMCC__main(int argc, char** argv);

MODULE = SPVMCC    PACKAGE = SPVMCC

PROTOTYPES: DISABLE
EXPORT_XSUB_SYMBOLS: ENABLE

void
main(...)
PPCODE:
  int argc = items;
  char** argv;
  Newx(argv, argc + 1, char*);
  
  for (int i = 0; i < argc; i++) {
    STRLEN len;
    char* str = SvPV(ST(i), len);
    
    Newx(argv[i], len + 1, char);
    Copy(str, argv[i], len + 1, char);
  }
  argv[argc] = NULL;
  
  int return_value = SPVMCC__main(argc, argv);
  
  for (int i = 0; i < argc; i++) {
    Safefree(argv[i]);
  }
  Safefree(argv);
  
  EXTEND(SP, 1);
  PUSHs(sv_2mortal(newSViv(return_value)));
  XSRETURN(1);

BOOT:
  /* Empty boot section to ensure the boot symbol is generated */

EOS
  
  $tmp_dir = File::Temp->newdir;
  my $output_file = $config->output_file;
  my $output_file_base = basename $output_file;
  my $file_base = $output_file_base =~ s/\.[^.]+$//r;

  my $bootstrap_xs_file = "$tmp_dir/${file_base}_bootstrap_xs.xs";
  my $bootstrap_xs_c_file = "$tmp_dir/${file_base}_bootstrap_xs.c";

  open my $bootstrap_xs_fh, '>', $bootstrap_xs_file or die "Can't open $bootstrap_xs_file: $!";
  print $bootstrap_xs_fh $bootstrap_xs;
  close $bootstrap_xs_fh;

  ExtUtils::ParseXS::process_file(
    filename => $bootstrap_xs_file,
    output   => $bootstrap_xs_c_file,
    hiertype => 1,
  );

  my $builder = ExtUtils::CBuilder->new;
  
  my $perl_inc = "$Config{installarchlib}/CORE";

  my $bootstrap_xs_object_file = $builder->compile(
    source       => $bootstrap_xs_c_file,
    include_dirs => [$perl_inc],
  );
  
  push @{$link_info->object_file_infos}, SPVM::Builder::ObjectFileInfo->new(file => $bootstrap_xs_object_file);
  
  my $xs_bootstrap_cfunc_name = 'XS_SPVMCC_main';
  
  push @{$link_info->dl_func_list}, $xs_bootstrap_cfunc_name;
});

$config->global->add_after_link_cb(sub {
  my ($link_info) = @_;
  
  my $config = $link_info->config;
  
  my $output_file = $config->output_file;
  
  my $output_dir = dirname $output_file;
  
  my $output_file_base = basename $output_file;
  
  my $perl_script_file_base = ($output_file_base =~ s/\..*$//r) . '.pl';
  
  my $perl_script_file = "$output_dir/$perl_script_file_base";
  
  open my $perl_fh, '>', $perl_script_file
    or confess("Cannot open file '$perl_script_file':$!");
  
  my $perl_script = <<"EOS";
use strict;
use warnings;
use Carp 'confess';
use DynaLoader;

my \$dl_error;

my \$dynamic_lib_file = '$output_file';

my \$dynamic_lib_libref = DynaLoader::dl_load_file(\$dynamic_lib_file);
\$dl_error = DynaLoader::dl_error();
unless (\$dynamic_lib_libref) {
  confess("Cannot load dynamic link library '\$dynamic_lib_file':\$dl_error");
}

my \$xs_bootstrap_cfunc_name = 'XS_SPVMCC_main';
my \$xs_bootstrap_cfunc_address = DynaLoader::dl_find_symbol(\$dynamic_lib_libref, \$xs_bootstrap_cfunc_name);
\$dl_error = DynaLoader::dl_error();
unless (\$xs_bootstrap_cfunc_address) {
  confess("Cannot find symbol '\$xs_bootstrap_cfunc_name' in dynamic link file '\$dynamic_lib_file':\$dl_error");
}

my \$perl_func_name = 'SPVMCC::main';

my \$xs_bootstrap_cb = DynaLoader::dl_install_xsub(\$perl_func_name, \$xs_bootstrap_cfunc_address);

my \$exit_status = \$xs_bootstrap_cb->(\$0, \@ARGV);

exit \$exit_status;

EOS
  
  print $perl_fh $perl_script;
});

$config;
