require File.expand_path('../../../spec_helper', __FILE__)
$extmk = false

require 'rbconfig'
require 'fileutils'
require 'tmpdir'

OBJDIR ||= File.expand_path("../../../ext/#{RUBY_NAME}/#{RUBY_VERSION}", __FILE__)
FileUtils.makedirs(OBJDIR)

def extension_path
  File.expand_path("../ext", __FILE__)
end

def object_path
  OBJDIR
end

def compile_extension(name)
  preloadenv = RbConfig::CONFIG["PRELOADENV"] || "LD_PRELOAD"
  preload, ENV[preloadenv] = ENV[preloadenv], nil if preloadenv

  path = extension_path
  objdir = object_path

  # TODO use rakelib/ext_helper.rb?
  arch_hdrdir = nil
  ruby_hdrdir = nil

  if RUBY_NAME == 'rbx'
    hdrdir = RbConfig::CONFIG["rubyhdrdir"]
  elsif RUBY_NAME =~ /^ruby/
    if hdrdir = RbConfig::CONFIG["rubyhdrdir"]
      arch_hdrdir = RbConfig::CONFIG["rubyarchhdrdir"] ||
                    File.join(hdrdir, RbConfig::CONFIG["arch"])
      ruby_hdrdir = File.join hdrdir, "ruby"
    else
      hdrdir = RbConfig::CONFIG["archdir"]
    end
  elsif RUBY_NAME == 'jruby'
    require 'mkmf'
    hdrdir = $hdrdir
  elsif RUBY_NAME == "maglev"
    require 'mkmf'
    hdrdir = $hdrdir
  elsif RUBY_NAME == 'jruby+truffle'
    return compile_jruby_truffle_extconf_make(name, path, objdir)
  else
    raise "Don't know how to build C extensions with #{RUBY_NAME}"
  end

  ext       = "#{name}_spec"
  source    = File.join(path, "#{ext}.c")
  obj       = File.join(objdir, "#{ext}.#{RbConfig::CONFIG['OBJEXT']}")
  lib       = File.join(objdir, "#{ext}.#{RbConfig::CONFIG['DLEXT']}")

  ruby_header     = File.join(hdrdir, "ruby.h")
  rubyspec_header = File.join(path, "rubyspec.h")

  return lib if File.exist?(lib) and File.mtime(lib) > File.mtime(source) and
                File.mtime(lib) > File.mtime(ruby_header) and
                File.mtime(lib) > File.mtime(rubyspec_header) and
                true            # sentinel

  # avoid problems where compilation failed but previous shlib exists
  File.delete lib if File.exist? lib

  cc        = RbConfig::CONFIG["CC"]
  cflags    = (ENV["CFLAGS"] || RbConfig::CONFIG["CFLAGS"]).dup
  cflags   += " #{RbConfig::CONFIG["ARCH_FLAG"]}" if RbConfig::CONFIG["ARCH_FLAG"]
  cflags   += " #{RbConfig::CONFIG["CCDLFLAGS"]}" if RbConfig::CONFIG["CCDLFLAGS"]
  incflags  = "-I#{path} -I#{hdrdir}"
  incflags << " -I#{arch_hdrdir}" if arch_hdrdir
  incflags << " -I#{ruby_hdrdir}" if ruby_hdrdir

  output = `#{cc} #{incflags} #{cflags} -c #{source} -o #{obj}`

  unless $?.success? and File.exist?(obj)
    puts "ERROR:\n#{output}"
    puts "incflags=#{incflags}"
    puts "cflags=#{cflags}"
    raise "Unable to compile \"#{source}\""
  end

  ldshared  = RbConfig::CONFIG["LDSHARED"]
  ldshared += " #{RbConfig::CONFIG["ARCH_FLAG"]}" if RbConfig::CONFIG["ARCH_FLAG"]
  libpath   = "-L#{path}"
  libs      = RbConfig::CONFIG["LIBS"]
  dldflags  = "#{RbConfig::CONFIG["LDFLAGS"]} #{RbConfig::CONFIG["DLDFLAGS"]} #{RbConfig::CONFIG["EXTDLDFLAGS"]}"
  dldflags.sub!(/-Wl,-soname,\S+/, '')
  dldflags.sub!("$(TARGET_ENTRY)", "Init_#{ext}")

  link_cmd = "#{ldshared} #{obj} #{libpath} #{dldflags} #{libs} -o #{lib}"
  output = `#{link_cmd}`

  unless $?.success?
    puts "ERROR:\n#{link_cmd}\n#{output}"
    raise "Unable to link \"#{source}\""
  end

  lib
ensure
  ENV[preloadenv] = preload if preloadenv
end

def compile_extension_jruby_truffle(name)
  sulong_config_file = File.join(extension_path, '.jruby-cext-build.yml')
  output_file = File.join(object_path, "#{name}_spec.#{RbConfig::CONFIG['DLEXT']}")

  File.open(sulong_config_file, 'w') do |f|
    f.puts "src: #{name}_spec.c"
    f.puts "out: #{output_file}"
  end

  command = ["#{RbConfig::CONFIG['bindir']}/../tool/jt.rb", 'cextc', extension_path]
  system(*command)
  raise "Compilation of #{extension_path} failed: #{$?}\n#{command.join(' ')}" unless $?.success?

  output_file
ensure
  File.delete(sulong_config_file) if File.exist?(sulong_config_file)
end

def compile_jruby_truffle_extconf_make(name, path, objdir)
  ext       = "#{name}_spec"
  file      = "#{ext}.c"
  source    = "#{path}/#{file}"
  lib_target = "#{objdir}/#{ext}.#{RbConfig::CONFIG['DLEXT']}"
  temp_dir = Dir.mktmpdir
  begin
    copy =  "#{temp_dir}/#{file}"
    FileUtils.cp "#{path}/rubyspec.h", temp_dir
    FileUtils.cp "#{path}/jruby_truffle.h", temp_dir
    FileUtils.cp source, copy
    extconf_src = "require 'mkmf'\n" +
                  "create_makefile('#{ext}', '#{temp_dir}')"
    File.write("#{temp_dir}/extconf.rb", extconf_src)
    Dir.chdir(temp_dir) do
      system "#{RbConfig.ruby} extconf.rb"
      system "make"                                    # run make in temp dir
      FileUtils.cp "#{ext}.su", lib_target             # copy to .su file to library dir
      FileUtils.cp "#{ext}.bc", objdir                 # copy to .bc file to library dir
    end
  ensure
    FileUtils.remove_entry temp_dir
  end
  lib_target
end

def load_extension(name)
  require compile_extension(name)
rescue LoadError
  if %r{/usr/sbin/execerror ruby "\(ld 3 1 main ([/a-zA-Z0-9_\-.]+_spec\.so)"} =~ $!.message
    system('/usr/sbin/execerror', "#{RbConfig::CONFIG["bindir"]}/ruby", "(ld 3 1 main #{$1}")
  end
  raise
end

# Constants
CAPI_SIZEOF_LONG = [0].pack('l!').size
