export
    saelgv,
    scard,
    scard!,
    scdecd,
    sce2c,
    sce2s,
    sce2t,
    scencd,
    scfmt,
    scpart,
    scs2e,
    sct2e,
    sctiks,
    sdiff,
    set,
    sincpt,
    spd,
    sphcyl,
    sphlat,
    sphrec,
    spk14a,
    spk14b,
    spk14e,
    spkacs,
    spkapo,
    spkapp,
    spkaps,
    spkcls,
    spkcov!,
    spkcov,
    spkcpo,
    spkcpt,
    spkcvo,
    spkcvt,
    spkez,
    spkezp,
    spkezr,
    spkgeo,
    spkgps,
    spklef,
    spkltc,
    spkobj!,
    spkobj,
    spkopa,
    spkopn,
    spkpds,
    spkpos,
    spkpvn,
    spksfs,
    spkssb,
    spksub,
    spksub!,
    spkuds,
    spkuef,
    spkw02,
    spkw03,
    spkw05,
    spkw08,
    spkw09,
    spkw10,
    spkw12,
    spkw13,
    spkw15,
    spkw17,
    spkw18,
    spkw20,
    srfc2s,
    srfcss,
    srfnrm,
    srfrec,
    srfs2c,
    srfscc,
    srfxpt,
    ssize,
    ssize!,
    stelab,
    stpool,
    str2et,
    subpnt,
    subpt,
    subpt_pl02,
    subslr,
    subsol,
    subsol_pl02,
    sumad,
    sumai,
    surfnm,
    surfpt,
    surfpv,
    swpool,
    sxform,
    szpool

const SPICE_SRF_SFNMLN = 37

"""
    saelgv(vec1, vec2)

Find semi-axis vectors of an ellipse generated by two arbitrary three-dimensional vectors.

### Arguments ###

- `vec1`, `vec2`: Two vectors used to generate an ellipse

### Output ###

- `smajor`: Semi-major axis of ellipse
- `sminor`: Semi-minor axis of ellipse

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/saelgv_c.html)
"""
function saelgv(vec1, vec2)
    @checkdims 3 vec1 vec2
    smajor = Array{SpiceDouble}(undef, 3)
    sminor = Array{SpiceDouble}(undef, 3)
    ccall((:saelgv_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          vec1, vec2, smajor, sminor)
    smajor, sminor
end

"""
    scard!(cell::SpiceCell{T}, card) where T

Set the cardinality of a cell.

### Arguments ###

- `cell`: The cell
- `card`: Cardinality of (number of elements in) the cell

### Output ###

Returns `cell` with its cardinality set to `card`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scard_c.html)
"""
function scard!(cell::SpiceCell{T}, card) where T
    data = cell.data
    GC.@preserve data begin
        ccall((:scard_c, libcspice), Cvoid, (SpiceInt, Ref{Cell{T}}), card, cell.cell)
    end
    cell
end

@deprecate scard scard!

"""
    scdecd(sc, sclkdp, lenout=128)

Convert double precision encoding of spacecraft clock time into a character representation.

### Arguments ###

- `sc`: NAIF spacecraft identification code
- `sclkdp`: Encoded representation of a spacecraft clock count
- `lenout`: Maximum allowed length of output SCLK string

### Output ###

Returns the character representation of a clock count.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scdecd_c.html)
"""
function scdecd(sc, sclkdp, lenout=128)
    sclkch = Array{SpiceChar}(undef, lenout)
    ccall((:scdecd_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, SpiceInt, Ref{SpiceChar}),
          sc, sclkdp, lenout, sclkch)
    handleerror()
    chararray_to_string(sclkch)
end

"""
    sce2c(sc, et)

Convert ephemeris seconds past J2000 (ET) to continuous encoded spacecraft clock ("ticks").
Non-integral tick values may be returned.

### Arguments ###

- `sc`: NAIF spacecraft ID code
- `et`: Ephemeris time, seconds past J2000

### Output ###

Returns SCLK, encoded as ticks since spacecraft clock start.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sce2c_c.html)
"""
function sce2c(sc, et)
    sclkdp = Ref{SpiceDouble}()
    ccall((:sce2c_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Ref{SpiceDouble}),
          sc, et, sclkdp)
    handleerror()
    sclkdp[]
end

"""
    sce2s(sc, et, lenout=128)

Convert an epoch specified as ephemeris seconds past J2000 (ET) to a character string
representation of a spacecraft clock value (SCLK).

### Arguments ###

- `sc`: NAIF spacecraft identification code
- `et`: Ephemeris time, specified as seconds past J2000
- `lenout`: Maximum allowed length of output SCLK string

### Output ###

Returns an SCLK string.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sce2s_c.html)
"""
function sce2s(sc, et, lenout=128)
    sclkch = Array{SpiceChar}(undef, lenout)
    ccall((:sce2s_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, SpiceInt, Ref{SpiceChar}),
          sc, et, lenout, sclkch)
    handleerror()
    chararray_to_string(sclkch)
end

"""
    sce2t(sc, et)

Convert ephemeris seconds past J2000 (ET) to integral encoded spacecraft clock ("ticks").
For conversion to fractional ticks, (required for C-kernel production), see the routine
[`sce2c`](@ref).

### Arguments ###

- `sc`: NAIF spacecraft ID code
- `et`: Ephemeris time, seconds past J2000

### Output ###

Returns SCLK, encoded as ticks since spacecraft clock start.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sce2c_t.html)
"""
function sce2t(sc, et)
    sclkdp = Ref{SpiceDouble}()
    ccall((:sce2t_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Ref{SpiceDouble}),
          sc, et, sclkdp)
    handleerror()
    sclkdp[]
end

"""
    scencd(sc, sclkch)

Encode character representation of spacecraft clock time into a double precision number.

### Arguments ###

- `sc`: NAIF spacecraft identification code
- `sclkch`: Character representation of a spacecraft clock

### Output ###

Returns the encoded representation of the clock count.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scencd_c.html)
"""
function scencd(sc, sclkch)
    sclkdp = Ref{SpiceDouble}()
    ccall((:scencd_c, libcspice), Cvoid,
          (SpiceInt, Cstring, Ref{SpiceDouble}),
          sc, sclkch, sclkdp)
    handleerror()
    sclkdp[]
end

"""
    scfmt(sc, ticks, lenout=128)

Convert encoded spacecraft clock ticks to character clock format.

### Arguments ###

- `sc`: NAIF spacecraft identification code
- `ticks`: Encoded representation of a spacecraft clock count
- `lenout`: Maximum allowed length of output string

### Output ###

Returns a character representation of a clock count.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scfmt_c.html)
"""
function scfmt(sc, ticks, lenout=128)
    clkstr = Array{SpiceChar}(undef, lenout)
    ccall((:scfmt_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, SpiceInt, Ref{SpiceChar}),
          sc, ticks, lenout, clkstr)
    handleerror()
    chararray_to_string(clkstr)
end

"""
    scpart(sc)

Get spacecraft clock partition information from a spacecraft clock kernel file.

### Arguments ###

- `sc`: NAIF spacecraft identification code

### Output ###

- `pstart`: Array of partition start times
- `pstop`: Array of partition stop times

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scpart_c.html)
"""
function scpart(sc)
    nparts = Ref{SpiceInt}()
    pstart = Array{SpiceDouble}(undef, 9999)
    pstop = Array{SpiceDouble}(undef, 9999)
    ccall((:scpart_c, libcspice), Cvoid,
          (SpiceInt, Ref{SpiceInt}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          sc, nparts, pstart, pstop)
    handleerror()
    pstart[1:nparts[]], pstop[1:nparts[]]
end

"""
    scs2e(sc, sclkch)

Convert a spacecraft clock string to ephemeris seconds past J2000 (ET).

### Arguments ###

- `sc`: NAIF integer code for a spacecraft
- `sclkch`: An SCLK string

### Output ###

Returns ephemeris time seconds past J2000.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/scs2e_c.html)
"""
function scs2e(sc, sclkch)
    et = Ref{SpiceDouble}()
    ccall((:scs2e_c, libcspice), Cvoid,
          (SpiceInt, Cstring, Ref{SpiceDouble}),
          sc, sclkch, et)
    handleerror()
    et[]
end

"""
    sct2e(sc, sclkdp)

Convert encoded spacecraft clock ("ticks") to ephemeris seconds past J2000 (ET).

### Arguments ###

- `sc`: NAIF integer code for a spacecraft
- `sclkdp`: SCLK, encoded as ticks since spacecraft clock start.

### Output ###

Returns ephemeris time seconds past J2000.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sct2e_c.html)
"""
function sct2e(sc, sclkdp)
    et = Ref{SpiceDouble}()
    ccall((:sct2e_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Ref{SpiceDouble}),
          sc, sclkdp, et)
    handleerror()
    et[]
end

"""
    sctiks(sc, clkstr)

Convert a spacecraft clock format string to number of "ticks".

### Arguments ###

- `sc`: NAIF spacecraft identification code
- `clkstr`: Character representation of a spacecraft clock

### Output ###

Returns the number of ticks represented by the clock string.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sctiks_c.html)
"""
function sctiks(sc, clkstr)
    ticks = Ref{SpiceDouble}()
    ccall((:sctiks_c, libcspice), Cvoid,
          (SpiceInt, Cstring, Ref{SpiceDouble}),
          sc, clkstr, ticks)
    handleerror()
    ticks[]
end

"""
    sdiff(a::T, b::T) where T <: SpiceCell

Compute the symmetric difference of two sets of any data type to form a third set.

### Arguments ###

- `a`: First input set
- `b`: Second input set

### Output ###

Returns a cell containing the symmetric difference of `a` and `b`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sdiff_c.html)
"""
function sdiff(a::SpiceCell{T}, b::SpiceCell{T}) where T
    size = max(a.cell.size, b.cell.size)
    if T <: SpiceChar
        length = max(a.cell.length, b.cell.length)
        out = SpiceCell{T}(size, length)
    else
        out = SpiceCell{T}(size)
    end
    a_data = a.data
    b_data = b.data
    out_data = out.data
    GC.@preserve a_data b_data out_data begin
        ccall((:sdiff_c, libcspice), Cvoid, (Ref{Cell{T}}, Ref{Cell{T}}, Ref{Cell{T}}),
              a.cell, b.cell, out.cell)
    end
    handleerror()
    out
end

"""
    set(a::T, b::T) where T <: SpiceCell

Given a relational operator, compare two sets of any data type.

### Arguments ###

- `a`: First set
- `op`: Comparison operator
- `b`: Second set

### Output ###

Returns the result of the comparison: `a (op) b`.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/set_c.html)
"""
function set(a::SpiceCell{T}, op, b::SpiceCell{T}) where T
    a_data = a.data
    b_data = b.data
    GC.@preserve a_data b_data begin
        res = ccall((:set_c, libcspice), SpiceBoolean, (Ref{Cell{T}}, Cstring, Ref{Cell{T}}),
                    a.cell, op, b.cell)
    end
    handleerror()
    Bool(res[])
end

function _shellc(array)
    array_, m, n = chararray(array)
    ccall((:shellc_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, Ref{SpiceChar}),
          m, n, array_)
    handleerror()
    array[:] .= chararray_to_string(array_, m)
end

"""
    shellc(array)

!!! warning "Deprecated"
    Use `sort!(array)` instead.
"""
shellc

@deprecate shellc(array) sort!(array)

function _shelld(array)
    n = length(array)
    ccall((:shelld_c, libcspice), Cvoid,
          (SpiceInt, Ref{SpiceDouble}),
          n, array)
    handleerror()
    array
end

"""
    shelld(array)

!!! warning "Deprecated"
    Use `sort!(array)` instead.
"""
shelld

@deprecate shelld(array) sort!(array)

function _shelli(array)
    n = length(array)
    array_ = SpiceInt.(array)
    ccall((:shelli_c, libcspice), Cvoid,
          (SpiceInt, Ref{SpiceInt}),
          n, array_)
    handleerror()
    Int.(array_)
end

"""
    shelli(array)

!!! warning "Deprecated"
    Use `sort!(array)` instead.
"""
shelli

@deprecate shelli(array) sort!(array)

"""
    sincpt(method, target, et, fixref, abcorr, obsrvr, dref, dvec)

Given an observer and a direction vector defining a ray, compute the surface intercept
of the ray on a target body at a specified epoch, optionally corrected for light time
and stellar aberration.

The surface of the target body may be represented by a triaxial ellipsoid or by
topographic data provided by DSK files.

### Arguments ###

- `method`: Computation method
- `target`: Name of target body
- `et`: Epoch in TDB seconds past J2000 TDB
- `fixref`: Body-fixed, body-centered target body frame
- `abcorr`: Aberration correction flag
- `obsrvr`: Name of observing body
- `dref`: Reference frame of ray's direction vector
- `dvec`: Ray's direction vector

### Output ###

Returns a tuple consisting of the following data or `nothing` if no intercept was found.

- `spoint`: Surface intercept point on the target body
- `trgepc`: Intercept epoch
- `srfvec`: Vector from observer to intercept point

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sincpt_c.html)
"""
function sincpt(method, target, et, fixref, abcorr, obsrvr, dref, dvec)
    @checkdims 3 dvec

    spoint = Array{SpiceDouble}(undef, 3)
    trgepc = Ref{SpiceDouble}()
    srfvec = Array{SpiceDouble}(undef, 3)
    found = Ref{SpiceBoolean}()

    ccall((:sincpt_c, libcspice), Cvoid,
          (Cstring, Cstring, SpiceDouble, Cstring, Cstring, Cstring, Cstring, Ref{SpiceDouble},
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceBoolean}),
          method, target, et, fixref, abcorr, obsrvr, dref, dvec, spoint, trgepc, srfvec, found)

    handleerror()
    Bool(found[]) || return nothing

    spoint, trgepc[], srfvec
end

"""
    spd()

Returns the number of seconds in a day.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spd_c.html)
"""
function spd()
    ccall((:spd_c, libcspice), SpiceDouble, ())
end

"""
    sphcyl(radius, colat, slon)

Converts from spherical coordinates to cylindrical coordinates.

### Arguments ###

- `radius`: Distance of point from origin
- `colat`: Polar angle (co-latitude in radians) of point
- `slon`: Azimuthal angle (longitude) of point (radians)

### Output ###

- `r`: Distance of point from Z axis
- `lon`: Angle (radians) of point from XZ plane
- `z`: Height of point above XY plane

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sphcyl_c.html)
"""
function sphcyl(radius, colat, slon)
    r = Ref{SpiceDouble}()
    lon = Ref{SpiceDouble}()
    z = Ref{SpiceDouble}()
    ccall((:sphcyl_c, libcspice), Cvoid,
          (SpiceDouble, SpiceDouble, SpiceDouble,
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          radius, colat, slon, r, lon, z)
    r[], lon[], z[]
end

"""
    sphlat(r, colat, lons)

Convert from spherical coordinates to latitudinal coordinates.

### Arguments ###

- `r`: Distance of the point from the origin
- `colat`: Angle of the point from positive z axis (radians)
- `lons`: Angle of the point from the XZ plane (radians)

### Output ###

- `radius`: Distance of a point from the origin
- `lon`: Angle of the point from the XZ plane in radians
- `lat`: Angle of the point from the XY plane in radians

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sphlat_c.html)
"""
function sphlat(r, colat, lons)
    radius = Ref{SpiceDouble}()
    lon = Ref{SpiceDouble}()
    lat = Ref{SpiceDouble}()
    ccall((:sphcyl_c, libcspice), Cvoid,
          (SpiceDouble, SpiceDouble, SpiceDouble,
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          r, colat, lons, radius, lon, lat)
    radius[], lon[], lat[]
end

"""
    sphrec(r, colat, lon)

Convert from spherical coordinates to rectangular coordinates.

### Arguments ###

- `r`: Distance of a point from the origin
- `colat`: Angle of the point from the Z-axis in radians
- `lon`: Angle of the point from the XZ plane in radians

### Output ###

Returns the rectangular coordinates of the point.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sphrec_c.html)
"""
function sphrec(r, colat, lon)
    rectan = Array{SpiceDouble}(undef, 3)
    ccall((:sphrec_c, libcspice), Cvoid,
          (SpiceDouble, SpiceDouble, SpiceDouble, Ref{SpiceDouble}),
          r, colat, lon, rectan)
    rectan
end

"""
    spk14a(handle, ncsets, coeffs, epochs)

Add data to a type 14 SPK segment associated with `handle`.
See also [`spk14b`](@ref) and [`spk14e`](@ref).

### Arguments ###

- `handle`: The handle of an SPK file open for writing
- `ncsets`: The number of coefficient sets and epochs
- `coeffs`: The collection of coefficient sets
- `epochs`: The epochs associated with the coefficient sets

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spk14a_c.html)
"""
function spk14a(handle, ncsets, coeffs, epochs)
    ccall((:spk14a_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, ncsets, coeffs, epochs)
    handleerror()
end

"""
    spk14b(handle, segid, body, center, frame, first, last, chbdeg)

Begin a type 14 SPK segment in the SPK file associated with `handle`.
See also [`spk14a`](@ref) and [`spk14e`](@ref).

### Arguments ###

- `handle`: The handle of an SPK file open for writing
- `segid`: The string to use for segment identifier
- `body`: The NAIF ID code for the body of the segment
- `center`: The center of motion for body
- `frame`: The reference frame for this segment
- `first`: The first epoch for which the segment is valid
- `last`: The last epoch for which the segment is valid
- `chbdeg`: The degree of the Chebyshev Polynomial used

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spk14b_c.html)
"""
function spk14b(handle, segid, body, center, frame, first, last, chbdeg)
    ccall((:spk14b_c, libcspice), Cvoid,
          (SpiceInt, Cstring, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, SpiceInt),
          handle, segid, body, center, frame, first, last, chbdeg)
    handleerror()
end

"""
    spk14e(handle)

End the type 14 SPK segment currently being written to the SPK file associated with `handle`.
See also [`spk14a`](@ref) and [`spk14b`](@ref).

### Arguments ###

- `handle`: The handle of an SPK file open for writing

### Output ###

Returns the handle of the SPK file.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spk14e_c.html)
"""
function spk14e(handle)
    ccall((:spk14e_c, libcspice), Cvoid, (SpiceInt,), handle)
    handle
end

"""
    spkacs(targ, et, ref, abcorr, obs, starg, lt, dlt)

Return the state (position and velocity) of a target body relative to an observer, optionally
corrected for light time and stellar aberration, expressed relative to an inertial reference frame.

### Arguments ###

- `targ`: Target body
- `et`: Observer epoch
- `ref`: Inertial reference frame of output state
- `abcorr`: Aberration correction flag
- `obs`: Observer

### Output ###

- `starg`: State of target
- `lt`: One way light time between observer and target
- `dlt`: Derivative of light time with respect to time

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkacs_c.html)
"""
function spkacs(targ, et, ref, abcorr, obs)
    starg = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    dlt = Ref{SpiceDouble}()
    ccall((:spkacs_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Cstring, SpiceInt,
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, obs, starg, lt, dlt)
    handleerror()
    starg, lt[], dlt[]
end

"""
    spkapo(targ, et, ref, sobs, abcorr)

Return the position of a target body relative to an observer, optionally corrected for light time
and stellar aberration.

### Arguments ###

- `targ`: Target body
- `et`: Observer epoch
- `ref`: Inertial reference frame of observer's state
- `sobs`: State of observer wrt. solar system barycenter
- `abcorr`: Aberration correction flag

### Output ###

- `ptarg`: Position of target
- `lt`: One way light time between observer and target

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkapo_c.html)
"""
function spkapo(targ, et, ref, sobs, abcorr)
    @checkdims 6 sobs
    ptarg = Array{SpiceDouble}(undef, 3)
    lt = Ref{SpiceDouble}()
    ccall((:spkapo_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Ref{SpiceDouble}, Cstring,
           Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, sobs, abcorr, ptarg, lt)
    handleerror()
    ptarg, lt[]
end

"""
    spkaps(targ, et, ref, abcorr, stobs, accobs)

Given the state and acceleration of an observer relative to the solar system barycenter, return the
state (position and velocity) of a target body relative to the observer, optionally corrected for
light time and stellar aberration. All input and output vectors are expressed relative to an
inertial reference frame.

Users normally should call the high-level API routines [`spkezr`](@ref) or [`spkez`](@ref) rather
than this routine.

### Arguments ###

- `targ`: Target body.
- `et`: Observer epoch.
- `ref`: Inertial reference frame of output state.
- `abcorr`: Aberration correction flag.
- `stobs`: State of the observer relative to the SSB.
- `accobs`: Acceleration of the observer relative to the SSB.

### Output ###

- `starg`: State of target.
- `lt`: One way light time between observer and target.
- `dlt`: Derivative of light time with respect to time.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkaps_c.html)
"""
function spkaps(targ, et, ref, abcorr, stobs, accobs)
    @checkdims 6 stobs accobs
    starg = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    dlt = Ref{SpiceDouble}()
    ccall((:spkaps_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Cstring, Ref{SpiceDouble}, Ref{SpiceDouble},
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, stobs, accobs, starg, lt, dlt)
    handleerror()
    starg, lt[], dlt[]
end

@deprecate spkapp spkaps

"""
    spkcls(handle)

Close an open SPK file.

### Arguments ###

- `handle`: Handle of the SPK file to be closed

### Output ###

Returns the handle of the closed file.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcls_c.html)
"""
function spkcls(handle)
    ccall((:spkcls_c, libcspice), Cvoid, (SpiceInt,), handle)
    handleerror()
    handle
end

"""
    spkcov!(cover, spk, idcode)

Find the coverage window for a specified ephemeris object in a specified SPK file.

### Arguments ###

- `cover`: Window giving coverage in `spk` for `idcode`
- `spk`: Name of the SPK file
- `idcode`: ID code of ephemeris object

### Output ###

Returns the extended coverage window.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcov_c.html)
"""
function spkcov!(cover, spk, idcode)
    data = cover.data
    GC.@preserve data begin
        ccall((:spkcov_c, libcspice), Cvoid,
              (Cstring, SpiceInt, Ref{Cell{SpiceDouble}}),
              spk, idcode, cover.cell)
    end
    handleerror()
    cover
end

@deprecate spkcov spkcov!

"""
    spkcpo(target, et, outref, refloc, abcorr, obspos, obsctr, obsref)

Return the state of a specified target relative to an "observer,"
where the observer has constant position in a specified reference
frame. The observer's position is provided by the calling program
rather than by loaded SPK files.

### Arguments ###

- `target`: Name of target ephemeris object
- `et`: Observation epoch
- `outref`: Reference frame of output state
- `refloc`: Output reference frame evaluation locus
- `abcorr`: Aberration correction
- `obspos`: Observer position relative to center of motion
- `obsctr`: Center of motion of observer
- `obsref`: Frame of observer position

### Output ###

- `state`: State of target with respect to observer
- `lt`: One way light time between target and observer

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcpo_c.html)
"""
function spkcpo(target, et, outref, refloc, abcorr, obspos, obsctr, obsref)
    @checkdims 3 obspos
    state = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkcpo_c, libcspice), Cvoid,
          (Cstring, SpiceDouble, Cstring, Cstring, Cstring, Ref{SpiceDouble}, Cstring, Cstring,
           Ref{SpiceDouble}, Ref{SpiceDouble}),
          target, et, outref, refloc, abcorr, obspos, obsctr, obsref, state, lt)
    handleerror()
    state, lt[]
end

"""
    spkcpt(trgpos, trgctr, trgref, et, outref, refloc, abcorr, obsrvr)

Return the state, relative to a specified observer, of a target having constant position in a
specified reference frame. The target's position is provided by the calling program rather than by
loaded SPK files.

### Arguments ###

- `trgpos`: Target position relative to center of motion
- `trgctr`: Center of motion of target
- `trgref`: Frame of target position
- `et`: Observation epoch
- `outref`: Reference frame of output state
- `refloc`: Output reference frame evaluation locus
- `abcorr`: Aberration correction
- `obsrvr`: Name of observing ephemeris object

### Output ###

- `state`: State of target with respect to observer
- `lt`: One way light time between target and observer

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcpt_c.html)
"""
function spkcpt(trgpos, trgctr, trgref, et, outref, refloc, abcorr, obsrvr)
    @checkdims 3 trgpos
    state = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkcpt_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Cstring, Cstring, SpiceDouble, Cstring, Cstring, Cstring, Cstring,
           Ref{SpiceDouble}, Ref{SpiceDouble}),
          trgpos, trgctr, trgref, et, outref, refloc, abcorr, obsrvr, state, lt)
    handleerror()
    state, lt[]
end

"""
    spkcvo(target, et, outref, refloc, abcorr, obssta, obsepc, obsctr, obsref)

Return the state of a specified target relative to an "observer," where the observer has constant
velocity in a specified reference frame.  The observer's state is provided by the calling program
rather than by loaded SPK files.

### Arguments ###

- `target`: Name of target ephemeris object
- `et`: Observation epoch
- `outref`: Reference frame of output state
- `refloc`: Output reference frame evaluation locus
- `abcorr`: Aberration correction
- `obssta`: Observer state relative to center of motion
- `obsepc`: Epoch of observer state
- `obsctr`: Center of motion of observer
- `obsref`: Frame of observer state

### Output ###

- `state`: State of target with respect to observer
- `lt`: One way light time between target and observer

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcvo_c.html)
"""
function spkcvo(target, et, outref, refloc, abcorr, obssta, obsepc, obsctr, obsref)
    @checkdims 6 obssta
    state = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkcvo_c, libcspice), Cvoid,
          (Cstring, SpiceDouble, Cstring, Cstring, Cstring, Ref{SpiceDouble}, SpiceDouble, Cstring,
           Cstring, Ref{SpiceDouble}, Ref{SpiceDouble}),
          target, et, outref, refloc, abcorr, obssta, obsepc, obsctr, obsref, state, lt)
    handleerror()
    state, lt[]
end

"""
    spkcvt(trgsta, trgepc, trgctr, trgref, et, outref, refloc, abcorr, obsrvr)

Return the state, relative to a specified observer, of a target having constant velocity in a
specified reference frame. The target's state is provided by the calling program rather than by
loaded SPK files.

### Arguments ###

- `trgsta`: Target state relative to center of motion
- `trgepc`: Epoch of target state
- `trgctr`: Center of motion of target
- `trgref`: Frame of target state
- `et`: Observation epoch
- `outref`: Reference frame of output state
- `refloc`: Output reference frame evaluation locus
- `abcorr`: Aberration correction
- `obsrvr`: Name of observing ephemeris object

### Output ###

- `state`: State of target with respect to observer
- `lt`: One way light time between target and observer

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkcvt_c.html)
"""
function spkcvt(trgsta, trgepc, trgctr, trgref, et, outref, refloc, abcorr, obsrvr)
    @checkdims 6 trgsta
    state = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkcvt_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, SpiceDouble, Cstring, Cstring, SpiceDouble, Cstring, Cstring, Cstring,
           Cstring, Ref{SpiceDouble}, Ref{SpiceDouble}),
          trgsta, trgepc, trgctr, trgref, et, outref, refloc, abcorr, obsrvr, state, lt)
    handleerror()
    state, lt[]
end

"""
    spkez(targ, et, ref, abcorr, obs)

Return the state (position and velocity) of a target body relative to an observing body,
optionally corrected for light time (planetary aberration) and stellar aberration.

### Arguments ###

- `targ`: Target body
- `et`: Observer epoch
- `ref`: Reference frame of output state vector
- `abcorr`: Aberration correction flag
- `obs`: Observing body

### Output ###

- `starg`: State of target
- `lt`: One way light time between observer and target

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkez_c.html)
"""
function spkez(targ, et, ref, abcorr, obs)
    starg = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkez_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Cstring, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, obs, starg, lt)
    handleerror()
    starg, lt[]
end

"""
    spkezp(targ, et, ref, abcorr, obs)

Return the position of a target body relative to an observing body,
optionally corrected for light time (planetary aberration) and stellar aberration.

### Arguments ###

- `targ`: Target body
- `et`: Observer epoch
- `ref`: Reference frame of output state vector
- `abcorr`: Aberration correction flag
- `obs`: Observing body

### Output ###

- `ptarg`: Position of target
- `lt`: One way light time between observer and target

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezp_c.html)
"""
function spkezp(targ, et, ref, abcorr, obs)
    ptarg = Array{SpiceDouble}(undef, 3)
    lt = Ref{SpiceDouble}()
    ccall((:spkezp_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Cstring, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, obs, ptarg, lt)
    handleerror()
    ptarg, lt[]
end

"""
    spkezr(targ, et, ref, abcorr, obs)

Return the state (position and velocity) of a target body relative to an observing body, optionally
corrected for light time (planetary aberration) and stellar aberration.

### Arguments ###

- `targ`: Target body name
- `et`: Observer epoch
- `ref`: Reference frame of output state vector
- `abcorr`: Aberration correction flag
- `obs`: Observing body name

### Output ###

- `starg`: State of target
- `lt`: One way light time between observer and target

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkezr_c.html)
"""
function spkezr(targ, et, ref, abcorr, obs)
    starg = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkezr_c, libcspice), Cvoid,
          (Cstring, SpiceDouble, Cstring, Cstring, Cstring, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, obs, starg, lt)
    handleerror()
    starg, lt[]
end

"""
    spkgeo(targ, et, ref, obs)

Compute the geometric state (position and velocity) of a target body relative to an observing body.

### Arguments ###

- `targ`: Target body.
- `et`: Target epoch.
- `ref`: Target reference frame.
- `obs`: Observing body.

### Output ###

- `state`: State of target.
- `lt`: Light time.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkgeo_c.html)
"""
function spkgeo(targ, et, ref, obs)
    state = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    ccall((:spkgeo_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, obs, state, lt)
    handleerror()
    state, lt[]
end

"""
    spkgps(targ, et, ref, obs)

Compute the geometric position of a target body relative to an observing body.

### Arguments ###

- `targ`: Target body
- `et`: Target epoch
- `ref`: Target reference frame
- `obs`: Observing body

### Output ###

- `pos`: Position of target
- `lt`: Light time

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkgps_c.html)
"""
function spkgps(targ, et, ref, obs)
    pos = Array{SpiceDouble}(undef, 3)
    lt = Ref{SpiceDouble}()
    ccall((:spkgps_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, obs, pos, lt)
    handleerror()
    pos, lt[]
end

"""
    spklef(fname)

Load an ephemeris file for use by the readers. Return that file's handle, to be used by other
SPK routines to refer to the file.

### Arguments ###

- `fname`: Name of the file to be loaded

### Output ###

- `handle`: Loaded file's handle

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spklef_c.html)
"""
function spklef(fname)
    handle = Ref{SpiceInt}()
    ccall((:spklef_c, libcspice), Cvoid, (Cstring, Ref{SpiceInt}), fname, handle)
    handleerror()
    handle[]
end

"""
    spkltc(targ, et, ref, abcorr, stobs)

Return the state (position and velocity) of a target body relative to an observer, optionally
corrected for light time, expressed relative to an inertial reference frame.

### Arguments ###

- `targ`: Target body
- `et`: Observer epoch
- `ref`: Inertial reference frame of output state
- `abcorr`: Aberration correction flag
- `stobs`: State of the observer relative to the SSB

### Output ###

- `starg`: State of target
- `lt`: One way light time between observer and target
- `dlt`: Derivative of light time with respect to time

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkltc_c.html)
"""
function spkltc(targ, et, ref, abcorr, stobs)
    @checkdims 6 stobs
    starg = Array{SpiceDouble}(undef, 6)
    lt = Ref{SpiceDouble}()
    dlt = Ref{SpiceDouble}()
    ccall((:spkltc_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Cstring, Ref{SpiceDouble},
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, stobs, starg, lt, dlt)
    handleerror()
    starg, lt[], dlt[]
end

"""
    spkobj!(ids, spk)

Find the set of ID codes of all objects in a specified SPK file.

### Arguments ###

- `ids`: A preallocated set of ID codes of objects in SPK file
- `spk`: Name of the SPK file

### Output ###

Returns the set of id codes.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkobj_c.html)
"""
function spkobj!(ids, spk)
    data = ids.data
    GC.@preserve data begin
        ccall((:spkobj_c, libcspice), Cvoid,
              (Cstring, Ref{Cell{SpiceInt}}),
              spk, ids.cell)
    end
    handleerror()
    ids
end

@deprecate spkobj spkobj!

"""
    spkopa(file)

Open an existing SPK file for subsequent write.

### Arguments ###

- `file`: The name of an existing SPK file

### Output ###

Returns a handle attached to the SPK file opened to append.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkopa_c.html)
"""
function spkopa(file)
    handle = Ref{SpiceInt}()
    ccall((:spkopa_c, libcspice), Cvoid, (Cstring, Ref{SpiceInt}), file, handle)
    handleerror()
    handle[]
end

"""
    spkopn(name, ifname="", ncomch=0)

Create a new SPK file, returning the handle of the opened file.

### Arguments ###

- `name`: The name of the new SPK file to be created
- `ifname`: The internal filename for the SPK file (default: "")
- `ncomch`: The number of characters to reserve for comments (default: 0)

### Output ###

Returns the handle of the opened SPK file.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkopn_c.html)
"""
function spkopn(name, ifname="", ncomch=0)
    handle = Ref{SpiceInt}()
    ccall((:spkopn_c, libcspice), Cvoid,
          (Cstring, Cstring, Cint, Ref{Cint}),
          name, ifname, ncomch, handle)
    handleerror()
    handle[]
end

"""
    spkpds(body, center, frame, typ, first, last)

Perform routine error checks and if all checks pass, pack the descriptor for an SPK segment.

### Arguments ###

- `body`: The NAIF ID code for the body of the segment
- `center`: The center of motion for body
- `frame`: The frame for this segment
- `type`: The type of SPK segment to create
- `first`: The first epoch for which the segment is valid
- `last`: The last  epoch for which the segment is valid

### Output ###

Returns an SPK segment descriptor.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpds_c.html)
"""
function spkpds(body, center, frame, typ, first, last)
    descr = Array{SpiceDouble}(undef, 5)
    ccall((:spkpds_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, Cstring, SpiceInt, SpiceDouble, SpiceDouble, Ref{SpiceDouble}),
          body, center, frame, typ, first, last, descr)
    handleerror()
    descr
end

"""
    spkpos(targ, et, ref, abcorr, obs)

Return the position of a target body relative to an observing body, optionally corrected for
light time (planetary aberration) and stellar aberration.

### Arguments ###

- `targ`: Target body name
- `et`: Observer epoch
- `ref`: Reference frame of output position vector
- `abcorr`: Aberration correction flag
- `obs`: Observing body name

### Output ###

- `ptarg`: Position of target
- `lt`: One way light time between observer and target

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpos_c.html)
"""
function spkpos(targ, et, ref, abcorr, obs)
    starg = Array{SpiceDouble}(undef, 3)
    lt = Ref{SpiceDouble}()
    ccall((:spkpos_c, libcspice), Cvoid,
          (Cstring, SpiceDouble, Cstring, Cstring, Cstring, Ref{SpiceDouble}, Ref{SpiceDouble}),
          targ, et, ref, abcorr, obs, starg, lt)
    handleerror()
    starg, lt[]
end

"""
    spkpvn(handle, descr, et)

For a specified SPK segment and time, return the state (position and velocity) of the segment's
target body relative to its center of motion.

### Arguments ###

- `handle`: File handle
- `descr`: Segment descriptor
- `et`: Evaluation epoch

### Output ###

- `ref`: Segment reference frame ID code
- `state`: Output state vector
- `center`: Center of state

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkpvn_c.html)
"""
function spkpvn(handle, descr, et)
    @checkdims 5 descr
    ref = Ref{SpiceInt}()
    state = Array{SpiceDouble}(undef, 6)
    center = Ref{SpiceInt}()
    ccall((:spkpvn_c, libcspice), Cvoid,
          (SpiceInt, Ref{SpiceDouble}, SpiceDouble,
           Ref{SpiceInt}, Ref{SpiceDouble}, Ref{SpiceInt}),
          handle, descr, et, ref, state, center)
    handleerror()
    ref[], state, center[]
end

"""
    spksfs(body, et)

Search through loaded SPK files to find the highest-priority segment applicable to the body and
time specified.

### Arguments ###

- `body`: Body ID
- `et`: Ephemeris time

### Output ###

Returns `nothing` if no segment was found or a tuple consisting of:

- `handle`: Handle of file containing the applicable segment
- `descr`: Descriptor of the applicable segment
- `ident`: Identifier of the applicable segment

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spksfs_c.html)
"""
function spksfs(body, et)
    idlen = 41
    handle = Ref{SpiceInt}()
    descr = Array{SpiceDouble}(undef, 5)
    ident = Array{SpiceChar}(undef, idlen)
    found = Ref{SpiceBoolean}()
    ccall((:spksfs_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, SpiceInt,
           Ref{SpiceInt}, Ref{SpiceDouble}, Ref{SpiceChar}, Ref{SpiceBoolean}),
          body, et, idlen, handle, descr, ident, found)
    handleerror()
    Bool(found[]) || return nothing
    handle[], descr, chararray_to_string(ident)
end

"""
    spkssb(targ, et, ref)

Return the state (position and velocity) of a target body relative to the solar system barycenter.

### Arguments ###

- `targ`: Target body
- `et`: Target epoch
- `ref`: Target reference frame

### Output ###

Returns the state of target.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkssb_c.html)
"""
function spkssb(targ, et, ref)
    starg = Array{SpiceDouble}(undef, 6)
    ccall((:spkssb_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, Cstring, Ref{SpiceDouble}),
          targ, et, ref, starg)
    handleerror()
    starg
end

"""
    spksub!(newh, handle, descr, ident, start, stop)

Extract a subset of the data in an SPK segment into a separate segment.

### Arguments ###

- `newh`: Handle of new segment
- `handle`: Handle of source segment
- `descr`: Descriptor of source segment
- `ident`: Identifier of source segment
- `start`: Beginning (initial epoch) of subset
- `stop`: End (final epoch) of subset

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spksub_c.html)
"""
function spksub!(newh, handle, descr, ident, start, stop)
    @checkdims 5 descr
    ccall((:spksub_c, libcspice), Cvoid,
          (SpiceInt, Ref{SpiceDouble}, Cstring, SpiceDouble, SpiceDouble, SpiceInt),
          handle, descr, ident, start, stop, newh)
    handleerror()
end

@deprecate spksub spksub!

"""
    spkuds(descr)

Unpack the contents of an SPK segment descriptor.

### Arguments ###

- `descr`: An SPK segment descriptor

### Output ###

- `body`: The NAIF ID code for the body of the segment
- `center`: The center of motion for body
- `frame`: The ID code for the frame of this segment
- `type`: The type of SPK segment
- `first`: The first epoch for which the segment is valid
- `last`: The last  epoch for which the segment is valid
- `start`: Beginning DAF address of the segment
- `stop`: Ending DAF address of the segment

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkuds_c.html)
"""
function spkuds(descr)
    @checkdims 5 descr
    body = Ref{SpiceInt}()
    center = Ref{SpiceInt}()
    frame = Ref{SpiceInt}()
    typ = Ref{SpiceInt}()
    first = Ref{SpiceDouble}()
    last = Ref{SpiceDouble}()
    start = Ref{SpiceInt}()
    stop = Ref{SpiceInt}()
    ccall((:spkuds_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceInt}, Ref{SpiceInt}, Ref{SpiceInt}, Ref{SpiceInt},
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceInt}, Ref{SpiceInt}),
          descr, body, center, frame, typ, first, last, start, stop)
    handleerror()
    Int(body[]), Int(center[]), Int(frame[]), Int(typ[]),
    first[], last[], Int(start[]), Int(stop[])
end

"""
    spkuef(handle)

Unload an ephemeris file so that it will no longer be searched by the readers.

### Arguments ###

- `handle`: Handle of file to be unloaded

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkuef_c.html)
"""
function spkuef(handle)
    ccall((:spkuef_c, libcspice), Cvoid, (SpiceInt,), handle)
end

"""
    spkw02(handle, body, center, frame, first, last, segid, intlen, cdata, btime)

Write a type 2 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `intlen`: Length of time covered by logical record
- `cdata`: Array of Chebyshev coefficients
- `btime`: Begin time of first logical record

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw02_c.html)
"""
function spkw02(handle, body, center, frame, first, last, segid, intlen, cdata, btime)
    cdata_ = array_to_cmatrix(cdata)
    m, n = size(cdata_)
    polydg = m - 1
    ccall((:spkw02_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring, SpiceDouble,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, SpiceDouble),
          handle, body, center, frame, first, last, segid, intlen,
          n, polydg, cdata_, btime)
    handleerror()
end

"""
    spkw03(handle, body, center, frame, first, last, segid, intlen, cdata, btime)

Write a type 3 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `intlen`: Length of time covered by logical record
- `cdata`: Array of Chebyshev coefficients
- `btime`: Begin time of first logical record

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw03_c.html)
"""
function spkw03(handle, body, center, frame, first, last, segid, intlen, cdata, btime)
    cdata_ = array_to_cmatrix(cdata)
    m, n = size(cdata_)
    polydg = m ÷ 6 - 1
    ccall((:spkw03_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring, SpiceDouble,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, SpiceDouble),
          handle, body, center, frame, first, last, segid, intlen,
          n, polydg, cdata_, btime)
    handleerror()
end

"""
    spkw05(handle, body, center, frame, first, last, segid, gm, states, epochs)

Write an SPK segment of type 5 given a time-ordered set of discrete states and epochs,
and the gravitational parameter of a central body.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `gm`: Gravitational parameter of central body
- `states`: States
- `epochs`: Epochs

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw05_c.html)
"""
function spkw05(handle, body, center, frame, first, last, segid, gm, states, epochs)
    n = length(states)
    @checkdims n epochs
    states_ = array_to_cmatrix(states, n=6)
    ccall((:spkw05_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceDouble, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, body, center, frame, first, last, segid,
          gm, n, states_, epochs)
    handleerror()
end

"""
    spkw08(handle, body, center, frame, first, last, segid, degree, states, epoch1, step)

Write a type 8 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `degree`: Degree of interpolating polynomials
- `states`: States
- `epoch1`: Epoch of first state in states array
- `step`: Time step separating epochs of states

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw08_c.html)
"""
function spkw08(handle, body, center, frame, first, last, segid, degree, states, epoch1, step)
    n = length(states)
    states_ = array_to_cmatrix(states, n=6)
    ccall((:spkw08_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, SpiceDouble, SpiceDouble),
          handle, body, center, frame, first, last, segid,
          degree, n, states_, epoch1, step)
    handleerror()
end

"""
    spkw09(handle, body, center, frame, first, last, segid, degree, states, epochs)

Write a type 9 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `degree`: Degree of interpolating polynomials
- `states`: States
- `epochs`: Epochs

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw09_c.html)
"""
function spkw09(handle, body, center, frame, first, last, segid, degree, states, epochs)
    n = length(states)
    @checkdims n epochs
    states_ = array_to_cmatrix(states, n=6)
    ccall((:spkw09_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, body, center, frame, first, last, segid,
          degree, n, states_, epochs)
    handleerror()
end

"""
    spkw10(handle, body, center, frame, first, last, segid, consts, elems, epochs)

Write a type 10 segment to an SPK file.

### Arguments ###

- `handle`: The handle of a DAF file open for writing
- `body`: The NAIF ID code for the body of the segment
- `center`: The center of motion for body
- `frame`: The reference frame for this segment
- `first`: The first epoch for which the segment is valid
- `last`: The last  epoch for which the segment is valid
- `segid`: The string to use for segment identifier
- `consts`: The array of geophysical constants for the segmen
- `elems`: The collection of "two-line" element sets
- `epochs`: The epochs associated with the element sets

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw10_c.html)
"""
function spkw10(handle, body, center, frame, first, last, segid, consts, elems, epochs)
    n = length(elems)
    @checkdims n epochs
    elems_ = array_to_cmatrix(elems, n=10)
    ccall((:spkw10_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           Ref{SpiceDouble}, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, body, center, frame, first, last, segid,
          consts, n, elems_, epochs)
    handleerror()
end

"""
    spkw12(handle, body, center, frame, first, last, segid, degree, states, epoch1, step)

Write a type 12 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `degree`: Degree of interpolating polynomials
- `states`: States
- `epoch1`: Epoch of first state in states array
- `step`: Time step separating epochs of states

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw12_c.html)
"""
function spkw12(handle, body, center, frame, first, last, segid, degree, states, epoch1, step)
    n = length(states)
    states_ = array_to_cmatrix(states, n=6)
    ccall((:spkw12_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, SpiceDouble, SpiceDouble),
          handle, body, center, frame, first, last, segid,
          degree, n, states_, epoch1, step)
    handleerror()
end

"""
    spkw13(handle, body, center, frame, first, last, segid, degree, states, epochs)

Write a type 13 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `degree`: Degree of interpolating polynomials
- `states`: States
- `epochs`: Epochs

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw13_c.html)
"""
function spkw13(handle, body, center, frame, first, last, segid, degree, states, epochs)
    n = length(states)
    @checkdims n epochs
    states_ = array_to_cmatrix(states, n=6)
    ccall((:spkw13_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, body, center, frame, first, last, segid,
          degree, n, states_, epochs)
    handleerror()
end

"""
    spkw15(handle, body, center, frame, first, last, segid,
           epoch, tp, pa, p, ecc, j2flg, pv, gm, j2, radius)

Write a type 15 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `epoch`: Epoch of the periapse
- `tp`: Trajectory pole vector
- `pa`: Periapsis vector
- `p`: Semi-latus rectum
- `ecc`: Eccentricity
- `j2flg`: J2 processing flag
- `pv`: Central body pole vector
- `gm`: Central body GM
- `j2`: Central body J2
- `radius`: Equatorial radius of central body

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw15_c.html)
"""
function spkw15(handle, body, center, frame, first, last, segid, epoch, tp, pa, p, ecc, j2flg, pv,
                gm, j2, radius)
    @checkdims 3 tp pa pv
    ccall((:spkw15_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceDouble, SpiceDouble,
           SpiceDouble, Ref{SpiceDouble}, SpiceDouble, SpiceDouble, SpiceDouble),
          handle, body, center, frame, first, last, segid,
          epoch, tp, pa, p, ecc, j2flg, pv, gm, j2, radius)
    handleerror()
end

"""
    spkw17(handle, body, center, frame, first, last, segid, epoch, eqel, rapol, decpol)

Write a type 17 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `body`: Body code for ephemeris object
- `center`: Body code for the center of motion of the body
- `frame`: The reference frame of the states
- `first`: First valid time for which states can be computed
- `last`: Last valid time for which states can be computed
- `segid`: Segment identifier
- `epoch`: Epoch of elements in seconds past J2000
- `eqel`: Array of equinoctial elements
- `rapol`: Right Ascension of the pole of the reference plane
- `decpol`: Declination of the pole of the reference plane

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw17_c.html)
"""
function spkw17(handle, body, center, frame, first, last, segid, epoch, eqel, rapol, decpol)
    @checkdims 9 eqel
    ccall((:spkw17_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceDouble, Ref{SpiceDouble}, SpiceDouble, SpiceDouble),
          handle, body, center, frame, first, last, segid,
          epoch, eqel, rapol, decpol)
    handleerror()
end

"""
    spkw18(handle, subtyp, body, center, frame, first, last, segid, degree, packts, epochs)

Write a type 18 segment to an SPK file.

### Arguments ###

- `handle`: Handle of an SPK file open for writing
- `subtyp`: SPK type 18 subtype code, either `:S18TP0` or `:S18TP1`
- `body`: NAIF code for an ephemeris object
- `center`: NAIF code for center of motion of body
- `frame`: Reference frame name
- `first`: Start time of interval covered by segment
- `last`: End time of interval covered by segment
- `segid`: Segment identifier
- `degree`: Degree of interpolating polynomials
- `packts`: Time-ordered array of data packets representing geometric states of body
    - For `:S18TP0`: `[x,  y,  z,  dx/dt,  dy/dt,  dz/dt, vx, vy, vz, dvx/dt, dvy/dt, dvz/dt]`
    - For `:S18TP1`: `[x,  y,  z,  dx/dt,  dy/dt,  dz/dt]`
- `epochs`: Array of epochs corresponding to states.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw18_c.html)
"""
function spkw18(handle, subtyp, body, center, frame, first, last, segid, degree, packts, epochs)
    subtyp = subtyp == :S18TP0 ? 0 : subtyp == :S18TP1 ? 1 : throw(ArgumentError("Unknown `subtype`."))
    n = length(packts)
    @checkdims n epochs
    m = subtyp == :S18TP0 ? 12 : subtyp == :S18TP1 ? 6 : 0
    packts_ = array_to_cmatrix(packts, n=m)
    ccall((:spkw18_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceInt, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          handle, subtyp, body, center, frame, first, last, segid, degree, n, packts_, epochs)
    handleerror()
end

"""
    spkw20(handle, body, center, frame, first, last, segid, intlen, n, polydg, cdata, dscale,
           tscale, initjd, initfr)

Write a type 20 segment to an SPK file.

### Arguments ###

- `handle`: Handle of SPK file open for writing
- `body`: NAIF code for ephemeris object
- `center`: NAIF code for the center of motion of the body
- `frame`: Reference frame name
- `first`: Start time of interval covered by segment
- `last`: End time of interval covered by segment
- `segid`: Segment identifier
- `intlen`: Length of time covered by logical record (days)
- `cdata`: Array of Chebyshev coefficients and positions
- `dscale`: Distance scale of data
- `tscale`: Time scale of data
- `initjd`: Integer part of begin time (TDB Julian date) of first record
- `initfr`: Fractional part of begin time (TDB Julian date) of first record

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/spkw20_c.html)
"""
function spkw20(handle, body, center, frame, first, last, segid, intlen, cdata, dscale,
                tscale, initjd, initfr)
    n = length(cdata)
    polydg = length(cdata[1]) ÷ 3 - 2
    cdata_ = array_to_cmatrix(cdata)
    ccall((:spkw20_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Cstring, SpiceDouble, SpiceDouble, Cstring,
           SpiceDouble, SpiceInt, SpiceInt, Ref{SpiceDouble},
           SpiceDouble, SpiceDouble, SpiceDouble, SpiceDouble),
          handle, body, center, frame, first, last, segid,
          intlen, n, polydg, cdata_,
          dscale, tscale, initjd, initfr)
    handleerror()
end

"""
    srfc2s(code, bodyid)

Translate a surface ID code, together with a body ID code, to the corresponding surface name.
If no such name exists, return a string representation of the surface ID code.

### Arguments ###

- `code`: Integer surface ID code to translate to a string
- `bodyid`: ID code of body associated with surface

### Output ###

- `srfstr`: String corresponding to surface ID code
- `isname`: Logical flag indicating output is a surface name

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfc2s_c.html)
"""
function srfc2s(code, bodyid)
    srfstr = Array{SpiceChar}(undef, SPICE_SRF_SFNMLN)
    isname = Ref{SpiceBoolean}()
    ccall((:srfc2s_c, libcspice), Cvoid,
          (SpiceInt, SpiceInt, SpiceInt, Ref{SpiceChar}, Ref{SpiceBoolean}),
          code, bodyid, SPICE_SRF_SFNMLN, srfstr, isname)
    handleerror()
    chararray_to_string(srfstr), Bool(isname[])
end

"""
    srfcss(code, bodstr)

Translate a surface ID code, together with a body string, to the corresponding surface name.
If no such surface name exists, return a string representation of the surface ID code.

### Arguments ###

- `code`: Integer surface ID code to translate to a string
- `bodstr`: Name or ID of body associated with surface

### Output ###

- `srfstr`: String corresponding to surface ID code
- `isname`: Logical flag indicating output is a surface name

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfcss_c.html)
"""
function srfcss(code, bodstr)
    srfstr = Array{SpiceChar}(undef, SPICE_SRF_SFNMLN)
    isname = Ref{SpiceBoolean}()
    ccall((:srfcss_c, libcspice), Cvoid,
          (SpiceInt, Cstring, SpiceInt, Ref{SpiceChar}, Ref{SpiceBoolean}),
          code, bodstr, SPICE_SRF_SFNMLN, srfstr, isname)
    handleerror()
    chararray_to_string(srfstr), Bool(isname[])
end

"""
    srfnrm(method, target, et, fixref, npts, srfpts)

Map array of surface points on a specified target body to the corresponding unit length outward
surface normal vectors.

The surface of the target body may be represented by a triaxial ellipsoid or by topographic data
provided by DSK files.

### Arguments ###

- `method`: Computation method
- `target`: Name of target body
- `et`: Epoch in TDB seconds past J2000 TDB
- `fixref`: Body-fixed, body-centered target body frame
- `srfpts`: Array of surface points

### Output ###

Returns an array of outward, unit length normal vectors.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfnrm_c.html)
"""
function srfnrm(method, target, et, fixref, srfpts)
    npts = length(srfpts)
    srfpts_ = array_to_cmatrix(srfpts, n=3)
    normls = similar(srfpts_)
    ccall((:srfnrm_c, libcspice), Cvoid,
          (Cstring, Cstring, SpiceDouble, Cstring, SpiceInt, Ref{SpiceDouble}, Ref{SpiceDouble}),
          method, target, et, fixref, npts, srfpts_, normls)
    handleerror()
    cmatrix_to_array(normls)
end

"""
    srfrec(body, longitude, latitude)

Convert planetocentric latitude and longitude of a surface point on a specified body to rectangular
coordinates.

### Arguments ###

- `body`: NAIF integer code of an extended body.
- `longitude`: Longitude of point in radians.
- `latitude`: Latitude of point in radians.

### Output ###

Returns the rectangular coordinates of the point.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfrec_c.html)
"""
function srfrec(body, longitude, latitude)
    rectan = Array{SpiceDouble}(undef, 3)
    ccall((:srfrec_c, libcspice), Cvoid,
          (SpiceInt, SpiceDouble, SpiceDouble, Ref{SpiceDouble}),
          body, longitude, latitude, rectan)
    handleerror()
    rectan
end

"""
    srfs2c(srfstr, bodstr)

Translate a surface string, together with a body string, to the corresponding surface ID code.
The input strings may contain names or integer ID codes.

### Arguments ###

- `srfstr`: Surface name or ID string
- `bodstr`: Body name or ID string

### Output ###

Returns the surface ID code if it was found or `nothing` otherwise.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfs2c_c.html)
"""
function srfs2c(srfstr, bodstr)
    code = Ref{SpiceInt}()
    found = Ref{SpiceBoolean}()
    ccall((:srfs2c_c, libcspice), Cvoid,
          (Cstring, Cstring, Ref{SpiceInt}, Ref{SpiceBoolean}),
          srfstr, bodstr, code, found)
    handleerror()
    Bool(found[]) || return nothing
    Int(code[])
end

"""
    srfscc(srfstr, bodyid)

Translate a surface string, together with a body ID code, to the corresponding surface ID code.
The input surface string may contain a name or an integer ID code.

### Arguments ###

- `srfstr`: Surface name or ID string
- `bodyid`: Body ID code.

### Output ###

Returns the surface ID code if it was found or `nothing` otherwise.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/srfscc_c.html)
"""
function srfscc(srfstr, bodyid)
    code = Ref{SpiceInt}()
    found = Ref{SpiceBoolean}()
    ccall((:srfscc_c, libcspice), Cvoid,
          (Cstring, SpiceInt, Ref{SpiceInt}, Ref{SpiceBoolean}),
          srfstr, bodyid, code, found)
    handleerror()
    Bool(found[]) || return nothing
    Int(code[])
end

@deprecate srfxpt sincpt

"""
    srfxpt

!!! warning "Deprecated"
    Use `sincpt` instead.
"""
srfxpt

"""
    ssize!(cell, size)

Set the size (maximum cardinality) of a cell of any data type.

### Arguments ###

- `cell`: The cell
- `size`: Size (maximum cardinality) of the cell

### Output ###

Returns the updated cell.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/ssize_c.html)
"""
function ssize!(cell::SpiceCell{T}, size) where T
    data = cell.data
    GC.@preserve data begin
        ccall((:ssize_c, libcspice), Cvoid, (SpiceInt, Ref{Cell{T}}), size, cell.cell)
    end
    handleerror()
    cell
end

@deprecate ssize ssize!

"""
    stelab(pobj, vobs)

Correct the apparent position of an object for stellar aberration.

### Arguments ###

- `pobj`: Position of an object with respect to the observer
- `vobs`: Velocity of the observer with respect to the Solar System barycenter

### Output ###

Returns the apparent position of the object with respect to the observer, corrected for stellar aberration.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/stelab_c.html)
"""
function stelab(pobj, vobs)
    @checkdims 3 pobj vobs
    appobj = Array{SpiceDouble}(undef, 3)
    ccall((:stelab_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          pobj, vobs, appobj)
    handleerror()
    appobj
end

"""
    stpool(item, nth, contin, lenout=1024)

Retrieve the nth string from the kernel pool variable, where the string may be continued across
several components of the kernel pool variable.

### Arguments ###

- `item`: Name of the kernel pool variable
- `nth`: Index of the full string to retrieve
- `contin`: Character sequence used to indicate continuation
- `lenout`: Available space in output string (default: 1024)

### Output ###

Returns the full string concatenated across continuations if the kernel variable was found or
`nothing` otherwise.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/stpool_c.html)
"""
function stpool(item, nth, contin, lenout=1024)
    string = Array{SpiceChar}(undef, lenout)
    size = Ref{SpiceInt}()
    found = Ref{SpiceBoolean}()
    ccall((:stpool_c, libcspice), Cvoid,
          (Cstring, SpiceInt, Cstring, SpiceInt, Ref{SpiceChar}, Ref{SpiceInt}, Ref{SpiceBoolean}),
          item, nth - 1, contin, lenout, string, size, found)
    handleerror()
    chararray_to_string(string)
end

"""
    str2et(str)

Convert a string representing an epoch to a double precision value representing the number of TDB
seconds past the J2000 epoch corresponding to the input epoch.

### Arguments ###

- `str`: A string representing an epoch

### Output ###

Returns the equivalent value in seconds past J2000, TDB.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/str2et_c.html)
"""
function str2et(str)
    et = Ref{SpiceDouble}()
    ccall((:str2et_c, libcspice), Cvoid, (Cstring, Ref{SpiceDouble}), str, et)
    handleerror()
    et[]
end

"""
    subpnt(method, target, et, fixref, obsrvr, abcorr)

Compute the rectangular coordinates of the sub-observer point on
a target body at a specified epoch, optionally corrected for
light time and stellar aberration.

### Arguments ###

- `method`: Computation method
- `target`: Name of target body
- `et`: Epoch in ephemeris seconds past J2000 TDB
- `fixref`: Body-fixed, body-centered target body frame
- `abcorr`: Aberration correction
- `obsrvr`: Name of observing body

### Output ###

- `spoint`: Sub-solar point on the target body
- `trgepc`: Sub-solar point epoch
- `srfvec`: Vector from observer to sub-solar point

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/subpnt_c.html)
"""
function subpnt(method, target, et, fixref, abcorr, obsrvr)
    spoint = Array{SpiceDouble}(undef, 3)
    trgepc = Ref{SpiceDouble}()
    srfvec = Array{SpiceDouble}(undef, 3)
    ccall((:subpnt_c, libcspice), Cvoid,
          (Cstring, Cstring, SpiceDouble, Cstring, Cstring, Cstring,
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          method, target, et, fixref, abcorr, obsrvr, spoint, trgepc, srfvec)
    handleerror()
    spoint, trgepc[], srfvec
end

@deprecate subpt subpnt

"""
    subpt

!!! warning "Deprecated"
    Use `subpnt` instead.
"""
subpt

@deprecate subpt_pl02 subpnt

"""
    subpt_pl02

!!! warning "Deprecated"
    Use `subpnt` instead.
"""
subpt_pl02

"""
    subslr(method, target, et, fixref, abcorr, obsrvr)

Compute the rectangular coordinates of the sub-solar point on
a target body at a specified epoch, optionally corrected for
light time and stellar aberration.

### Arguments ###

- `method`: Computation method
- `target`: Name of target body
- `et`: Epoch in ephemeris seconds past J2000 TDB
- `fixref`: Body-fixed, body-centered target body frame
- `abcorr`: Aberration correction
- `obsrvr`: Name of observing body

### Output ###

- `spoint`: Sub-solar point on the target body
- `trgepc`: Sub-solar point epoch
- `srfvec`: Vector from observer to sub-solar point

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/subslr_c.html)
"""
function subslr(method, target, et, fixref, abcorr, obsrvr)
    spoint = Array{SpiceDouble}(undef, 3)
    trgepc = Ref{SpiceDouble}()
    srfvec = Array{SpiceDouble}(undef, 3)
    ccall((:subslr_c, libcspice), Cvoid,
          (Cstring, Cstring, SpiceDouble, Cstring, Cstring, Cstring,
           Ref{SpiceDouble}, Ref{SpiceDouble}, Ref{SpiceDouble}),
          method, target, et, fixref, abcorr, obsrvr, spoint, trgepc, srfvec)
    handleerror()
    spoint, trgepc[], srfvec
end

@deprecate subsol subslr

"""
    subsol

!!! warning "Deprecated"
    Use `subslr` instead.
"""
subsol

@deprecate subsol_pl02 subslr

"""
    subsol_pl02

!!! warning "Deprecated"
    Use `subslr` instead.
"""
subsol_pl02

function _sumad(array)
    n = length(array)
    ccall((:sumad_c, libcspice), SpiceDouble, (Ref{SpiceDouble}, SpiceInt), array, n)
end

@deprecate sumad sum

"""
    sumad(array)

!!! warning "Deprecated"
    Use `sum(array)` instead.
"""
sumad

function _sumai(array)
    n = length(array)
    array_ = SpiceInt.(array)
    ccall((:sumai_c, libcspice), SpiceInt, (Ref{SpiceInt}, SpiceInt), array_, n)
end

@deprecate sumai sum

"""
    sumai(array)

!!! warning "Deprecated"
    Use `sum(array)` instead.
"""
sumai

"""
    surfnm(a, b, c, point)

Computes the outward-pointing, unit normal vector from a point on the surface of an ellipsoid.

### Arguments ###

- `a`: Length of the ellisoid semi-axis along the x-axis
- `b`: Length of the ellisoid semi-axis along the y-axis
- `c`: Length of the ellisoid semi-axis along the z-axis
- `point`: Body-fixed coordinates of a point on the ellipsoid

### Output ###

Return the outward pointing unit normal to ellipsoid at point

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/surfnm_c.html)
"""
function surfnm(a, b, c, point)
    @checkdims 3 point
    normal = Array{SpiceDouble}(undef, 3)
    ccall((:surfnm_c, libcspice), Cvoid,
          (SpiceDouble, SpiceDouble, SpiceDouble, Ref{SpiceDouble}, Ref{SpiceDouble}),
          a, b, c, point, normal)
    handleerror()
    normal
end

"""
    surfpt(positn, u, a, b, c)

Determine the intersection of a line-of-sight vector with the surface of an ellipsoid.

### Arguments ###

- `positn`: Position of the observer in body-fixed frame
- `u`: Vector from the observer in some direction
- `a`: Length of the ellipsoid semi-axis along the x-axis
- `b`: Length of the ellipsoid semi-axis along the y-axis
- `c`: Length of the ellipsoid semi-axis along the z-axis

### Output ###

Returns the point on the ellipsoid pointed to by u or `nothing` if none was found.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/surfpt_c.html)
"""
function surfpt(positn, u, a, b, c)
    @checkdims 3 positn u
    point = Array{SpiceDouble}(undef, 3)
    found = Ref{SpiceBoolean}()
    ccall((:surfpt_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceDouble, SpiceDouble, SpiceDouble,
           Ref{SpiceDouble}, Ref{SpiceBoolean}),
          positn, u, a, b, c, point, found)
    handleerror()
    Bool(found[]) || return nothing
    point
end

"""
    surfpv(stvrtx, stdir, a, b, c)

Find the state (position and velocity) of the surface intercept defined by a specified ray,
ray velocity, and ellipsoid.

### Arguments ###

- `stvrtx`: State of ray's vertex
- `stdir`: State of ray's direction vector
- `a`: Length of ellipsoid semi-axis along the x-axis
- `b`: Length of ellipsoid semi-axis along the y-axis
- `c`: Length of ellipsoid semi-axis along the z-axis

### Output ###

Return the state of surface intercept or `nothing` if none was found.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/surfpv_c.html)
"""
function surfpv(stvrtx, stdir, a, b, c)
    @checkdims 6 stvrtx stdir
    stx = Array{SpiceDouble}(undef, 6)
    found = Ref{SpiceBoolean}()
    ccall((:surfpv_c, libcspice), Cvoid,
          (Ref{SpiceDouble}, Ref{SpiceDouble}, SpiceDouble, SpiceDouble, SpiceDouble,
           Ref{SpiceDouble}, Ref{SpiceBoolean}),
          stvrtx, stdir, a, b, c, stx, found)
    handleerror()
    Bool(found[]) || return nothing
    stx
end

"""
    swpool(agent, names)

Add a name to the list of agents to notify whenever a member of a list of kernel variables is updated.

### Arguments ###

- `agent`: The name of an agent to be notified after updates
- `names`: Variable names whose update causes the notice

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/swpool_c.html)
"""
function swpool(agent, names)
    names_, m, n = chararray(names)
    ccall((:swpool_c, libcspice), Cvoid, (Cstring, SpiceInt, SpiceInt, Ref{SpiceChar}),
          agent, m, n, names_)
    handleerror()
end

"""
    sxform(from, to, et)

Return the state transformation matrix from one frame to another at a specified epoch.

### Arguments ###

- `from`: Name of the frame to transform from
- `to`: Name of the frame to transform to
- `et`: Epoch of the state transformation matrix

### Output ###

Returns the state transformation matrix.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sxform_c.html)
"""
function sxform(from, to, et)
    rot = Array{SpiceDouble}(undef, 6, 6)
    ccall((:sxform_c, libcspice), Cvoid,
          (Cstring, Cstring, SpiceDouble, Ref{SpiceDouble}),
          from, to, et, rot)
    handleerror()
    permutedims(rot)
end

"""
    szpool(name)

Return the kernel pool size limitations.

### Arguments ###

- `name`: Name of the parameter to be returned

### Output ###

Returns the value of the parameter specified by `name` or `nothing` if none was found.

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/szpool_c.html)
"""
function szpool(name)
    n = Ref{SpiceInt}()
    found = Ref{SpiceBoolean}()
    ccall((:szpool_c, libcspice), Cvoid,
          (Cstring, Ref{SpiceInt}, Ref{SpiceBoolean}),
          name, n, found)
    Bool(found[]) || return nothing
    Int(n[])
end


module Recordings

import SignalAnalysis: signal, framerate, nchannels
import Statistics: mean
import DataFrames: DataFrame, DataFrameRow
import TimeZones: localzone, ZonedDateTime, astimezone, @tz_str
import Dates: unix2datetime, datetime2unix, now, UTC

const MAGIC = hton(0x43c04d126f173001)

# defaults from old file format without header
const FRAMERATE = 32000.0
const NCHANNELS = 4

function read(filename; tz=localzone())
  isfile(filename) && return readrec(filename)
  df = DataFrame(time=ZonedDateTime[], filename=String[], duration=Float64[], nchannels=Int[], framerate=Float64[])
  for filename ∈ readdir(filename; join=true)
    m = match(r"/?rec\-(\d+)\.dat", filename)
    if m !== nothing
      t = 0
      filesize = stat(filename).size
      nch = NCHANNELS
      fs = FRAMERATE
      if filesize > 24
        open(filename, "r") do f
          hdr = read(f, RecordingHeader)
          if hdr !== nothing
            t = astimezone(ZonedDateTime(unix2datetime(hdr.millis / 1000), tz"UTC"), tz)
            nch = hdr.nchannels
            fs = hdr.framerate
          end
        end
      end
      d = filesize / (4 * nch * fs)
      t == 0 && (t = astimezone(ZonedDateTime(unix2datetime(parse(Int64, m[1]) / 1000), tz"UTC"), tz))
      push!(df, (t, filename, d, nch, Float64(fs)))
    end
  end
  sort!(df, :time)
end

read(filenames::AbstractVector; tz=localzone()) = sort!(vcat(read.(filenames; tz)...), :time)

read(row::DataFrameRow) = readrec(row.filename)
read(df::DataFrame, i) = readrec(df.filename[i])

Base.@kwdef struct RecordingHeader
  magic::UInt64 = MAGIC
  millis::Int64 = round(Int64, 1000 * time())
  framerate::Int32
  nchannels::Int16 = 1
end

function read(io::IO, ::Type{RecordingHeader})
  p = position(io)
  try
    x = Base.read(io, UInt64)
    if x == MAGIC
      hdr = RecordingHeader(
        millis = Base.read(io, Int64),
        framerate = Base.read(io, Int32),
        nchannels = Base.read(io, Int16))
      [Base.read(io, UInt16) for _ in 1:5]
      return hdr
    end
  catch ex
    ex isa EOFError || @warn "$ex"
  end
  seek(io, p)
  nothing
end

function readrec(filename)
  open(filename, "r") do f
    nch = NCHANNELS
    fs = FRAMERATE
    hdr = read(f, RecordingHeader)
    if hdr !== nothing
      nch = hdr.nchannels
      fs = hdr.framerate
    end
    bytes = Base.read(f)
    raw = reinterpret(Float32, bytes)
    x = signal(reshape(raw, nch, :)', fs)
    x .- mean(x; dims=1)
  end
end

function write(filename, x; fs=framerate(x))
  open(filename, "w") do io
    hdr = RecordingHeader(
      millis = round(Int64, 1000 * datetime2unix(now(UTC))),
      framerate = round(Int32, fs),
      nchannels = nchannels(x))
    Base.write(io, hdr.magic)
    Base.write(io, hdr.millis)
    Base.write(io, hdr.framerate)
    Base.write(io, hdr.nchannels)
    Base.write(io, UInt16[0, 0, 0, 0, 0])
    Base.write(io, Float32.(x'))
  end
  nothing
end

end # module


"""
Representation of a trajectory, ie a Hamiltonian with a discrete integrator that
also checks for divergence.
"""
struct TrajectoryNUTS{Tf,S}
    # "Hamiltonian."
    # H::TH
    "Log density of z (negative log energy) at initial point."
    π₀::Tf
    "Stepsize for leapfrog."
    ϵ::Tf
    "Smallest decrease allowed in the log density."
    min_Δ::Tf
    "Turn statistic configuration."
    turn_statistic_configuration::S
end

function move(tree::Tree{D,T,L}, H::Hamiltonian, trajectory::TrajectoryNUTS, z::PhasePoint{D,T,L}, fwd::Bool) where {D,T,L}
    @unpack ϵ = trajectory
    leapfrog(tree, H, z, fwd ? ϵ : -ϵ)
end

###
### proposals
###

"""
$(SIGNATURES)
Random boolean which is `true` with the given probability `exp(logprob)`, which can be `≥ 1`
in which case no random value is drawn.
"""
function rand_bool_logprob(rng::AbstractRNG, logprob)
    logprob ≥ 0 || (randexp(rng, Float64) > -logprob)
end

function calculate_logprob2(::TrajectoryNUTS, is_doubling, ω₁, ω₂, ω)
    biased_progressive_logprob2(is_doubling, ω₁, ω₂, ω)
end

function combine_proposals(rng, tree::Tree, ::TrajectoryNUTS, z₁, z₂, logprob2::Real, is_forward::Bool)
    z, flag = rand_bool_logprob(rng, logprob2) ? (z₂, z₁.flag) : (z₁, z₂.flag)
    # is_doubling || free_z!(tree, flag)
    free_z!(tree, flag)
    z
end
# function combine_proposals(
    # rng, tree::Tree, ::TrajectoryNUTS, z₁, z₂, logprob2::Real, is_forward, guard₁, guard₂
# )
    # z, flag, guard = rand_bool_logprob(rng, logprob2) ? (z₂, z₁.flag, guard₁) : (z₁, z₂.flag, guard₂)
    # guard || free_z!(tree, flag)
    # z
# end

###
### statistics for visited nodes
###

struct AcceptanceStatistic{T}
    """
    Logarithm of the sum of metropolis acceptances probabilities over the whole trajectory
    (including invalid parts).
    """
    log_sum_α::T
    "Total number of leapfrog steps."
    steps::Int32
end

function combine_acceptance_statistics(A::AcceptanceStatistic, B::AcceptanceStatistic)
    AcceptanceStatistic(logaddexp(A.log_sum_α, B.log_sum_α), A.steps + B.steps)
end

"""
$(SIGNATURES)
Acceptance statistic for a leaf. The initial leaf is considered not to be visited.
"""
function leaf_acceptance_statistic(Δ, is_initial)
    is_initial ? AcceptanceStatistic(oftype(Δ, -Inf), zero(Int32)) : AcceptanceStatistic(Δ < zero(Δ) ? Δ : zero(Δ), one(Int32))
end

"""
$(SIGNATURES)
Return the acceptance rate (a `Real` betwen `0` and `1`).
"""
acceptance_rate(A::AcceptanceStatistic) = (a = exp(A.log_sum_α) / A.steps; a < one(a) ? a : one(a))

combine_visited_statistics(::TrajectoryNUTS, v, w) = combine_acceptance_statistics(v, w)

###
### turn analysis
###

"Statistics for the identification of turning points. See Betancourt (2017, appendix)."
struct GeneralizedTurnStatistic{D,T,L}
    p♯₋::FlaggedVector{D,T,L}
    p♯₊::FlaggedVector{D,T,L}
    ρ::FlaggedVector{D,T,L}
end

"""
A dummy turn statistic to use for convenient type stability; returns null ptrs that should hopefully segfault if accessed
(we'd only access them accidentally in case of a bug!)
"""
function dummy_turn_statistic(::Tree{D,T,L}) where {D,T,L}
    dummy_flag = zero(UInt32)
    dummy_ptr = reinterpret(Ptr{T}, zero(UInt))
    GeneralizedTurnStatistic(
        FlaggedVector{D,T,L}(dummy_ptr, dummy_flag),
        FlaggedVector{D,T,L}(dummy_ptr, dummy_flag),
        FlaggedVector{D,T,L}(dummy_ptr, dummy_flag)
    )
end

function leaf_turn_statistic(tree::Tree{D,T,L}, ::Val{:generalized}, H, z::PhasePoint{D,T,L}) where {D,T,L}
    p♯ = calculate_p♯(tree, H, z)
    GeneralizedTurnStatistic(p♯, p♯, FlaggedVector{D,T,L}(z.p, 0x00000000))
end

function combine_turn_statistics(
    tree::Tree, ::TrajectoryNUTS,
    x::GeneralizedTurnStatistic{D,T,L},
    y::GeneralizedTurnStatistic{D,T,L}
) where {D,T,L}
    ρₓ = x.ρ
    ρʸ = y.ρ
    # @show x.p♯₊.flag, y.p♯₋.flag
    if ρₓ.flag == 0x00000000
        # We are in depth 1; position allocated as part of a PhasePoint, not as part of a turn statistic
        # Therefore we cannot free either of them, and must allocate a new vector to store the results in.
        ρ = undefined_Σρ(tree)
        # Additionally, x.p♯₊ == x.p♯₋ and y.p♯₊ == y.p♯₋, therefore we cannot free them.
    else # we are at a depth of 2 or greater.
        # We can therefore reuse a ρ; arbitrarily, we reuse xρ
        ρ = ρₓ
        # and free yρ as well as the two discarded p♯
        free_Σρ!(tree, ρʸ.flag)
        free_ρ♯!(tree, x.p♯₊.flag)
        free_ρ♯!(tree, y.p♯₋.flag)
    end
    @avx for l in eachindex(ρ)
        ρ[l] = ρₓ[l] + ρʸ[l]
    end
    # x.p♯₊.flag == x.p♯₋.flag || free_ρ♯!(tree, x.p♯₊.flag)
    # y.p♯₊.flag == y.p♯₋.flag || free_ρ♯!(tree, y.p♯₋.flag)
    GeneralizedTurnStatistic(x.p♯₋, y.p♯₊, ρ)
end

function is_turning(::TrajectoryNUTS, τ::GeneralizedTurnStatistic{D,T,L}) where {D,T,L}
    # Uses the generalized NUTS criterion from Betancourt (2017).
    @unpack p♯₋, p♯₊, ρ = τ
    # @argcheck p♯₋ ≢ p♯₊ "internal error: is_turning called on a leaf"
    d♯₋ = zero(T)
    d♯₊ = zero(T)
    @avx for d ∈ eachindex(ρ)
        ρᵈ = ρ[d]
        d♯₋ += ρᵈ * p♯₋[d]
        d♯₊ += ρᵈ * p♯₊[d]
    end
    # Current version always calculates both dot products; alternative:
    # dot(p♯₋, ρ) < 0 || dot(p♯₊, ρ) < 0
    # Calculating both in one loop is faster
    # ( 2D fma + 3D loads vs 2D fma + 4D loads; at most 2/4 ops/cycle
    #   can be loads, so the loads cannot keep up with the 2/4 fma/cycle
    #   and are the bottleneck ),
    # but it disallows conditional checking to skip second dot product.
    #
    # Which is faster on average depends on probability of turning
    # probability of turning will be fairly low for most models.
    (d♯₋ < zero(T)) | (d♯₊ < zero(T))
end

###
### leafs
###

function leaf(tree::Tree, H::Hamiltonian, trajectory::TrajectoryNUTS, z, is_initial)
    @unpack π₀, min_Δ, turn_statistic_configuration = trajectory
    # @show logdensity(H, z), π₀
    Δ = is_initial ? zero(π₀) : logdensity(H, z) - π₀
    isdiv = Δ < min_Δ
    v = leaf_acceptance_statistic(Δ, is_initial)
    if isdiv
        # @show H
        # @show z
        # @show typeof(z)
        (z, Δ, dummy_turn_statistic(tree)), v, true
    else
        τ = leaf_turn_statistic(tree, turn_statistic_configuration, H, z)
        (z, Δ, τ), v, false
    end
end

####
#### NUTS interface
####

"""
$(TYPEDEF)
Implementation of the “No-U-Turn Sampler” of Hoffman and Gelman (2014), including subsequent
developments, as described in Betancourt (2017).
# Fields
$(FIELDS)
"""
struct NUTS{S}
    "Maximum tree depth."
    max_depth::Int
    "Threshold for negative energy relative to starting point that indicates divergence."
    min_Δ::Float64
    """
    Turn statistic configuration. Currently only `Val(:generalized)` (the default) is
    supported.
    """
    turn_statistic_configuration::S
    function NUTS(; max_depth = DEFAULT_MAX_TREE_DEPTH, min_Δ = -1000.0,
                  turn_statistic_configuration::S = Val{:generalized}()) where {S}
        # @argcheck 0 < max_depth ≤ MAX_DIRECTIONS_DEPTH
        # @argcheck min_Δ < 0
        new{S}(Int(max_depth), Float64(min_Δ), turn_statistic_configuration)
    end
end

"""
$(TYPEDEF)
Diagnostic information for a single tree built with the No-U-turn sampler.
# Fields
Accessing fields directly is part of the API.
$(FIELDS)
"""
struct TreeStatisticsNUTS # now 32 bytes; fits in ymm register
    "Log density (negative energy)."
    π::Float64
    "Acceptance rate statistic."
    acceptance_rate::Float64
    "Reason for termination. See [`InvalidTree`](@ref) and [`REACHED_MAX_DEPTH`](@ref)."
    termination::InvalidTree
    "Depth of the tree."
    depth::Int32
    "Number of leapfrog steps evaluated."
    steps::Int32
    # "Directions for tree doubling (useful for debugging)."
    # directions::Directions
end

"""
$(SIGNATURES)
No-U-turn Hamiltonian Monte Carlo transition, using Hamiltonian `H`, starting at evaluated
log density position `Q`, using stepsize `ϵ`. Parameters of `algorithm` govern the details
of tree construction.
Return two values, the new evaluated log density position, and tree statistics.
"""
function sample_tree(rng, tree::Tree, algorithm::NUTS, H::Hamiltonian, z::PhasePoint, ϵ;
                          p = nothing, directions = rand(rng, Directions))
    if p === nothing
        rand_p!(rng, z.p, H.κ)
    else
        @unpack Q, flag = z
        z = PhasePoint(Q, p, flag)
    end
    @unpack max_depth, min_Δ, turn_statistic_configuration = algorithm
    trajectory = TrajectoryNUTS(logdensity(H, z), ϵ, min_Δ, turn_statistic_configuration)
    ζ, v, termination, depth = sample_trajectory(rng, tree, H, trajectory, z, max_depth, directions)
    tree_statistics = TreeStatisticsNUTS(logdensity(H, ζ), acceptance_rate(v), termination, depth, v.steps)#, directions)
    ζ, tree_statistics
end


# step one, generate optimizer info
function rehearse_contraction(tn::CuTensorNetwork, max_workspace_size::Integer, optimizer_conf::OptimizerConfig=OptimizerConfig())
    info = CuTensorNetworkContractionOptimizerInfo(tn.desc)
    # set optimizer options
    config = CuTensorNetworkContractionOptimizerConfig(optimizer_conf)
    # perform optimization
    cutensornetContractionOptimize(handle(), tn.desc, config, max_workspace_size, info)
    plan = CuTensorNetworkContractionPlan(tn.desc, info, max_workspace_size)
    return info, plan
end

function num_slices(info::CuTensorNetworkContractionOptimizerInfo)
    n = Ref{Int64}()
    cutensornetContractionOptimizerInfoGetAttribute(handle(), info, CUTENSORNET_CONTRACTION_OPTIMIZER_INFO_NUM_SLICES, n, sizeof(Int64))
    return n[]
end

function num_sliced_modes(info::CuTensorNetworkContractionOptimizerInfo)
    n = Ref{Int32}()
    cutensornetContractionOptimizerInfoGetAttribute(handle(), info, CUTENSORNET_CONTRACTION_OPTIMIZER_INFO_NUM_SLICED_MODES, n, sizeof(Int32))
    return n[]
end

function flop_count(info::CuTensorNetworkContractionOptimizerInfo)
    n = Ref{Float64}()
    cutensornetContractionOptimizerInfoGetAttribute(handle(), info, CUTENSORNET_CONTRACTION_OPTIMIZER_INFO_FLOP_COUNT, n, sizeof(Float64))
    return n[]
end

# step 2, contract
function perform_contraction!(tn::CuTensorNetwork, info, plan, workspace_size::Integer, ::NoAutoTune; prefs::AutotunePreferences=AutotunePreferences(), stream::CuStream=stream())
    with_workspace(workspace_size) do workspace
        for slice_ix in 0:num_slices(info)-1
            cutensornetContraction(handle(), plan, pointer.(tn.input_arrs),
                                   pointer(tn.output_arr), workspace, workspace_size,
                                   slice_ix, stream)
        end
    end
    return tn
end

function perform_contraction!(tn::CuTensorNetwork, info, plan, workspace_size::Integer, ::AutoTune; prefs::AutotunePreferences=AutotunePreferences(), stream::CuStream=stream())
    ctn_prefs = CuTensorNetworkAutotunePreference(prefs)
    with_workspace(workspace_size) do workspace
        cutensornetContractionAutotune(handle(), plan, pointer.(tn.input_arrs),
                                       pointer(tn.output_arr), workspace, workspace_size,
                                       ctn_prefs, stream)
        for slice_ix in 0:num_slices(info)-1
            cutensornetContraction(handle(), plan, pointer.(tn.input_arrs),
                                   pointer(tn.output_arr), workspace, workspace_size,
                                   slice_ix, stream)
        end
    end
    return tn
end


isless{N, ES, mode}(lhs::Sigmoid{N, ES, mode}, rhs::Sigmoid{N, ES, mode}) = @s(lhs) < @s(rhs)

Base.:<{N, ES, mode}(lhs::Sigmoid{N, ES, mode}, rhs::Sigmoid{N, ES, mode}) = (@u(rhs) == (@signbit)) | isless(lhs, rhs)
Base.:<={N, ES, mode}(lhs::Sigmoid{N, ES, mode}, rhs::Sigmoid{N, ES, mode}) = (@u(rhs) == (@signbit)) | (@s(lhs) <= @s(rhs))


Core.eval(Main, quote
    hello_from_sideeffect = rand()
end)


"""
Determines how to react to an exceeded rate limit.

- `ORL_RETURN`: Return a [`Result`](@ref) containing a [`RateLimited`](@ref) exception.
- `ORL_WAIT`: Block and wait for the rate limit to expire.
"""
@enum OnRateLimit ORL_RETURN ORL_WAIT

"""
A signal that a rate limit has been exceeded.
Can contain the amount of time until its expiry.
"""
struct RateLimited <: Exception
    period::Union{Period, Nothing}
end

"""
A generic rate limiter using the `[X-]RateLimit-Remaining` and `[X-]RateLimit-Reset` response headers.
The reset header is assumed to be a Unix timestamp in seconds.
"""
mutable struct RateLimiter
    remaining::Int
    reset::Int

    RateLimiter() = new(1, 0)
end

rate_limit_check(rl::RateLimiter) =
    rl.remaining == 0 && rate_limit_period(rl) > Millisecond(0)

rate_limit_wait(rl::RateLimiter) = sleep(rate_limit_period(rl))

rate_limit_period(rl::RateLimiter) = max(Millisecond(0), unix2datetime(rl.reset) - now(UTC))

function rate_limit_update!(rl::RateLimiter, r::HTTP.Response)
    remaining = tryheader(i -> parse(Int, i), r, "RateLimit-Remaining")
    reset = tryheader(i -> parse(Int, i), r, "RateLimit-Reset")
    if remaining === nothing  || reset === nothing
        @warn "Parsing rate limit headers failed"
    else
        rl.remaining = remaining
        rl.reset = reset
    end
end

function tryheader(f::Function, r::HTTP.Response, header::AStr)
    for h in [header, "X-$header"]
        HTTP.hasheader(r, h) && return f(HTTP.header(r, h))
    end
    return nothing
end


# Default values for the integrator

"Default values for the integrator"
module defaults
import Ogre: bar, K, R_earth

# These were mostly used in stage 1 for the interior models only
"Surface pressure"
const P_surf = 1bar
"Surface temperature"
const T_surf = 300K
"Mass fractions of the different layers"
const mass_fractions = [1.]
"Total number of points to use in the integration"
const total_points = 500
"Radius search bracket"
const R_bracket = [0., 10.] .* R_earth
end