#
# C interface wrappers for FGb
#
libfgb_handle = Libdl.dlopen(joinpath(@__DIR__, "..", "deps","libfgb.so"))
macro libfgb(symbol)
    ptr = Libdl.dlsym(libfgb_handle, symbol)
    quote
        $ptr
    end
end

unsafe_store!(
    cglobal(@libfgb(FGb_julia_info), Ptr{Void}),
    cfunction(
        info∘unsafe_string,
        Void,
        (Cstring,)
    ),
)
unsafe_store!(
    cglobal(@libfgb(FGb_julia_error), Ptr{Void}),
    cfunction(
        error∘unsafe_string,
        Void,
        (Cstring,)
    ),
)

enter_INT() = ccall(@libfgb(FGb_int_enter_INT), Void, ())
init_urgent(bytes_coeff, bytes_exponent, cargo_cult_drldrl, cargo_cult_100000, cargo_cult_0) = ccall(@libfgb(FGb_int_init_urgent), Void, (UInt32,UInt32,Cstring,UInt32,UInt32), bytes_coeff, bytes_exponent, cargo_cult_drldrl, cargo_cult_100000, cargo_cult_0)
log_output = Ref{UInt}(0)
init(cargo_cult_1, cargo_cult_1_again, cargo_cult_0, logging_file_handle) = ccall(@libfgb(FGb_int_init), Void, (UInt32,UInt32,UInt32,Ref{UInt}), cargo_cult_1, cargo_cult_1_again, cargo_cult_0, logging_file_handle)
reset_coeffs(cargo_cult_1) = ccall(@libfgb(FGb_int_reset_coeffs), Void, (UInt32,), cargo_cult_1)
reset_expos(nvars1, nvars2, varnames) = ccall(@libfgb(FGb_int_reset_expos), Void, (UInt32,UInt32,Ref{Cstring}), nvars1, nvars2, varnames)
internal_threads(n) = ccall(@libfgb(FGb_int_internal_threads), Void, (UInt32,), n)
mod_internal_threads(n) = ccall(@libfgb(FGb_internal_threads), Void, (UInt32,), n)

creat_poly(num_terms) = ccall(@libfgb(FGb_int_creat_poly), Ptr{Void}, (UInt32,), num_terms)
exp = UInt32[0,1,1,0,0]
set_expos2(poly, term, exp, explen) = ccall(@libfgb(FGb_int_set_expos2), Void, (Ptr{Void}, UInt32, Ref{UInt32},UInt32), poly, term, exp, explen)
set_coeff_gmp(poly, term, coeff) = ccall(@libfgb(FGb_int_set_coeff_gmp), Void, (Ptr{Void}, UInt32, Ref{BigInt}), poly,term,coeff)
full_sort_poly2(poly) = ccall(@libfgb(FGb_int_full_sort_poly2), Void, (Ptr{Void},), poly)

mutable struct SFGB_Comp_Desc
    _compute::UInt32   # in C, an enum
    _nb::Int32
    _force_elim::Int32
    _off::UInt32
    _index::UInt32
    _zone::UInt32
    _memory::UInt32

    _nb2::Int32
    _force_elim2::Int32
    _bk2::UInt32
    _aggressive2::Int32
    _dlim::Int32
    _skip::Int32
    SFGB_Comp_Desc() = begin
        env = new()
        env._compute=1 #FGB_COMPUTE_GBASIS
        env._nb=0
        env._force_elim=0 # if force_elim=1 then return only the result of the elimination
                          # (need to define a monomial ordering DRL(k1,k2) with k2>0 )
        env._off=0        # use to select another subset of prime numbers
        env._index=5000000 # This is is the maximal size of the matrices generated by F4
                          # you can increase this value according to your memory
        env._zone=0       # should be 0 
        env._memory=0     # should be 0 
        env
    end
end

groebner(input_basis, input_basis_length, output_basis, _mini, _elim, cputime, _bk0, _step0, _elim0, _env) = ccall(@libfgb(FGb_int_groebner), UInt32,
       (Ref{Ptr{Void}},UInt32,Ref{Ptr{Void}},UInt32,UInt32,Ref{Float64},UInt32, Int32, UInt32, Ref{SFGB_Comp_Desc}),
       input_basis, input_basis_length, output_basis, _mini, _elim, cputime, _bk0, _step0, _elim0, _env)

nb_terms(p) = ccall(@libfgb(FGb_int_nb_terms), UInt32, (Ptr{Void},), p)
export_poly_INT_gmp2(n_variables, n_terms, coefficients, exponents, p) = ccall(@libfgb(FGb_int_export_poly_INT_gmp2), UInt32, (UInt32, UInt32, Ref{Ptr{BigInt}}, Ref{UInt32}, Ptr{Void}), n_variables, n_terms, coefficients, exponents, p)

reset_memory() = ccall(@libfgb(FGb_int_reset_memory), Void, ())
exit_INT() = ccall(@libfgb(FGb_int_exit_INT), Void, ())

