using Libdl
# Check for an iconv implementation with the GNU (non-POSIX) behavior:
# EILSEQ is returned when a sequence cannot be converted to target encoding,
# instead of succeeding and only returning the number of invalid conversions
# This non-standard behavior is required to allow replacing invalid sequences
# with a user-defined character.
# Implementations with this behavior include glibc, GNU libiconv (on which Mac
# OS X's is based) and win_iconv.
function validate_iconv(lib, iconv_open, iconv_close, iconv)
    h = dlopen_e(lib)
    h == C_NULL && return false
    # Needed to check libc
    f = dlsym_e(h, iconv_open)
    f == C_NULL && return false

    cd = ccall(f, Ptr{Nothing}, (Cstring, Cstring), "ASCII", "UTF-8")
    cd == Ptr{Nothing}(-1) && return false

    s = "café"
    a = Vector{UInt8}(undef, sizeof(s))
    inbufptr = Ref{Ptr{UInt8}}(pointer(s))
    inbytesleft = Ref{Csize_t}(sizeof(s))
    outbufptr = Ref{Ptr{UInt8}}(pointer(a))
    outbytesleft = Ref{Csize_t}(length(a))
    ret = ccall(dlsym_e(h, iconv), Csize_t,
                (Ptr{Nothing}, Ptr{Ptr{UInt8}}, Ref{Csize_t}, Ptr{Ptr{UInt8}}, Ref{Csize_t}),
                cd, inbufptr, inbytesleft, outbufptr, outbytesleft)
    ccall(dlsym_e(h, iconv_close), Nothing, (Ptr{Nothing},), cd) == -1 && return false

    return ret == -1 % Csize_t && Libc.errno() == Libc.EILSEQ
end

using BinaryProvider

# Parse some basic command-line arguments
const verbose = "--verbose" in ARGS
const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr")))
products = [
    LibraryProduct(prefix, String["libiconv"], :libiconv)
]

# Download binaries from hosted location
bin_prefix = "https://github.com/JuliaStrings/IConvBuilder/releases/download/v1.15+build.3"

# Listing of files generated by BinaryBuilder:
download_info = Dict(
    Linux(:aarch64, :glibc) => ("$bin_prefix/IConv.aarch64-linux-gnu.tar.gz", "7a8d86f866ebee3218d590f74887d464c9980125a2e0c590acdcc0cfc5c84dd5"),
    Linux(:aarch64, :musl) => ("$bin_prefix/IConv.aarch64-linux-musl.tar.gz", "a37fb558319c37970e70bdc786d6bfe84f95ca27a72f6d334a1411d9c305ee2c"),
    Linux(:armv7l, :glibc, :eabihf) => ("$bin_prefix/IConv.arm-linux-gnueabihf.tar.gz", "c3e7eaf54fe3722153e29e29f1e08f7acdbe432fb1bcf8e0d72f15e710ff0c03"),
    Linux(:armv7l, :musl, :eabihf) => ("$bin_prefix/IConv.arm-linux-musleabihf.tar.gz", "200db91435875f57ea827ef69b31b6d4c11f089aacc695846699155488245b1d"),
    Linux(:i686, :glibc) => ("$bin_prefix/IConv.i686-linux-gnu.tar.gz", "c0dea5b3c6ebfc0ae0d3b3c1affe25a93bb11c5da8dd34c2a40ffb132e61e4ee"),
    Linux(:i686, :musl) => ("$bin_prefix/IConv.i686-linux-musl.tar.gz", "96cccd8a539c3ad4a70ddade4396f91015248d6fad799adcf0f79f3940d1c672"),
    Windows(:i686) => ("$bin_prefix/IConv.i686-w64-mingw32.tar.gz", "7d8b600337c4153f9a4ce2115f071aabc9a8c5565cb95cad4c6cc3dd6df17fb3"),
    Linux(:powerpc64le, :glibc) => ("$bin_prefix/IConv.powerpc64le-linux-gnu.tar.gz", "8e7edc2adbd331cb1f0a944f7eb26ed56ac884830d4d65785671c0e620be84e6"),
    MacOS(:x86_64) => ("$bin_prefix/IConv.x86_64-apple-darwin14.tar.gz", "5b585a372c272aed10823f5ea4473849396ee928f9f6b7643411138549349bf2"),
    Linux(:x86_64, :glibc) => ("$bin_prefix/IConv.x86_64-linux-gnu.tar.gz", "6f58cd9722f492aa0e519c9d91082c0b0776bd53cbff17f643172fec2c4d4347"),
    Linux(:x86_64, :musl) => ("$bin_prefix/IConv.x86_64-linux-musl.tar.gz", "c72aaabc45465fdb2b7d74df2bd4a0282cf1d2a46e55868b129003547396b69f"),
    FreeBSD(:x86_64) => ("$bin_prefix/IConv.x86_64-unknown-freebsd11.1.tar.gz", "69a5585871a61547e71cd06d32269e972f2d7ab488f82ffb852b3b5beaa83d8f"),
    Windows(:x86_64) => ("$bin_prefix/IConv.x86_64-w64-mingw32.tar.gz", "c553d5d36ad831c13572b1c06230b41471b1dea560fbc03d5213cf4304de74cf"),
)

# Detect already present libc iconv or libiconv
# (notably for Linux, Mac OS and other Unixes)
global found_iconv = false
for lib in ("libc", "libc-bin", "libiconv", "iconv")
    if lib in ("libc", "libc-bin")
        global iconv_open = :iconv_open
        global iconv_close = :iconv_close
        global iconv = :iconv
    else
        global iconv_open =  :libiconv_open
        global iconv_close = :libiconv_close
        global iconv = :libiconv
    end
    if validate_iconv(lib, iconv_open, iconv_close, iconv)
        global found_iconv = true
        write(joinpath(@__DIR__, "deps.jl"),
              """
              ## This file autogenerated by Pkg.build(\\"StringEncodings\\").
              ## Do not edit.
              const libiconv = "$lib"
              function check_deps()
                  global libiconv
                  if Libdl.dlopen_e(libiconv) == C_NULL
                      error("\$(libiconv) cannot be opened. Please re-run Pkg.build(\\"StringEncodings\\"), and restart Julia.")
                  end

              end
              """)
        break
    end
end

if !found_iconv
    iconv_open =  :libiconv_open
    iconv_close = :libiconv_close
    iconv = :libiconv
    # Install unsatisfied or updated dependencies:
    unsatisfied = any(!satisfied(p; verbose=verbose) for p in products)
    if haskey(download_info, platform_key())
        url, tarball_hash = download_info[platform_key()]
        if unsatisfied || !isinstalled(url, tarball_hash; prefix=prefix)
            # Download and install binaries
            install(url, tarball_hash; prefix=prefix, force=true, verbose=verbose)
        end
    elseif unsatisfied
        # If we don't have a BinaryProvider-compatible .tar.gz to download, complain.
        # Alternatively, you could attempt to install from a separate provider,
        # build from source or something even more ambitious here.
        error("Your platform $(triplet(platform_key())) is not supported by this package! Try installing libiconv manually.")
    end

    # Write out a deps.jl file that will contain mappings for our products
    write_deps_file(joinpath(@__DIR__, "deps.jl"), products)
end

open(joinpath(@__DIR__, "deps.jl"), "a") do io
    write(io,
          """
          const iconv_open_s = :$iconv_open
          const iconv_close_s = :$iconv_close
          const iconv_s = :$iconv
          """)
end
