text:
#=
TODO
    * move all gl_ methods to GLPlot
    * integrate GLPlot UI
    * clean up corner cases
    * find a cleaner way for extracting properties
    * polar plots
    * labes and axis
    * fix units in all visuals (e.g dotted lines, marker scale, surfaces)
    * why is there so little unicode supported in the font!??!?
=#

const _glvisualize_attr = merge_with_base_supported([
    :annotations,
    :background_color_legend, :background_color_inside, :background_color_outside,
    :foreground_color_grid, :foreground_color_legend, :foreground_color_title,
    :foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
    :label,
    :linecolor, :linestyle, :linewidth, :linealpha,
    :markershape, :markercolor, :markersize, :markeralpha,
    :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
    :fillrange, :fillcolor, :fillalpha,
    :bins, :bar_width, :bar_edges, :bar_position,
    :title, :title_location, :titlefont,
    :window_title,
    :guide, :lims, :ticks, :scale, :flip, :rotation,
    :tickfont, :guidefont, :legendfont,
    :grid, :legend, :colorbar,
    :marker_z,
    :line_z,
    :levels,
    :ribbon, :quiver, :arrow,
    :orientation,
    :overwrite_figure,
    #:polar,
    :normalize, :weights,
    :contours, :aspect_ratio,
    :match_dimensions,
    :clims,
    :inset_subplots,
    :dpi,
    :hover
])
const _glvisualize_seriestype = [
    :path, :shape,
    :scatter, :hexbin,
    :bar, :boxplot,
    :heatmap, :image, :volume,
    :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
]
const _glvisualize_style = [:auto, :solid, :dash, :dot, :dashdot]
const _glvisualize_marker = _allMarkers
const _glvisualize_scale = [:identity, :ln, :log2, :log10]

# --------------------------------------------------------------------------------------

function _initialize_backend(::GLVisualizeBackend; kw...)
    @eval begin
        import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour
        import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle
        import FileIO, Images
        export GLVisualize
        import Reactive: Signal
        import GLAbstraction: Style
        import GLVisualize: visualize
        import Plots.GL
        Plots.slice_arg(img::Images.AbstractImage, idx::Int) = img
        is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
        is_marker_supported{Img<:Images.AbstractImage}(::GLVisualizeBackend, shape::Union{Vector{Img}, Img}) = true
        is_marker_supported{C<:Colorant}(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) = true
        is_marker_supported(::GLVisualizeBackend, shape::Shape) = true
        const GL = Plots
    end
end

function add_backend_string(b::GLVisualizeBackend)
    """
    For those incredibly brave souls who assume full responsibility for what happens next...
    There's an easy way to get what you need for the GLVisualize backend to work:

    Pkg.clone("https://github.com/tbreloff/MetaPkg.jl")
    import MetaPkg
    MetaPkg.checkout("MetaGL")

    See the MetaPkg readme for details...
    """
end

# ---------------------------------------------------------------------------

# initialize the figure/window
# function _create_backend_figure(plt::Plot{GLVisualizeBackend})
#     # init a screen
#
#     GLPlot.init()
# end
const _glplot_deletes = []
function empty_screen!(screen)
    if isempty(_glplot_deletes)
        screen.renderlist = ()
        for c in screen.children
            empty!(c)
        end
        empty!(screen.children)
    else
        for del_signal in _glplot_deletes
            push!(del_signal, true) # trigger delete
        end
        empty!(_glplot_deletes)
    end
    nothing
end
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
    # init a screen
    if isempty(GLVisualize.get_screens())
        s = GLVisualize.glscreen()
        Reactive.stop()
        @async begin
            while isopen(s)
                tic()
                GLWindow.pollevents()
                if Base.n_avail(Reactive._messages) > 0
                    Reactive.run_till_now()
                    GLWindow.render_frame(s)
                    GLWindow.swapbuffers(s)
                end
                yield()
                diff = (1/60) - toq()
                while diff >= 0.001
                    tic()
                    sleep(0.001) # sleep for the minimal amount of time
                    diff -= toq()
                end
            end
            GLWindow.destroy!(s)
            GLVisualize.cleanup_old_screens()
        end
    else
        s = GLVisualize.current_screen()
        empty_screen!(s)
    end
    s
end
# ---------------------------------------------------------------------------

const _gl_marker_map = KW(
    :rect => '■',
    :star5 => '★',
    :diamond => '◆',
    :hexagon => '⬢',
    :cross => '✚',
    :xcross => '❌',
    :utriangle => '▲',
    :dtriangle => '▼',
    :pentagon => '⬟',
    :octagon => '⯄',
    :star4 => '✦',
    :star6 => '🟋',
    :star8 => '✷',
    :vline => '┃',
    :hline => '━',
    :+ => '+',
    :x => 'x',
)

function gl_marker(shape, size)
    shape
end
function gl_marker(shape::Shape, size::FixedSizeArrays.Vec{2,Float32})
    points = Point2f0[Vec{2,Float32}(p)*10f0 for p in zip(shape.x, shape.y)]
    GeometryTypes.GLNormalMesh(points)
end
# create a marker/shape type
function gl_marker(shape::Symbol, msize)
    isa(msize, Array) && (msize = first(msize)) # size doesn't really matter now
    if shape == :rect
        GeometryTypes.HyperRectangle(Vec{2, Float32}(0), msize)
    elseif shape == :circle || shape == :none
        GeometryTypes.HyperSphere(Point{2, Float32}(0), maximum(msize))
    elseif haskey(_gl_marker_map, shape)
        _gl_marker_map[shape]
    elseif haskey(_shapes, shape)
        gl_marker(_shapes[shape], msize)
    else
        error("Shape $shape not supported by GLVisualize")
    end
end

function extract_limits(sp, d, kw_args)
    clims = sp[:clims]
    if is_2tuple(clims)
        if isfinite(clims[1]) && isfinite(clims[2])
          kw_args[:limits] = Vec2f0(clims)
        end
    end
    nothing
end

function extract_marker(d, kw_args)
    dim = Plots.is3d(d) ? 3 : 2
    scaling = dim == 3 ? 0.003 : 2
    if haskey(d, :markersize)
        msize = d[:markersize]
        if isa(msize, AbstractArray)
            kw_args[:scale] = map(x->GeometryTypes.Vec{dim, Float32}(x*scaling), msize)
        else
            kw_args[:scale] = GeometryTypes.Vec{dim, Float32}(msize*scaling)
        end
    end
    if haskey(d, :markershape)
        shape = d[:markershape]
        shape = gl_marker(shape, kw_args[:scale])
        if shape != :none
            kw_args[:primitive] = shape
        end
    end
    # get the color
    key = :markercolor
    haskey(d, key) || return
    c = gl_color(d[key])
    if isa(c, AbstractVector) && d[:marker_z] != nothing
        extract_colornorm(d, kw_args)
        kw_args[:color] = nothing
        kw_args[:color_map] = c
        kw_args[:intensity] = convert(Vector{Float32}, d[:marker_z])
    else
        kw_args[:color] = c
    end
    key = :markerstrokecolor
    haskey(d, key) || return
    c = gl_color(d[key])
    if c != nothing
        if !(isa(c, Colorant) || (isa(c, Vector) && eltype(c) <: Colorant))
            error("Stroke Color not supported: $c")
        end
        kw_args[:stroke_color] = c
        kw_args[:stroke_width] = Float32(d[:markerstrokewidth])
    end
end

function _extract_surface(d::Plots.Surface)
    d.surf
end
function _extract_surface(d::AbstractArray)
    d
end
# TODO when to transpose??
function extract_surface(d)
    map(_extract_surface, (d[:x], d[:y], d[:z]))
end
function topoints{P}(::Type{P}, array)
    P[x for x in zip(array...)]
end
function extract_points(d)
    dim = is3d(d) ? 3 : 2
    array = (d[:x], d[:y], d[:z])[1:dim]
    topoints(Point{dim, Float32}, array)
end
function make_gradient{C<:Colorant}(grad::Vector{C})
    grad
end
function make_gradient(grad::ColorGradient)
    RGBA{Float32}[c for c in grad.colors]
end
make_gradient(c) = make_gradient(cgrad())

function extract_any_color(d, kw_args)
    if d[:marker_z] == nothing
        c = scalar_color(d, :fill)
        extract_c(d, kw_args, :fill)
        if isa(c, Colorant)
            kw_args[:color] = c
        else
            kw_args[:color] = nothing
            kw_args[:color_map] = make_gradient(c)
            clims = d[:subplot][:clims]
            if Plots.is_2tuple(clims)
                if isfinite(clims[1]) && isfinite(clims[2])
                    kw_args[:color_norm] = Vec2f0(clims)
                end
            elseif clims == :auto
                kw_args[:color_norm] = Vec2f0(extrema(d[:y]))
            end
        end
    else
        kw_args[:color] = nothing
        clims = d[:subplot][:clims]
        if Plots.is_2tuple(clims)
            if isfinite(clims[1]) && isfinite(clims[2])
                kw_args[:color_norm] = Vec2f0(clims)
            end
        elseif clims == :auto
            kw_args[:color_norm] = Vec2f0(extrema(d[:y]))
        else
            error("Unsupported limits: $clims")
        end
        kw_args[:intensity] = convert(Vector{Float32}, d[:marker_z])
        kw_args[:color_map] = gl_color_map(d, :marker)
    end
end

function extract_stroke(d, kw_args)
    extract_c(d, kw_args, :line)
    if haskey(d, :linewidth)
        kw_args[:thickness] = d[:linewidth]*3
    end
end
function extract_color(d, sym)
    d[Symbol("$(sym)color")]
end

gl_color(c::PlotUtils.ColorGradient) = c.colors
gl_color{T<:Colorant}(c::Vector{T}) = c
gl_color(c::RGBA{Float32}) = c
gl_color(c::Colorant) = RGBA{Float32}(c)

function gl_color(tuple::Tuple)
    gl_color(tuple...)
end

# convert to RGBA
function gl_color(c, a)
    c = convertColor(c, a)
    RGBA{Float32}(c)
end
function scalar_color(d, sym)
    gl_color(extract_color(d, sym))
end

function gl_color_map(d, sym)
    colors = extract_color(d, sym)
    _gl_color_map(colors)
end
function _gl_color_map(colors::PlotUtils.ColorGradient)
    colors.colors
end
function _gl_color_map(c)
    Plots.default_gradient()
end

dist(a, b) = abs(a-b)
mindist(x, a, b) = min(dist(a, x), dist(b, x))
function gappy(x, ps)
    n = length(ps)
    x <= first(ps) && return first(ps) - x
    for j=1:(n-1)
        p0 = ps[j]
        p1 = ps[min(j+1, n)]
        if p0 <= x && p1 >= x
            return mindist(x, p0, p1) * (isodd(j) ? 1 : -1)
        end
    end
    return last(ps) - x
end
function ticks(points, resolution)
    Float16[gappy(x, points) for x=linspace(first(points),last(points), resolution)]
end

function insert_pattern!(points, kw_args)
    tex = GLAbstraction.Texture(ticks(points, 100), x_repeat=:repeat)
    kw_args[:pattern] = tex
    kw_args[:pattern_length] = Float32(last(points))
end
function extract_linestyle(d, kw_args)
    haskey(d, :linestyle) || return
    ls = d[:linestyle]
    lw = d[:linewidth]
    kw_args[:thickness] = lw
    if ls == :dash
        points = [0.0, lw, 2lw, 3lw, 4lw]
        insert_pattern!(points, kw_args)
    elseif ls == :dot
        tick, gap = lw/2, lw/4
        points = [0.0, tick, tick+gap, 2tick+gap, 2tick+2gap]
        insert_pattern!(points, kw_args)
    elseif ls == :dashdot
        dtick, dgap = lw, lw
        ptick, pgap = lw/2, lw/4
        points = [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap]
        insert_pattern!(points, kw_args)
    elseif ls == :dashdotdot
        dtick, dgap = lw, lw
        ptick, pgap = lw/2, lw/4
        points = [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap, dtick+dgap+ptick+pgap+ptick,  dtick+dgap+ptick+pgap+ptick+pgap]
        insert_pattern!(points, kw_args)
    end
    extract_c(d, kw_args, :line)
    nothing
end
function hover(to_hover::Vector, to_display, window)
    hover(to_hover[], to_display, window)
end
function get_cam(x)
    if isa(x, GLAbstraction.Context)
        return get_cam(x.children)
    elseif isa(x, Vector)
        return get_cam(first(x))
    elseif isa(x, GLAbstraction.RenderObject)
        return x[:preferred_camera]
    end
end

function hover(to_hover, to_display, window)
    if isa(to_hover, GLAbstraction.Context)
        return hover(to_hover.children, to_display, window)
    end
    area = map(window.inputs[:mouseposition]) do mp
        SimpleRectangle{Int}(round(Int, mp+10)..., 100, 70)
    end
    background = visualize((GLVisualize.ROUNDED_RECTANGLE, Point2f0[0]),
        color=RGBA{Float32}(0,0,0,0), scale=Vec2f0(100, 70), offset=Vec2f0(0),
        stroke_color=RGBA{Float32}(0,0,0,0.4),
        stroke_width=-1.0f0
    )
    mh = GLWindow.mouse2id(window)
    popup = GLWindow.Screen(window, area=area, hidden=true)
    cam = get!(popup.cameras, :perspective) do
        GLAbstraction.PerspectiveCamera(
            popup.inputs, Vec3f0(3), Vec3f0(0),
            keep=Signal(false),
            theta= Signal(Vec3f0(0)), trans= Signal(Vec3f0(0))
        )
    end
    Reactive.preserve(map(mh) do mh
        popup.hidden = !(mh.id == to_hover.id)
    end)

    map(enumerate(to_display)) do id
        i,d = id
        robj = visualize(d)
        viewit = Reactive.droprepeats(map(mh->mh.id == to_hover.id && mh.index == i, mh))
        camtype = get_cam(robj)
        Reactive.preserve(map(viewit) do vi
            if vi
                empty!(popup)
                if camtype == :perspective
                    cam.projectiontype.value = GLVisualize.PERSPECTIVE
                else
                    cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
                end
                GLVisualize._view(robj, popup, camera=cam)
                GLVisualize._view(background, popup, camera=:fixed_pixel)
                bb = GLAbstraction.boundingbox(robj).value
                mini = minimum(bb)
                w = GeometryTypes.widths(bb)
                wborder = w*0.08f0 #8 percent border
                bb = GeometryTypes.AABB{Float32}(mini-wborder, w+2f0*wborder)
                GLAbstraction.center!(cam, bb)
            end
        end)
    end
    nothing
end

function extract_extrema(d, kw_args)
    xmin,xmax = extrema(d[:x]); ymin,ymax = extrema(d[:y])
    kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin)
    nothing
end

function extract_font(font, kw_args)
    kw_args[:family] = font.family
    kw_args[:relative_scale] = font.pointsize*1.5 ./ GLVisualize.glyph_scale!('X')
    kw_args[:color] = gl_color(font.color)
end

function extract_colornorm(d, kw_args)
    clims = d[:subplot][:clims]
    if Plots.is_2tuple(clims)
        if isfinite(clims[1]) && isfinite(clims[2])
            kw_args[:color_norm] = Vec2f0(clims)
        end
    elseif clims == :auto
        z = if haskey(d, :marker_z) && d[:marker_z] != nothing
            d[:marker_z]
        elseif haskey(d, :line_z) && d[:line_z] != nothing
            d[:line_z]
        elseif isa(d[:z], Plots.Surface)
            d[:z].surf
        else
            d[:y]
        end
        kw_args[:color_norm] = Vec2f0(extrema(z))
        kw_args[:intensity] = map(Float32, collect(z))
    end
end
function extract_gradient(d, kw_args, sym)
    key = Symbol("$(sym)color")
    haskey(d, key) || return
    c = make_gradient(d[key])
    kw_args[:color] = nothing
    extract_colornorm(d, kw_args)
    kw_args[:color_map] = c
    return
end
function extract_c(d, kw_args, sym)
    key = Symbol("$(sym)color")
    haskey(d, key) || return
    c = gl_color(d[key])
    kw_args[:color] = nothing
    kw_args[:color_map] = nothing
    kw_args[:color_norm] = nothing
    if isa(c, AbstractVector)
        extract_colornorm(d, kw_args)
        kw_args[:color_map] = c
    else
        kw_args[:color] = c
    end
    return
end

function extract_stroke(d, kw_args, sym)
    key = Symbol("$(sym)strokecolor")
    haskey(d, key) || return
    c = gl_color(d[key])
    if c != nothing
        if !isa(c, Colorant)
            error("Stroke Color not supported: $c")
        end
        kw_args[:stroke_color] = c
        kw_args[:stroke_width] = Float32(d[Symbol("$(sym)strokewidth")]) * 2
    end
    return
end

function draw_grid_lines(sp, grid_segs, thickness, style, model, color)

    kw_args = Dict{Symbol, Any}(
        :model => model
    )
    d = Dict(
        :linestyle => style,
        :linewidth => thickness,
        :linecolor => color
    )
    Plots.extract_linestyle(d, kw_args)
    GL.gl_lines(map(Point2f0, grid_segs.pts), kw_args)
end

function align_offset(startpos, lastpos, atlas, rscale, font, align)
    xscale, yscale = GLVisualize.glyph_scale!('X').*rscale
    xmove = (lastpos-startpos)[1]+xscale
    if align == :top
        return -Vec2f0(xmove/2f0, yscale)
    elseif align == :right
        return -Vec2f0(xmove, yscale/2f0)
    else
        error("Align $align not known")
    end
end
function align_offset(startpos, lastpos, atlas, rscale, font, align::Vec)
    xscale, yscale = GLVisualize.glyph_scale!('X').*rscale
    xmove = (lastpos-startpos)[1]+xscale
    return -Vec2f0(xmove, yscale) .* align
end
function alignment2num(x::Symbol)
    (x in (:hcenter, :vcenter)) && return 0.5
    (x in (:left, :bottom)) && return 0.0
    (x in (:right, :top)) && return 1.0
    0.0 # 0 default, or better to error?
end
function alignment2num(font::Plots.Font)
    Vec2f0(map(alignment2num, (font.halign, font.valign)))
end

function draw_ticks(axis, ticks, align, move, isx, lims, model, text = "", positions = Point2f0[], offsets=Vec2f0[])
    sz = axis[:tickfont].pointsize
    rscale2 = Vec2f0(3/sz)
    m = Reactive.value(model)
    xs, ys = m[1,1], m[2,2]
    rscale = rscale2 ./ Vec2f0(xs, ys)
    atlas = GLVisualize.get_texture_atlas()
    font = GLVisualize.DEFAULT_FONT_FACE
    if !(ticks in (nothing, false))
        # x labels
        flip = axis[:flip]
        for (cv, dv) in zip(ticks...)
            x,y = cv, (flip ? lims[2] : lims[1])
            startpos = Point2f0(isx ? (x,y) : (y,x))-move
            # @show cv dv ymin xi yi
            str = string(dv)
            position = GLVisualize.calc_position(str, startpos, rscale, font, atlas)
            offset = GLVisualize.calc_offset(str, rscale2, font, atlas)
            alignoff = align_offset(startpos, last(position), atlas, rscale, font, align)
            map!(position) do pos
                pos .+ alignoff
            end
            append!(positions, position)
            append!(offsets, offset)
            text *= str
        end
    end
    text, positions, offsets
end
function text(position, text, kw_args)
    text_align = alignment2num(text.font)
    startpos = Vec2f0(position)
    atlas = GLVisualize.get_texture_atlas()
    font = GLVisualize.DEFAULT_FONT_FACE
    rscale = kw_args[:relative_scale]
    m = Reactive.value(kw_args[:model])
    position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas)
    offset = GLVisualize.calc_offset(text.str, rscale, font, atlas)
    alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align)
    map!(position) do pos
        pos .+ alignoff
    end
    kw_args[:position] = position
    kw_args[:offset] = offset
    kw_args[:scale_primitive] = true
    visualize(text.str, Style(:default), kw_args)
end

function text_model(font, pivot)
    pv = GeometryTypes.Vec3f0(pivot[1], pivot[2], 0)
    if font.rotation != 0.0
        rot = Float32(deg2rad(font.rotation))
        rotm = GLAbstraction.rotationmatrix_z(rot)
        return GLAbstraction.translationmatrix(pv)*rotm*GLAbstraction.translationmatrix(-pv)
    else
        eye(GeometryTypes.Mat4f0)
    end
end
function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, area)
    xticks, yticks, spine_segs, grid_segs = Plots.axis_drawing_info(sp)
    xaxis = sp[:xaxis]; yaxis = sp[:yaxis]

    c = Colors.color(Plots.gl_color(sp[:foreground_color_grid]))
    axis_vis = []
    if sp[:grid]
        grid = draw_grid_lines(sp, grid_segs, 1f0, :dot, model, RGBA(c, 0.3f0))
        push!(axis_vis, grid)
    end
    if alpha(xaxis[:foreground_color_border]) > 0
        spine = draw_grid_lines(sp, spine_segs, 1f0, :solid, model, RGBA(c, 1.0f0))
        push!(axis_vis, spine)
    end
    fcolor = Plots.gl_color(xaxis[:foreground_color_axis])

    xlim = Plots.axis_limits(xaxis)
    ylim = Plots.axis_limits(yaxis)
    m = Reactive.value(model)
    xs, ys = m[1,1], m[2,2]
    # TODO: we should make sure we actually need to draw these...
    t, positions, offsets = draw_ticks(xaxis, xticks, :top, Point2f0(0, 7/ys), true, ylim, model)
    t, positions, offsets = draw_ticks(yaxis, yticks, :right, Point2f0(7/xs, 0), false, xlim, model, t, positions, offsets)
    sz = xaxis[:tickfont].pointsize
    kw_args = Dict{Symbol, Any}(
        :position => positions,
        :offset => offsets,
        :color => fcolor,
        :relative_scale =>  Vec2f0(3/sz),
        :model => model,
        :scale_primitive => false
    )
    if !(xaxis[:ticks] in (nothing,false,:none))
        push!(axis_vis, visualize(t, Style(:default), kw_args))
    end
    area_w = GeometryTypes.widths(area)
    if sp[:title] != ""
        tf = sp[:titlefont]; color = gl_color(sp[:foreground_color_title])
        font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, color)
        xy = Point2f0(area.w/2, area_w[2])
        kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
        extract_font(font, kw)
        t = PlotText(sp[:title], font)
        push!(axis_vis, text(xy, t, kw))
    end
    if xaxis[:guide] != ""
        tf = xaxis[:guidefont]; color = gl_color(xaxis[:foreground_color_guide])
        xy = Point2f0(area.w/2, 0)
        font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, color)
        kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
        t = PlotText(xaxis[:guide], font)
        extract_font(font, kw)
        push!(axis_vis, text(xy, t, kw))
    end

    if yaxis[:guide] != ""
        tf = yaxis[:guidefont]; color = gl_color(yaxis[:foreground_color_guide])
        font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, color)
        xy = Point2f0(0, area.h/2)
        kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
        t = PlotText(yaxis[:guide], font)
        extract_font(font, kw)
        push!(axis_vis, text(xy, t, kw))
    end

    axis_vis
end

function gl_draw_axes_3d(sp, model)
    x = Plots.axis_limits(sp[:xaxis])
    y = Plots.axis_limits(sp[:yaxis])
    z = Plots.axis_limits(sp[:zaxis])

    min = Vec3f0(x[1], y[1], z[1])
    visualize(
        GeometryTypes.AABB{Float32}(min, Vec3f0(x[2], y[2], z[2])-min),
        :grid, model=model
    )
end

function gl_bar(d, kw_args)
    x, y = d[:x], d[:y]
    nx, ny = length(x), length(y)
    axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
    cv = [discrete_value!(axis, xi)[1] for xi=x]
    x = if nx == ny
        cv
    elseif nx == ny + 1
        0.5diff(cv) + cv[1:end-1]
    else
        error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
    end
    if haskey(kw_args, :stroke_width) # stroke is inside for bars
        #kw_args[:stroke_width] = -kw_args[:stroke_width]
    end
    # compute half-width of bars
    bw = nothing
    hw = if bw == nothing
        mean(diff(x))
    else
        Float64[cycle(bw,i)*0.5 for i=1:length(x)]
    end

    # make fillto a vector... default fills to 0
    fillto = d[:fillrange]
    if fillto == nothing
        fillto = 0
    end
    # create the bar shapes by adding x/y segments
    positions, scales = Array(Point2f0, ny), Array(Vec2f0, ny)
    m = Reactive.value(kw_args[:model])
    sx, sy = m[1,1], m[2,2]
    for i=1:ny
        center = x[i]
        hwi = abs(cycle(hw,i)); yi = y[i]; fi = cycle(fillto,i)
        if Plots.isvertical(d)
            sz = (hwi*sx, yi*sy)
        else
            sz = (yi*sx, hwi*2*sy)
        end
        positions[i] = (center-hwi*0.5, fi)
        scales[i] = sz
    end

    kw_args[:scale] = scales
    kw_args[:offset] = Vec2f0(0)
    visualize((GLVisualize.RECTANGLE, positions), Style(:default), kw_args)
    #[]
end

const _box_halfwidth = 0.4

notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)

function gl_boxplot(d, kw_args)
    kwbox = copy(kw_args)
    range = 1.5; notch = false
    x, y = d[:x], d[:y]
    glabels = sort(collect(unique(x)))
    warning = false
    outliers_x, outliers_y = zeros(0), zeros(0)

    box_pos = Point2f0[]
    box_scale = Vec2f0[]
    outliers = Point2f0[]
    t_segments = Point2f0[]
    m = Reactive.value(kw_args[:model])
    sx, sy = m[1,1], m[2,2]
    for (i,glabel) in enumerate(glabels)
        # filter y
        values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
        # compute quantiles
        q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
        # notch
        n = Plots.notch_width(q2, q4, length(values))
        # warn on inverted notches?
        if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
            warn("Boxplot's notch went outside hinges. Set notch to false.")
            warning = true # Show the warning only one time
        end

        # make the shape
        center = Plots.discrete_value!(d[:subplot][:xaxis], glabel)[1]
        hw = d[:bar_width] == nothing ? Plots._box_halfwidth*2 : cycle(d[:bar_width], i)
        l, m, r = center - hw/2, center, center + hw/2

        # internal nodes for notches
        L, R = center - 0.5 * hw, center + 0.5 * hw
        # outliers
        if Float64(range) != 0.0  # if the range is 0.0, the whiskers will extend to the data
            limit = range*(q4-q2)
            inside = Float64[]
            for value in values
                if (value < (q2 - limit)) || (value > (q4 + limit))
                    push!(outliers, (center, value))
                else
                    push!(inside, value)
                end
            end
            # change q1 and q5 to show outliers
            # using maximum and minimum values inside the limits
            q1, q5 = extrema(inside)
        end
        # Box
        if notch
            push!(t_segments, (m, q1), (l, q1), (r, q1), (m, q1), (m, q2))# lower T
            push!(box_pos, (l, q2));push!(box_scale, (hw*sx, n*sy)) # lower box
            push!(box_pos, (l, q4));push!(box_scale, (hw*sx, n*sy)) # upper box
            push!(t_segments, (m, q5), (l, q5), (r, q5), (m, q5), (m, q4))# upper T

        else
            push!(t_segments, (m, q2), (m, q1), (l, q1), (r, q1))# lower T
            push!(box_pos, (l, q2)); push!(box_scale, (hw*sx, (q3-q2)*sy)) # lower box
            push!(box_pos, (l, q4)); push!(box_scale, (hw*sx, (q3-q4)*sy)) # upper box
            push!(t_segments, (m, q4), (m, q5), (r, q5), (l, q5))# upper T
        end
    end
    kwbox = Dict{Symbol, Any}(
        :scale => box_scale,
        :model => kw_args[:model],
        :offset => Vec2f0(0),
    )
    extract_marker(d, kw_args)
    outlier_kw = Dict(
        :model => kw_args[:model],
        :color =>  scalar_color(d, :fill),
        :stroke_width => Float32(d[:markerstrokewidth]),
        :stroke_color => scalar_color(d, :markerstroke),
    )
    lines_kw = Dict(
        :model => kw_args[:model],
        :stroke_width =>  d[:linewidth],
        :stroke_color =>  scalar_color(d, :fill),
    )
    vis1 = GLVisualize.visualize((GLVisualize.RECTANGLE, box_pos), Style(:default), kwbox)
    vis2 = GLVisualize.visualize((GLVisualize.CIRCLE, outliers), Style(:default), outlier_kw)
    vis3 = GLVisualize.visualize(t_segments, Style(:linesegment), lines_kw)
    [vis1, vis2, vis3]
end

# ---------------------------------------------------------------------------
function gl_viewport(bb, rect)
    l, b, bw, bh = bb
    rw, rh = rect.w, rect.h
    GLVisualize.SimpleRectangle(
        round(Int, rect.x + rw * l),
        round(Int, rect.y + rh * b),
        round(Int, rw * bw),
        round(Int, rh * bh)
    )
end

function to_modelmatrix(rect, subrect, rel_plotarea, sp)
    xmin, xmax = Plots.axis_limits(sp[:xaxis])
    ymin, ymax = Plots.axis_limits(sp[:yaxis])
    mini, maxi = Vec3f0(xmin, ymin, 0), Vec3f0(xmax, ymax, 1)
    if Plots.is3d(sp)
        zmin, zmax = Plots.axis_limits(sp[:zaxis])
        mini, maxi = Vec3f0(xmin, ymin, zmin), Vec3f0(xmax, ymax, zmax)
        s = Vec3f0(1) ./ (maxi-mini)
        return GLAbstraction.scalematrix(s)*GLAbstraction.translationmatrix(-mini)
    end
    l, b, bw, bh = rel_plotarea
    w, h = rect.w*bw, rect.h*bh
    x, y = rect.w*l - subrect.x, rect.h*b - subrect.y
    t = -mini
    s = Vec3f0(w, h, 1) ./ (maxi-mini)
    GLAbstraction.translationmatrix(Vec3f0(x,y,0))*GLAbstraction.scalematrix(s)*GLAbstraction.translationmatrix(t)
end

# ----------------------------------------------------------------

function _display(plt::Plot{GLVisualizeBackend})
    screen = plt.o
    empty_screen!(screen)
    sw, sh = plt[:size]
    sw, sh = sw*px, sh*px
    # TODO: use plt.subplots... plt.spmap can't be trusted
    for (name, sp) in plt.spmap
        _3d = Plots.is3d(sp)
        # camera = :perspective
        # initialize the sub-screen for this subplot
        # note: we create a lift function to update the size on resize

        rel_bbox = Plots.bbox_to_pcts(bbox(sp), sw, sh)
        sub_area = map(screen.area) do rect
            Plots.gl_viewport(rel_bbox, rect)
        end
        c = plt[:background_color_outside]
        sp_screen = GLVisualize.Screen(
            screen, name = name, color = c,
            area = sub_area
        )
        cam = get!(sp_screen.cameras, :perspective) do
            inside = sp_screen.inputs[:mouseinside]
            theta = _3d ? nothing : Signal(Vec3f0(0)) # surpress rotation for 2D (nothing will get usual rotation controle)
            GLAbstraction.PerspectiveCamera(
                sp_screen.inputs, Vec3f0(3), Vec3f0(0),
                keep=inside, theta=theta
            )
        end

        sp.o = sp_screen
        rel_plotarea = Plots.bbox_to_pcts(plotarea(sp), sw, sh)
        model_m = map(Plots.to_modelmatrix, screen.area, sub_area, Signal(rel_plotarea), Signal(sp))
        for ann in sp[:annotations]
            x, y, plot_text = ann
            txt_args = Dict{Symbol, Any}(:model => eye(GeometryTypes.Mat4f0))
            x, y, _1, _1 = Reactive.value(model_m) * Vec{4,Float32}(x, y, 0, 1)
            extract_font(plot_text.font, txt_args)
            t = text(Point2f0(x, y), plot_text, txt_args)
            GLVisualize._view(t, sp_screen, camera=cam)
        end
        # loop over the series and add them to the subplot
        if !_3d
            axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area))
            GLVisualize._view(axis, sp_screen, camera=cam)
            cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
            Reactive.run_till_now() # make sure Reactive.push! arrives
            GLAbstraction.center!(cam,
                GeometryTypes.AABB(
                    Vec3f0(-10), Vec3f0((GeometryTypes.widths(sp_screen)+20f0)..., 1)
                )
            )
        else
            axis = gl_draw_axes_3d(sp, model_m)
            GLVisualize._view(axis, sp_screen, camera=cam)
            push!(cam.projectiontype, GLVisualize.PERSPECTIVE)
        end
        for series in  Plots.series_list(sp)
            d = series.d
            st = d[:seriestype]; kw_args = KW() # exctract kw
            kw_args[:model] = model_m # add transformation
            if !_3d # 3D is treated differently, since we need boundingboxes for camera
                kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it
            end

            if st in (:surface, :wireframe)
                x, y, z = extract_surface(d)
                extract_gradient(d, kw_args, :fill)
                z = Plots.transpose_z(d, z, false)
                if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
                    x, y = Plots.transpose_z(d, x, false), Plots.transpose_z(d, y, false)
                end
                if st == :wireframe
                    kw_args[:wireframe] = true
                    kw_args[:stroke_color] = d[:linecolor]
                    kw_args[:stroke_width] = Float32(d[:linewidth]/100f0)
                end
                vis = GL.gl_surface(x, y, z, kw_args)
            elseif (st in (:path, :path3d)) && d[:linewidth] > 0
                kw = copy(kw_args)
                points = Plots.extract_points(d)
                extract_linestyle(d, kw)
                vis = GL.gl_lines(points, kw)
                if d[:markershape] != :none
                    kw = copy(kw_args)
                    extract_stroke(d, kw)
                    extract_marker(d, kw)
                    vis2 = GL.gl_scatter(copy(points), kw)
                    vis = [vis; vis2]
                end
                if d[:fillrange] != nothing
                    kw = copy(kw_args)
                    fr = d[:fillrange]
                    ps = if all(x->x>=0, diff(d[:x])) # if is monotonic
                        vcat(points, Point2f0[(points[i][1], cycle(fr, i)) for i=length(points):-1:1])
                    else
                        points
                    end
                    extract_c(d, kw, :fill)
                    vis = [GL.gl_poly(ps, kw), vis]
                end
            elseif st in (:scatter, :scatter3d) #|| d[:markershape] != :none
                extract_marker(d, kw_args)
                points = extract_points(d)
                vis = GL.gl_scatter(points, kw_args)
            elseif st == :shape
                extract_c(d, kw_args, :fill)
                vis = GL.gl_shape(d, kw_args)
            elseif st == :contour
                x,y,z = extract_surface(d)
                z = transpose_z(d, z, false)
                extract_extrema(d, kw_args)
                extract_gradient(d, kw_args, :fill)
                kw_args[:fillrange] = d[:fillrange]
                kw_args[:levels] = d[:levels]

                vis = GL.gl_contour(x,y,z, kw_args)
            elseif st == :heatmap
                x,y,z = extract_surface(d)
                extract_gradient(d, kw_args, :fill)
                extract_extrema(d, kw_args)
                extract_limits(sp, d, kw_args)
                vis = GL.gl_heatmap(x,y,z, kw_args)
            elseif st == :bar
                extract_c(d, kw_args, :fill)
                extract_stroke(d, kw_args, :marker)
                vis = gl_bar(d, kw_args)
            elseif st == :image
                extract_extrema(d, kw_args)
                z = transpose_z(series, d[:z].surf, false)
                vis = GL.gl_image(z, kw_args)
            elseif st == :boxplot
                 extract_c(d, kw_args, :fill)
                 vis = gl_boxplot(d, kw_args)
             elseif st == :volume
                  volume = d[:y]
                  _d = copy(d)
                  _d[:y] = 0:1
                  _d[:x] = 0:1
                  kw_args = KW()
                  extract_gradient(_d, kw_args, :fill)
                  vis = visualize(volume.v, Style(:default), kw_args)
             else
                error("failed to display plot type $st")
            end
            if isa(vis, Array) && isempty(vis)
                continue # nothing to see here
            end
            GLVisualize._view(vis, sp_screen, camera=cam)
            if haskey(d, :hover) && !(d[:hover] in (false, :none, nothing))
                hover(vis, d[:hover], sp_screen)
            end
            if isdefined(:GLPlot) && isdefined(Main.GLPlot, :(register_plot!))
                del_signal = Main.GLPlot.register_plot!(vis, sp_screen)
                append!(_glplot_deletes, del_signal)
            end
        end
        if _3d
            GLAbstraction.center!(sp_screen)
        end
    end
    Reactive.post_empty()
    yield()
end

function _show(io::IO, ::MIME"image/png", plt::Plot{GLVisualizeBackend})
    _display(plt)
    GLWindow.pollevents()
    yield()
    if Base.n_avail(Reactive._messages) > 0
        Reactive.run_till_now()
    end
    GLWindow.render_frame(plt.o)
    GLWindow.swapbuffers(plt.o)
    buff = GLWindow.screenbuffer(plt.o)
    png = Images.Image(buff,
        colorspace = "sRGB",
        spatialorder = ["y", "x"]
    )
    FileIO.save(FileIO.Stream(FileIO.DataFormat{:PNG}, io), png)
end

function gl_image(img, kw_args)
    rect = kw_args[:primitive]
    kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.h, rect.w) # seems to be flipped
    visualize(img, Style(:default), kw_args)
end

function handle_segment{P}(lines, line_segments, points::Vector{P}, segment)
    (isempty(segment) || length(segment) < 2) && return
    if length(segment) == 2
         append!(line_segments, view(points, segment))
    elseif length(segment) == 3
        p = view(points, segment)
        push!(line_segments, p[1], p[2], p[2], p[3])
    else
        append!(lines, view(points, segment))
        push!(lines, P(NaN))
    end
end

function gl_lines(points, kw_args)
    result = []
    isempty(points) && return result
    P = eltype(points)
    lines = P[]
    line_segments = P[]
    last = 1
    for (i,p) in enumerate(points)
        if isnan(p) || i==length(points)
            _i = isnan(p) ? i-1 : i
            handle_segment(lines, line_segments, points, last:_i)
            last = i+1
        end
    end
    if !isempty(lines)
        pop!(lines) # remove last NaN
        push!(result, visualize(lines, Style(:lines), kw_args))
    end
    if !isempty(line_segments)
        push!(result, visualize(line_segments, Style(:linesegment), kw_args))
    end
    return result
end
function gl_shape(d, kw_args)
    points = Plots.extract_points(d)
    result = []
    for rng in iter_segments(d[:x], d[:y])
        ps = points[rng]
        meshes = gl_poly(ps, kw_args)
        append!(result, meshes)
    end
    result
end
tovec2(x::FixedSizeArrays.Vec{2, Float32}) = x
tovec2(x::AbstractVector) = map(tovec2, x)
tovec2(x::FixedSizeArrays.Vec) = Vec2f0(x[1], x[2])

function gl_scatter(points, kw_args)
    prim = get(kw_args, :primitive, GeometryTypes.Circle)
    if isa(prim, GLNormalMesh)
        kw_args[:scale] = map(kw_args[:model]) do m
            s = m[1,1], m[2,2], m[3,3]
            1f0./Vec3f0(s)
        end
    else # 2D prim
        kw_args[:scale] = tovec2(kw_args[:scale])
    end
    if haskey(kw_args, :stroke_width)
        s = Reactive.value(kw_args[:scale])
        sw = kw_args[:stroke_width]
        if sw*5 > cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales)
            kw_args[:stroke_width] = s[1]/5f0
        end
    end
    kw_args[:scale_primitive] = false
    visualize((prim, points), Style(:default), kw_args)
end

function gl_poly(points, kw_args)
    last(points) == first(points) && pop!(points)
    polys = GeometryTypes.split_intersections(points)
    result = []
    for poly in polys
        mesh = GLNormalMesh(poly) # make polygon
        if !isempty(GeometryTypes.faces(mesh)) # check if polygonation has any faces
            push!(result, GLVisualize.visualize(mesh, Style(:default), kw_args))
        else
            warn("Couldn't draw the polygon: $points")
        end
    end
    result
end

function gl_surface(x,y,z, kw_args)
    if isa(x, Range) && isa(y, Range)
        main = z
        kw_args[:ranges] = (x, y)
    else
        if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
            main = map(s->map(Float32, s), (x, y, z))
        elseif isa(x, AbstractVector) || isa(y, AbstractVector)
            x = Float32[x[i] for i=1:size(z,1), j=1:size(z,2)]
            y = Float32[y[j] for i=1:size(z,1), j=1:size(z,2)]
            main = (x, y, map(Float32, z))
        else
            error("surface: combination of types not supported: $(typeof(x)) $(typeof(y)) $(typeof(z))")
        end
        if get(kw_args, :wireframe, false)
            points = map(Point3f0, zip(vec(x), vec(y), vec(z)))
            faces = Cuint[]
            idx = (i,j) -> sub2ind(size(z), i, j) - 1
            for i=1:size(z,1), j=1:size(z,2)
                i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
                j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1))
            end
            color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
            kw_args[:color] = color
            kw_args[:thickness] = get(kw_args, :stroke_width, 1f0)
            kw_args[:indices] = faces
            delete!(kw_args, :stroke_color)
            delete!(kw_args, :stroke_width)

            return visualize(points, Style(:linesegment), kw_args)
        end
    end
    return visualize(main, Style(:surface), kw_args)
end

function gl_contour(x,y,z, kw_args)
    if kw_args[:fillrange] != nothing
        delete!(kw_args, :intensity)
        I = GLVisualize.Intensity{1, Float32}
        main = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
        return visualize(main, Style(:default), kw_args)
    else
        h = kw_args[:levels]
        levels = Contour.contours(x, y, z, h)
        result = Point2f0[]
        zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z)))
        cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
        colors = RGBA{Float32}[]
        for c in levels.contours
            for elem in c.lines
                append!(result, elem.vertices)
                push!(result, Point2f0(NaN32))
                col = GLVisualize.color_lookup(cmap, c.level, zmin, zmax)
                append!(colors, fill(col, length(elem.vertices)+1))
            end
        end
        kw_args[:color] = colors
        kw_args[:color_map] = nothing
        kw_args[:color_norm] = nothing
        return visualize(result, Style(:lines),kw_args)
    end
end

function gl_heatmap(x,y,z, kw_args)
    get!(kw_args, :color_norm, Vec2f0(extrema(z)))
    get!(kw_args, :color_map, Plots.make_gradient(cgrad()))
    delete!(kw_args, :intensity)
    I = GLVisualize.Intensity{1, Float32}
    heatmap = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
    tex = GLAbstraction.Texture(heatmap, minfilter=:nearest)
    kw_args[:stroke_width] = 0f0
    kw_args[:levels] = 1f0
    visualize(tex, Style(:default), kw_args)
end

function text_plot(text, alignment, kw_args)
    transmat = kw_args[:model]
    obj = visualize(text, Style(:default), kw_args)
    bb = value(GLAbstraction.boundingbox(obj))
    w,h,_ = widths(bb)
    x,y,_ = minimum(bb)
    pivot = origin(alignment)
    pos = pivot - (Point2f0(x, y) .* widths(alignment))
    if kw_args[:rotation] != 0.0
        rot = GLAbstraction.rotationmatrix_z(Float32(font.rotation))
        transmat *= translationmatrix(pivot)*rot*translationmatrix(-pivot)
    end

    transmat *= GLAbstraction.translationmatrix(Vec3f0(pos..., 0))
    GLAbstraction.transformation(obj, transmat)
    view(obj, img.screen, camera=:orthographic_pixel)
end


text:
module JavaCodeGeneration

using Core: print
export loadclass

using Base.Iterators

using DataStructures

using JavaCall.CodeGeneration
using JavaCall.Reflection
using JavaCall.Utils

using JavaCall.JNI

const DEFAULT_SYMBOLS = Set([
    :Bool,
    :Int8,
    :Char,
    :Int16,
    :Int32,
    :Int64,
    :Float32,
    :Float64,
    :Nothing
])

const SHALLOW_LOADED_SYMBOLS =
    DefaultDict{Module, Set{Symbol}}(() -> copy(DEFAULT_SYMBOLS))

const FULLY_LOADED_SYMBOLS =
    DefaultDict{Module, Set{Symbol}}(() -> copy(DEFAULT_SYMBOLS))

structidfromtypeid(typeid::Symbol) = Symbol(typeid, "JuliaImpl")

paramnamefromindex(i::Int64) = Symbol("param", i)

paramexprfromtuple(x::Tuple{Int64, ClassDescriptor}) = :($(paramnamefromindex(x[1]))::$(x[2].juliatype))

function generateconvertarg(x::Tuple{Int64, ClassDescriptor})
    :(push!(args, JavaCall.Conversions.convert_to_jni($(x[2].jnitype), $(paramnamefromindex(x[1])))))
end

function loadclassfromobject(m::Module, object::jobject)
    class = JNI.get_object_class(object)
    loadclass(m, Reflection.descriptorfromclass(class))
end

function methodfromdescriptors(
    m::Module,
    classdescriptor::ClassDescriptor,
    methoddescriptor::MethodDescriptor
)
    methodfromdescriptors(
        Val(isstatic(methoddescriptor)),
        m::Module,
        classdescriptor,
        methoddescriptor
    )
end

function methodfromdescriptors(
    ::Val{true},
    m::Module,
    classdescriptor::ClassDescriptor,
    methoddescriptor::MethodDescriptor
)
    paramtypes = map(paramexprfromtuple, enumerate(methoddescriptor.paramtypes))
    signature = string(
        '(',
        map(x->x.signature, methoddescriptor.paramtypes)...,
        ')',
        methoddescriptor.rettype.signature)
    body = quote
        args = jvalue[]
        $(map(generateconvertarg, enumerate(methoddescriptor.paramtypes))...)
        result = JavaCall.Core.callstaticmethod(
            $(classdescriptor.jniclass),
            $(QuoteNode(Symbol(methoddescriptor.name))),
            $(methoddescriptor.rettype.jnitype),
            $signature,
            args...)

        $(generateexceptionhandling(m))

        if isa(result, jobject)
            Core.eval($m, JavaCall.JavaCodeGeneration.loadclassfromobject($m, result))
        end
        JavaCall.Conversions.convert_to_julia($(methoddescriptor.rettype.juliatype), result)
    end
    generatemethod(
        Symbol("j_", snakecase_from_camelcase(methoddescriptor.name)),
        [:(::Type{$(classdescriptor.juliatype)}), paramtypes...],
        body)
end

function methodfromdescriptors(
    ::Val{false},
    m::Module,
    receiverdescriptor::ClassDescriptor,
    descriptor::MethodDescriptor
)
    paramtypes = map(paramexprfromtuple, enumerate(descriptor.paramtypes))
    pushfirst!(paramtypes, :(receiver::$(receiverdescriptor.juliatype)))
    signature = string(
        '(',
        map(x->x.signature, descriptor.paramtypes)...,
        ')',
        descriptor.rettype.signature)

    body = quote
        obj = JavaCall.Conversions.convert_to_jni(jobject, receiver)
        args = jvalue[]
        $(map(generateconvertarg, enumerate(descriptor.paramtypes))...)
        result = JavaCall.Core.callinstancemethod(
            obj,
            $(QuoteNode(Symbol(descriptor.name))),
            $(descriptor.rettype.jnitype),
            $signature,
            args...)

        $(generateexceptionhandling(m))

        if isa(result, jobject)
            Core.eval($m, JavaCall.JavaCodeGeneration.loadclassfromobject($m, result))
        end
        JavaCall.Conversions.convert_to_julia($(descriptor.rettype.juliatype), result)
    end
    generatemethod(
        Symbol("j_", snakecase_from_camelcase(descriptor.name)),
        paramtypes,
        body)
end

function constructorfromdescriptors(
    classdescriptor::ClassDescriptor,
    constructordescriptor::ConstructorDescriptor
)
    paramtypes = map(paramexprfromtuple, enumerate(constructordescriptor.paramtypes))
    # As specified in the JNI reference object contructor methods signatures
    # should return void(V)
    signature = string(
        '(',
        map(x->x.signature, constructordescriptor.paramtypes)...,
        ")V")
    body = quote
        args = jvalue[]
        $(map(generateconvertarg, enumerate(constructordescriptor.paramtypes))...)
        result = JavaCall.Core.callconstructor(
            $(classdescriptor.jniclass),
            $signature,
            args...)
        JavaCall.Conversions.convert_to_julia($(classdescriptor.juliatype), result)
    end
    generatemethod(
        classdescriptor.juliatype,
        paramtypes,
        body)
end

loadclass(m::Module, classname::Symbol, shallow=false) =
    loadclass(m, findclass(classname), shallow)

function loadclass(m::Module, classdescriptor::ClassDescriptor, shallow=false)
    if isarray(classdescriptor)
        return generateblock(loadclass(m, classdescriptor.component, true))
    end

    exprstoeval = []

    if !shallowcomponentsloeaded(m, classdescriptor)
        loadshallowcomponents!(m, exprstoeval, classdescriptor)
    end

    if !shallow && !fullcomponentsloaded(m, classdescriptor)
        loadfullcomponents!(m, exprstoeval, classdescriptor)
    end

    generateblock(exprstoeval...)
end

## Loading of shallow components (minimal components required for the code to function)

shallowcomponentsloeaded(m::Module, d::ClassDescriptor) = d.juliatype in SHALLOW_LOADED_SYMBOLS[m]

function loadshallowcomponents!(m::Module, exprstoeval, classdescriptor)
    loadtype!(m, exprstoeval, classdescriptor)
    loadstruct!(exprstoeval, classdescriptor)
    loadconversions!(exprstoeval, classdescriptor)
    push!(SHALLOW_LOADED_SYMBOLS[m], classdescriptor.juliatype)
end

function loadtype!(m, exprstoeval, classdescriptor)
    typeid = classdescriptor.juliatype

    if !isinterface(classdescriptor) &&
        superclass(classdescriptor) !== nothing

        loadshallowcomponents!(m, exprstoeval, superclass(classdescriptor))
        push!(exprstoeval, generatetype(typeid, superclass(classdescriptor).juliatype))
    else
        push!(exprstoeval, generatetype(typeid))
    end
end

function loadstruct!(exprstoeval, classdescriptor)
    typeid = classdescriptor.juliatype
    structid = structidfromtypeid(typeid)
    push!(exprstoeval, generatestruct(structid, typeid, (:ref, :jobject)))
end

function loadconversions!(exprstoeval, classdescriptor)
    typeid = classdescriptor.juliatype
    structid = structidfromtypeid(typeid)
    push!(
        exprstoeval,
        generatemethod(
            :(JavaCall.Conversions.convert_to_julia),
            [:(::Type{$typeid}), :(x::jobject)],
            :($structid(x))
        ),
        generatemethod(
            :(JavaCall.Conversions.convert_to_jni),
            [:(::Type{jobject}), :(x::$typeid)],
            :(x.ref)
        )
    )
end

## Loading of full components (fully generate the code for the class
## such as methods and constructors)

fullcomponentsloaded(m::Module, d::ClassDescriptor) =
    d.juliatype in FULLY_LOADED_SYMBOLS[m]

function loadfullcomponents!(m::Module, exprstoeval, class::ClassDescriptor)
    loadsuperclass!(m, exprstoeval, class)
    methods = filter(ispublic, classdeclaredmethods(class))
    constructors = classconstructors(class)
    loaddependencies!(m, exprstoeval, methods)
    loaddependencies!(m, exprstoeval, constructors)
    loadmethods!(m, exprstoeval, class, methods)
    loadconstructors!(exprstoeval, class, constructors)
    loadjuliamethods!(exprstoeval, class)
    loadcustommethods!(Val(class.juliatype), exprstoeval, class)
    push!(FULLY_LOADED_SYMBOLS[m], class.juliatype)
end

function loadsuperclass!(m, exprstoeval, class)
    if !isinterface(class) && superclass(class) !== nothing
        push!(exprstoeval, loadclass(m, superclass(class)))
    end
end

function loaddependencies!(m, exprstoeval, methods::Vector{MethodDescriptor})
    dependencies =
        flatmap(m -> [m.rettype, m.paramtypes...], methods) |>
        l -> map(x -> loadclass(m, x, true), l)

    push!(exprstoeval, dependencies...)
end

function loaddependencies!(m, exprstoeval, constructors::Vector{ConstructorDescriptor})
    dependencies =
        flatmap(c -> c.paramtypes, constructors) |>
        l -> map(x -> loadclass(m, x, true), l)

    push!(exprstoeval, dependencies...)
end

function loadmethods!(m, exprstoeval, class, methods)
    push!(exprstoeval, map(x -> methodfromdescriptors(m, class, x), methods)...)
end

function loadconstructors!(exprstoeval, class, constructors)
    push!(exprstoeval, map(x -> constructorfromdescriptors(class, x), constructors)...)
end

function loadjuliamethods!(exprstoeval, class)
    typeid = class.juliatype
    push!(
        exprstoeval,
        # generatemethod(
        #     :(Base.show),
        #     [:(io::IO), :(o::$typeid)],
        #     :(print(io, JavaCall.Conversions.convert_to_string(String, j_to_string(o).ref)))),
        generatemethod(
            :(Base.:(==)),
            [:(o1::$typeid), :(o2::$typeid)],
            :(j_equals(o1, o2))
        )
    )
end

# Function to implement for concrete types to add
# custom methods to a given loaded class
loadcustommethods!(::Val{T}, exprstoeval, class) where T = nothing

function loadcustommethods!(::Val{:JString}, exprstoeval, class)
    push!(
        exprstoeval,
        # Add an extra constructor to facilitate
        # construction of java strings from julia strings
        generatemethod(
            :JString,
            [:(s::T)],
            :(JavaCall.Conversions.convert_to_julia(
                JString,
                JavaCall.JNI.new_string(map(JavaCall.JNI.jchar, collect(s)), length(s)))),
            :(T <: AbstractString)
        ),
        # Add extra string method from JString
        generatemethod(
            :string,
            [:(s::JString)],
            :(JavaCall.Conversions.convert_to_string(String, s.ref))
        )
    )
end

function generateexceptionhandling(m::Module)
    quote
        if JavaCall.JNI.exception_check() === JavaCall.JNI.JNI_TRUE
            exception = JavaCall.JNI.exception_occurred()
            class = JavaCall.JNI.get_object_class(exception)
            desc = JavaCall.Reflection.descriptorfromclass(class)
            Core.eval($m, JavaCall.JavaCodeGeneration.loadclass($m, desc))
            JavaCall.JNI.exception_clear()
            throw(Core.eval($m, quote
                JavaCall.Conversions.convert_to_julia(
                    $(desc.juliatype),
                    $exception
                )
                end
            ))
        end
    end
end

end

ext: jl


text:
export cordoba_strg_tnep
""
function cordoba_strg_tnep(data::Dict{String,Any}, model_type::Type, solver; ref_extensions = [_PMACDC.add_ref_dcgrid!, _PMACDC.add_candidate_dcgrid!, _FP.add_candidate_storage!, _PM.ref_add_on_off_va_bounds!, _PM.ref_add_ne_branch!], setting = s, kwargs...)
    s = setting
    return _PM.run_model(data, model_type, solver, cordoba_post_strg_tnep; ref_extensions = [_PMACDC.add_ref_dcgrid!, _PMACDC.add_candidate_dcgrid!, _FP.add_candidate_storage!, _PM.ref_add_on_off_va_bounds!, _PM.ref_add_ne_branch!], setting = s, kwargs...)
end

# for distribution models
""
function cordoba_strg_tnep(data::Dict{String,Any}, model_type::Type{T}, optimizer; kwargs...) where T <: _PM.AbstractBFModel
    return _PM.run_model(data, model_type, optimizer, cordoba_post_strg_tnep;
                         ref_extensions = [add_candidate_storage!, _PM.ref_add_on_off_va_bounds!, ref_add_ne_branch_allbranches!, ref_add_frb_branch!, ref_add_oltc_branch!],
                         solution_processors = [_PM.sol_data_model!],
                         kwargs...)
end

# Here the problem is defined, which is then sent to the solver.
# It is basically a declarion of variables and constraint of the problem
""
function cordoba_post_strg_tnep(pm::_PM.AbstractPowerModel)
# VARIABLES: defined within PowerModels(ACDC) can directly be used, other variables need to be defined in the according sections of the code: see storage.jl
    for n in _PM.nw_ids(pm)
        _PM.variable_bus_voltage(pm; nw = n)
        _PM.variable_gen_power(pm; nw = n)
        _PM.variable_branch_power(pm; nw = n)
        _PM.variable_storage_power(pm; nw = n)

        _PMACDC.variable_voltage_slack(pm; nw = n)
        _PMACDC.variable_active_dcbranch_flow(pm; nw = n)
        _PMACDC.variable_dc_converter(pm; nw = n)
        _PMACDC.variable_dcbranch_current(pm; nw = n)
        _PMACDC.variable_dcgrid_voltage_magnitude(pm; nw = n)
        _FP.variable_absorbed_energy(pm; nw = n)
        _FP.variable_absorbed_energy_ne(pm; nw = n)

        # new variables for TNEP problem
        _PM.variable_ne_branch_indicator(pm; nw = n)
        _PM.variable_ne_branch_power(pm; nw = n)
        _PM.variable_ne_branch_voltage(pm; nw = n)
        _FP.variable_storage_power_ne(pm; nw = n)
        _PMACDC.variable_active_dcbranch_flow_ne(pm; nw = n)
        _PMACDC.variable_branch_ne(pm; nw = n)
        _PMACDC.variable_dc_converter_ne(pm; nw = n)
        _PMACDC.variable_dcbranch_current_ne(pm; nw = n)
        _PMACDC.variable_dcgrid_voltage_magnitude_ne(pm; nw = n)
    end
#OBJECTIVE see objective.jl
    objective_min_cost_storage_cordoba(pm)
#CONSTRAINTS: defined within PowerModels(ACDC) can directly be used, other constraints need to be defined in the according sections of the code: storage.jl
    for n in _PM.nw_ids(pm)
        _PM.constraint_model_voltage(pm; nw = n)
        _PM.constraint_ne_model_voltage(pm; nw = n)
        _PMACDC.constraint_voltage_dc(pm; nw = n)
        _PMACDC.constraint_voltage_dc_ne(pm; nw = n)
        for i in _PM.ids(pm, n, :ref_buses)
            _PM.constraint_theta_ref(pm, i, nw = n)
        end

        for i in _PM.ids(pm, n, :bus)
            _FP.constraint_power_balance_acne_dcne_strg(pm, i; nw = n)
        end
        if haskey(pm.setting, "allow_line_replacement") && pm.setting["allow_line_replacement"] == true
            for i in _PM.ids(pm, n, :branch)
                _FP.constraint_ohms_yt_from_repl(pm, i; nw = n)
                _FP.constraint_ohms_yt_to_repl(pm, i; nw = n)
                _FP.constraint_voltage_angle_difference_repl(pm, i; nw = n)
                _FP.constraint_thermal_limit_from_repl(pm, i; nw = n)
                _FP.constraint_thermal_limit_to_repl(pm, i; nw = n)
            end
        else
            for i in _PM.ids(pm, n, :branch)
                _PM.constraint_ohms_yt_from(pm, i; nw = n)
                _PM.constraint_ohms_yt_to(pm, i; nw = n)
                _PM.constraint_voltage_angle_difference(pm, i; nw = n)
                _PM.constraint_thermal_limit_from(pm, i; nw = n)
                _PM.constraint_thermal_limit_to(pm, i; nw = n)
            end
        end
        for i in _PM.ids(pm, n, :ne_branch)
            _PM.constraint_ne_ohms_yt_from(pm, i; nw = n)
            _PM.constraint_ne_ohms_yt_to(pm, i; nw = n)
            _PM.constraint_ne_voltage_angle_difference(pm, i; nw = n)
            _PM.constraint_ne_thermal_limit_from(pm, i; nw = n)
            _PM.constraint_ne_thermal_limit_to(pm, i; nw = n)
            if n > 1
                _PMACDC.constraint_candidate_acbranches_mp(pm, n, i)
            end
        end

        for i in _PM.ids(pm, n, :busdc)
            _PMACDC.constraint_power_balance_dc_dcne(pm, i; nw = n)
        end
        for i in _PM.ids(pm, n, :busdc_ne)
            _PMACDC.constraint_power_balance_dcne_dcne(pm, i; nw = n)
        end

        for i in _PM.ids(pm, n, :branchdc)
            _PMACDC.constraint_ohms_dc_branch(pm, i; nw = n)
        end
        for i in _PM.ids(pm, n, :branchdc_ne)
            _PMACDC.constraint_ohms_dc_branch_ne(pm, i; nw = n)
            _PMACDC.constraint_branch_limit_on_off(pm, i; nw = n)
            if n > 1
                _PMACDC.constraint_candidate_dcbranches_mp(pm, n, i)
            end
        end

        for i in _PM.ids(pm, n, :convdc)
            _PMACDC.constraint_converter_losses(pm, i; nw = n)
            _PMACDC.constraint_converter_current(pm, i; nw = n)
            _PMACDC.constraint_conv_transformer(pm, i; nw = n)
            _PMACDC.constraint_conv_reactor(pm, i; nw = n)
            _PMACDC.constraint_conv_filter(pm, i; nw = n)
            if pm.ref[:nw][n][:convdc][i]["islcc"] == 1
                _PMACDC.constraint_conv_firing_angle(pm, i; nw = n)
            end
        end
        for i in _PM.ids(pm, n, :convdc_ne)
            _PMACDC.constraint_converter_losses_ne(pm, i; nw = n)
            _PMACDC.constraint_converter_current_ne(pm, i; nw = n)
            _PMACDC.constraint_converter_limit_on_off(pm, i; nw = n)
            if n > 1
                _PMACDC.constraint_candidate_converters_mp(pm, n, i)
            end
            _PMACDC.constraint_conv_transformer_ne(pm, i; nw = n)
            _PMACDC.constraint_conv_reactor_ne(pm, i; nw = n)
            _PMACDC.constraint_conv_filter_ne(pm, i; nw = n)
            if pm.ref[:nw][n][:convdc_ne][i]["islcc"] == 1
                _PMACDC.constraint_conv_firing_angle_ne(pm, i; nw = n)
            end
        end

        for i in _PM.ids(pm, :storage, nw=n)
            _FP.constraint_storage_excl_slack(pm, i, nw = n)
            _PM.constraint_storage_thermal_limit(pm, i, nw = n)
            _PM.constraint_storage_losses(pm, i, nw = n)
        end
        for i in _PM.ids(pm, :ne_storage, nw=n)
            _FP.constraint_storage_excl_slack_ne(pm, i, nw = n)
            _FP.constraint_storage_thermal_limit_ne(pm, i, nw = n)
            _FP.constraint_storage_losses_ne(pm, i, nw = n)
            _FP.constraint_storage_bounds_ne(pm, i, nw = n)
        end
    end

    network_ids = sort(collect(_PM.nw_ids(pm)))
    n_1 = network_ids[1]
    n_last = network_ids[end]

    for i in _PM.ids(pm, :storage, nw = n_1)
        _FP.constraint_storage_state(pm, i, nw = n_1)
        _FP.constraint_maximum_absorption(pm, i, nw = n_1)
    end

    for i in _PM.ids(pm, :ne_storage, nw = n_1)
        _FP.constraint_storage_state_ne(pm, i, nw = n_1)
        _FP.constraint_maximum_absorption_ne(pm, i, nw = n_1)
    end

    for i in _PM.ids(pm, :storage, nw = n_last)
        _FP.constraint_storage_state_final(pm, i, nw = n_last)
    end

    for i in _PM.ids(pm, :ne_storage, nw = n_last)
        _FP.constraint_storage_state_final_ne(pm, i, nw = n_last)
    end

    for n_2 in network_ids[2:end]
        for i in _PM.ids(pm, :storage, nw = n_2)
            _FP.constraint_storage_state(pm, i, n_1, n_2)
            _FP.constraint_maximum_absorption(pm, i, n_1, n_2)
        end
        for i in _PM.ids(pm, :ne_storage, nw = n_2)
            _FP.constraint_storage_state_ne(pm, i, n_1, n_2)
            _FP.constraint_maximum_absorption_ne(pm, i, n_1, n_2)
            _FP.constraint_storage_investment(pm, n_1, n_2, i)
        end
        n_1 = n_2
    end

end


text:
#
#  InternalForces.jl
#  CollagenPolymer
#
#  Created by Christopher Revell on 04/07/2020.
#
#

module InternalForces

# Import Julia packages
using LinearAlgebra
using StaticArrays
using LoopVectorization

# Import local program modules

function internalForces!(pos,F,nMonomers,nDomains,nParticles,k,rₑ,Ebend,AA,BB,CC,DD,EE)

	# Loop over all particles
	for jj=1:nParticles

		# Tension forces between monomer domains
		if jj%nDomains == 0
			# Skip at the end of each monomer
		else
			# Calculate tension forces between adjacent particles with Hookean spring potential

			# Find vector AA between 1st and 2nd particle
			AA = pos[jj+1] - pos[jj]
		    AA_mag = norm(AA)
			AA = AA/AA_mag
			#println("AA_mag = $AA_mag")

			dif = AA_mag - rₑ
			#println("dif = $dif")
		    F[jj]   += dif*k*AA
			#println("F[jj]   = $(F[jj]  )")
		    F[jj+1] -= dif*k*AA
			#println("F[jj+1] = $(F[jj+1])")

			# Bending forces
			if (jj+1)%nDomains == 0
				# Skip at the end of each monomer
			else
				# Vector BB between 2nd and 3rd particle
				BB = pos[jj+2] - pos[jj+1]
				BB_mag = norm(BB)
				BB = BB/BB_mag

				if dot(BB,AA) > 0.9999999999
					# Skip if colinear, ie cosθ=BB⋅AA≈1
				else

					# Cross produce of AA and BB gives vector CC, perpendicular to plane defined by AA and BB
					CC = AA×BB

					# Find vector DD perpendicular to AA and lying in plane of AA and BB with cross product of AA and CC
					DD = AA×CC
					DD_mag = norm(DD)
					DD = DD/DD_mag

					# Vector opposing the sum of CC and DD
					EE = BB×CC
					EE_mag = norm(EE)
					EE = EE/EE_mag

					θ    = acos(-AA⋅BB)
					Fmag = Ebend*θ
					F[jj]   += Fmag*DD
					F[jj+1] -= Fmag*(DD+EE)
					F[jj+2] += Fmag*EE

				end
			end
		end

	end
	return nothing
end

export internalForces!

end

max_stars_repo_name: chris-revell/collagen-model-jl

max_issues_repo_name: chris-revell/collagen-model-jl

max_forks_repo_name: chris-revell/collagen-model-jl


text:
export Site,
       ind,
       op,
       site,
       state,
       BasicSite,
       SiteSet,
       replaceBond!

abstract type Site end

ind(st::Site) = st.s

function operator(s::Site,opname::String)::ITensor
  error("Operator name $opname not recognized for generic site")
  return ITensor()
end

function op(site::Site,
            opname::AbstractString)::ITensor
  s = ind(site)
  sP = s'

  opname = strip(opname)

  Op = ITensor(dag(s),sP)
  if opname == "Id"
    for n=1:dim(s)
      Op[dag(s)(n),sP(n)] = 1.0
    end
  else
    starpos = findfirst("*",opname)
    if !isnothing(starpos)
      op1 = opname[1:starpos.start-1]
      op2 = opname[starpos.start+1:end]
      return multSiteOps(op(site,op1),op(site,op2))
    end
    return operator(site,opname)
  end
  return Op
end

struct BasicSite <: Site
  s::Index
  BasicSite(i::Index) = new(i)
end
BasicSite(d::Int) = BasicSite(Index(d,"Site"))
BasicSite(d::Int,n::Int) = BasicSite(Index(d,"Site,n=$n"))

struct SiteSet
  sites::Vector{Site}

  SiteSet() = new(Vector{Site}())

  SiteSet(N::Integer) = new(Vector{Site}(undef,N))

  function SiteSet(N::Integer, d::Integer)
    inds_ = Vector{Site}(undef,N)
    for n=1:N
      inds_[n] = BasicSite(d,n)
    end
    new(inds_)
  end
end

length(s::SiteSet) = length(s.sites)
getindex(s::SiteSet,n::Integer) = ind(s.sites[n])
op(s::SiteSet,opname::String,n::Int) = op(s.sites[n],opname)
set(s::SiteSet,n::Int,ns::Site) = (s.sites[n] = ns)
site(s::SiteSet,n::Int) = s.sites[n]

function show(io::IO,
              sites::SiteSet)
  print(io,"SiteSet")
  (length(sites) > 0) && print(io,"\n")
  for i=1:length(sites)
    println(io,"  $(sites[i])")
  end
end

function state(sset::SiteSet,
               n::Integer,
               st::Union{Integer,String})::IndexVal
  return state(sset.sites[n],st)
end


text:
using Documenter, IBMQClient, DocThemeIndigo
using IBMQClient.Schema
indigo = DocThemeIndigo.install(IBMQClient)

makedocs(;
    modules=[IBMQClient, Schema],
    authors="Roger-luo <rogerluo.rl18@gmail.com> and contributors",
    repo="https://github.com/QuantumBFS/IBMQClient.jl/blob/{commit}{path}#{line}",
    sitename="IBMQClient.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://QuantumBFS.github.io/IBMQClient.jl",
        assets=String[indigo, "assets/default.css"],
    ),
    pages=[
        "Quick Start" => "index.md",
        "Schema" => "schema.md",
    ],
)

deploydocs(;
    repo="github.com/QuantumBFS/IBMQClient.jl",
)


text:
# Julia wrapper for header: lsl_constants.h
# Automatically generated using Clang.jl

# Julia wrapper for header: lsl_c.h
# Automatically generated using Clang.jl

function lsl_protocol_version()
    ccall((:lsl_protocol_version, liblsl), Int32, ())
end

function lsl_library_version()
    ccall((:lsl_library_version, liblsl), Int32, ())
end

function lsl_library_info()
    ccall((:lsl_library_info, liblsl), Cstring, ())
end

function lsl_local_clock()
    ccall((:lsl_local_clock, liblsl), Cdouble, ())
end

function lsl_resolve_all(buffer, buffer_elements, wait_time)
    ccall((:lsl_resolve_all, liblsl), Int32, (Ptr{lsl_streaminfo}, UInt32, Cdouble), buffer, buffer_elements, wait_time)
end

function lsl_resolve_byprop(buffer, buffer_elements, prop, value, minimum, timeout)
    ccall((:lsl_resolve_byprop, liblsl), Int32, (Ptr{lsl_streaminfo}, UInt32, Cstring, Cstring, Int32, Cdouble), buffer, buffer_elements, prop, value, minimum, timeout)
end

function lsl_resolve_bypred(buffer, buffer_elements, pred, minimum, timeout)
    ccall((:lsl_resolve_bypred, liblsl), Int32, (Ptr{lsl_streaminfo}, UInt32, Cstring, Int32, Cdouble), buffer, buffer_elements, pred, minimum, timeout)
end

function lsl_destroy_string(s)
    ccall((:lsl_destroy_string, liblsl), Cvoid, (Cstring,), s)
end

function lsl_create_streaminfo(name, type, channel_count, nominal_srate, channel_format, source_id)
    ccall((:lsl_create_streaminfo, liblsl), lsl_streaminfo, (Cstring, Cstring, Int32, Cdouble, lsl_channel_format_t, Cstring), name, type, channel_count, nominal_srate, channel_format, source_id)
end

function lsl_destroy_streaminfo(info)
    ccall((:lsl_destroy_streaminfo, liblsl), Cvoid, (lsl_streaminfo,), info)
end

function lsl_copy_streaminfo(info)
    ccall((:lsl_copy_streaminfo, liblsl), lsl_streaminfo, (lsl_streaminfo,), info)
end

function lsl_get_name(info)
    ccall((:lsl_get_name, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_type(info)
    ccall((:lsl_get_type, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_channel_count(info)
    ccall((:lsl_get_channel_count, liblsl), Int32, (lsl_streaminfo,), info)
end

function lsl_get_nominal_srate(info)
    ccall((:lsl_get_nominal_srate, liblsl), Cdouble, (lsl_streaminfo,), info)
end

function lsl_get_channel_format(info)
    ccall((:lsl_get_channel_format, liblsl), lsl_channel_format_t, (lsl_streaminfo,), info)
end

function lsl_get_source_id(info)
    ccall((:lsl_get_source_id, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_version(info)
    ccall((:lsl_get_version, liblsl), Int32, (lsl_streaminfo,), info)
end

function lsl_get_created_at(info)
    ccall((:lsl_get_created_at, liblsl), Cdouble, (lsl_streaminfo,), info)
end

function lsl_get_uid(info)
    ccall((:lsl_get_uid, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_session_id(info)
    ccall((:lsl_get_session_id, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_hostname(info)
    ccall((:lsl_get_hostname, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_desc(info)
    ccall((:lsl_get_desc, liblsl), lsl_xml_ptr, (lsl_streaminfo,), info)
end

function lsl_get_xml(info)
    ccall((:lsl_get_xml, liblsl), Cstring, (lsl_streaminfo,), info)
end

function lsl_get_channel_bytes(info)
    ccall((:lsl_get_channel_bytes, liblsl), Int32, (lsl_streaminfo,), info)
end

function lsl_get_sample_bytes(info)
    ccall((:lsl_get_sample_bytes, liblsl), Int32, (lsl_streaminfo,), info)
end

function lsl_stream_info_matches_query(info, query)
    ccall((:lsl_stream_info_matches_query, liblsl), Int32, (lsl_streaminfo, Cstring), info, query)
end

function lsl_streaminfo_from_xml(xml)
    ccall((:lsl_streaminfo_from_xml, liblsl), lsl_streaminfo, (Cstring,), xml)
end

function lsl_create_outlet(info, chunk_size, max_buffered)
    ccall((:lsl_create_outlet, liblsl), lsl_outlet, (lsl_streaminfo, Int32, Int32), info, chunk_size, max_buffered)
end

function lsl_destroy_outlet(out)
    ccall((:lsl_destroy_outlet, liblsl), Cvoid, (lsl_outlet,), out)
end

function lsl_push_sample_f(out, data)
    ccall((:lsl_push_sample_f, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}), out, data)
end

function lsl_push_sample_d(out, data)
    ccall((:lsl_push_sample_d, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}), out, data)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_sample_l(out, data)
        ccall((:lsl_push_sample_l, liblsl), Int32, (lsl_outlet, Ptr{Clong}), out, data)
    end
end

function lsl_push_sample_i(out, data)
    ccall((:lsl_push_sample_i, liblsl), Int32, (lsl_outlet, Ptr{Int32}), out, data)
end

function lsl_push_sample_s(out, data)
    ccall((:lsl_push_sample_s, liblsl), Int32, (lsl_outlet, Ptr{Int16}), out, data)
end

function lsl_push_sample_c(out, data)
    ccall((:lsl_push_sample_c, liblsl), Int32, (lsl_outlet, Ptr{Cchar}), out, data)
end

function lsl_push_sample_v(out, data)
    ccall((:lsl_push_sample_v, liblsl), Int32, (lsl_outlet, Ptr{Cvoid}), out, data)
end

function lsl_push_sample_ft(out, data, timestamp)
    ccall((:lsl_push_sample_ft, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Cdouble), out, data, timestamp)
end

function lsl_push_sample_dt(out, data, timestamp)
    ccall((:lsl_push_sample_dt, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Cdouble), out, data, timestamp)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_sample_lt(out, data, timestamp)
        ccall((:lsl_push_sample_lt, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Cdouble), out, data, timestamp)
    end
end

function lsl_push_sample_it(out, data, timestamp)
    ccall((:lsl_push_sample_it, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Cdouble), out, data, timestamp)
end

function lsl_push_sample_st(out, data, timestamp)
    ccall((:lsl_push_sample_st, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Cdouble), out, data, timestamp)
end

function lsl_push_sample_ct(out, data, timestamp)
    ccall((:lsl_push_sample_ct, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Cdouble), out, data, timestamp)
end

function lsl_push_sample_vt(out, data, timestamp)
    ccall((:lsl_push_sample_vt, liblsl), Int32, (lsl_outlet, Ptr{Cvoid}, Cdouble), out, data, timestamp)
end

function lsl_push_sample_ftp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_ftp, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_dtp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_dtp, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_sample_ltp(out, data, timestamp, pushthrough)
        ccall((:lsl_push_sample_ltp, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Cdouble, Int32), out, data, timestamp, pushthrough)
    end
end

function lsl_push_sample_itp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_itp, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_stp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_stp, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_ctp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_ctp, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_strtp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_strtp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_vtp(out, data, timestamp, pushthrough)
    ccall((:lsl_push_sample_vtp, liblsl), Int32, (lsl_outlet, Ptr{Cvoid}, Cdouble, Int32), out, data, timestamp, pushthrough)
end

function lsl_push_sample_buf(out, data, lengths)
    ccall((:lsl_push_sample_buf, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}), out, data, lengths)
end

function lsl_push_sample_buft(out, data, lengths, timestamp)
    ccall((:lsl_push_sample_buft, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Cdouble), out, data, lengths, timestamp)
end

function lsl_push_sample_buftp(out, data, lengths, timestamp, pushthrough)
    ccall((:lsl_push_sample_buftp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Cdouble, Int32), out, data, lengths, timestamp, pushthrough)
end

function lsl_push_chunk_f(out, data, data_elements)
    ccall((:lsl_push_chunk_f, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Culong), out, data, data_elements)
end

function lsl_push_chunk_d(out, data, data_elements)
    ccall((:lsl_push_chunk_d, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Culong), out, data, data_elements)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_chunk_l(out, data, data_elements)
        ccall((:lsl_push_chunk_l, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Culong), out, data, data_elements)
    end
end

function lsl_push_chunk_i(out, data, data_elements)
    ccall((:lsl_push_chunk_i, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Culong), out, data, data_elements)
end

function lsl_push_chunk_s(out, data, data_elements)
    ccall((:lsl_push_chunk_s, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Culong), out, data, data_elements)
end

function lsl_push_chunk_c(out, data, data_elements)
    ccall((:lsl_push_chunk_c, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Culong), out, data, data_elements)
end

function lsl_push_chunk_ft(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_ft, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Culong, Cdouble), out, data, data_elements, timestamp)
end

function lsl_push_chunk_dt(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_dt, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Culong, Cdouble), out, data, data_elements, timestamp)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_chunk_lt(out, data, data_elements, timestamp)
        ccall((:lsl_push_chunk_lt, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Culong, Cdouble), out, data, data_elements, timestamp)
    end
end

function lsl_push_chunk_it(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_it, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Culong, Cdouble), out, data, data_elements, timestamp)
end

function lsl_push_chunk_st(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_st, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Culong, Cdouble), out, data, data_elements, timestamp)
end

function lsl_push_chunk_ct(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_ct, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Culong, Cdouble), out, data, data_elements, timestamp)
end

function lsl_push_chunk_strt(out, data, data_elements, timestamp)
    ccall((:lsl_push_chunk_strt, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Culong, Cdouble), out, data, data_elements, timestamp)
end

function lsl_push_chunk_ftp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_ftp, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_dtp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_dtp, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_chunk_ltp(out, data, data_elements, timestamp, pushthrough)
        ccall((:lsl_push_chunk_ltp, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
    end
end

function lsl_push_chunk_itp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_itp, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_stp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_stp, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_ctp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_ctp, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_strtp(out, data, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_strtp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Culong, Cdouble, Int32), out, data, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_ftn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_ftn, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

function lsl_push_chunk_dtn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_dtn, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_chunk_ltn(out, data, data_elements, timestamps)
        ccall((:lsl_push_chunk_ltn, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
    end
end

function lsl_push_chunk_itn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_itn, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

function lsl_push_chunk_stn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_stn, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

function lsl_push_chunk_ctn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_ctn, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

function lsl_push_chunk_strtn(out, data, data_elements, timestamps)
    ccall((:lsl_push_chunk_strtn, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Culong, Ptr{Cdouble}), out, data, data_elements, timestamps)
end

function lsl_push_chunk_ftnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_ftnp, liblsl), Int32, (lsl_outlet, Ptr{Cfloat}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

function lsl_push_chunk_dtnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_dtnp, liblsl), Int32, (lsl_outlet, Ptr{Cdouble}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_push_chunk_ltnp(out, data, data_elements, timestamps, pushthrough)
        ccall((:lsl_push_chunk_ltnp, liblsl), Int32, (lsl_outlet, Ptr{Clong}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
    end
end

function lsl_push_chunk_itnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_itnp, liblsl), Int32, (lsl_outlet, Ptr{Int32}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

function lsl_push_chunk_stnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_stnp, liblsl), Int32, (lsl_outlet, Ptr{Int16}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

function lsl_push_chunk_ctnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_ctnp, liblsl), Int32, (lsl_outlet, Ptr{Cchar}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

function lsl_push_chunk_strtnp(out, data, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_strtnp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Culong, Ptr{Cdouble}, Int32), out, data, data_elements, timestamps, pushthrough)
end

function lsl_push_chunk_buf(out, data, lengths, data_elements)
    ccall((:lsl_push_chunk_buf, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Culong), out, data, lengths, data_elements)
end

function lsl_push_chunk_buft(out, data, lengths, data_elements, timestamp)
    ccall((:lsl_push_chunk_buft, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Culong, Cdouble), out, data, lengths, data_elements, timestamp)
end

function lsl_push_chunk_buftp(out, data, lengths, data_elements, timestamp, pushthrough)
    ccall((:lsl_push_chunk_buftp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Culong, Cdouble, Int32), out, data, lengths, data_elements, timestamp, pushthrough)
end

function lsl_push_chunk_buftn(out, data, lengths, data_elements, timestamps)
    ccall((:lsl_push_chunk_buftn, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Culong, Ptr{Cdouble}), out, data, lengths, data_elements, timestamps)
end

function lsl_push_chunk_buftnp(out, data, lengths, data_elements, timestamps, pushthrough)
    ccall((:lsl_push_chunk_buftnp, liblsl), Int32, (lsl_outlet, Ptr{Cstring}, Ptr{UInt32}, Culong, Ptr{Cdouble}, Int32), out, data, lengths, data_elements, timestamps, pushthrough)
end

function lsl_have_consumers(out)
    ccall((:lsl_have_consumers, liblsl), Int32, (lsl_outlet,), out)
end

function lsl_wait_for_consumers(out, timeout)
    ccall((:lsl_wait_for_consumers, liblsl), Int32, (lsl_outlet, Cdouble), out, timeout)
end

function lsl_get_info(out)
    ccall((:lsl_get_info, liblsl), lsl_streaminfo, (lsl_outlet,), out)
end

function lsl_create_inlet(info, max_buflen, max_chunklen, recover)
    ccall((:lsl_create_inlet, liblsl), lsl_inlet, (lsl_streaminfo, Int32, Int32, Int32), info, max_buflen, max_chunklen, recover)
end

function lsl_destroy_inlet(in)
    ccall((:lsl_destroy_inlet, liblsl), Cvoid, (lsl_inlet,), in)
end

function lsl_get_fullinfo(in, timeout, ec)
    ccall((:lsl_get_fullinfo, liblsl), lsl_streaminfo, (lsl_inlet, Cdouble, Ptr{Int32}), in, timeout, ec)
end

function lsl_open_stream(in, timeout, ec)
    ccall((:lsl_open_stream, liblsl), Cvoid, (lsl_inlet, Cdouble, Ptr{Int32}), in, timeout, ec)
end

function lsl_close_stream(in)
    ccall((:lsl_close_stream, liblsl), Cvoid, (lsl_inlet,), in)
end

function lsl_time_correction(in, timeout, ec)
    ccall((:lsl_time_correction, liblsl), Cdouble, (lsl_inlet, Cdouble, Ptr{Int32}), in, timeout, ec)
end

function lsl_time_correction_ex(in, remote_time, uncertainty, timeout, ec)
    ccall((:lsl_time_correction_ex, liblsl), Cdouble, (lsl_inlet, Ptr{Cdouble}, Ptr{Cdouble}, Cdouble, Ptr{Int32}), in, remote_time, uncertainty, timeout, ec)
end

function lsl_set_postprocessing(in, flags)
    ccall((:lsl_set_postprocessing, liblsl), Int32, (lsl_inlet, UInt32), in, flags)
end

function lsl_pull_sample_f(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_f, liblsl), Cdouble, (lsl_inlet, Ptr{Cfloat}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

function lsl_pull_sample_d(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_d, liblsl), Cdouble, (lsl_inlet, Ptr{Cdouble}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_pull_sample_l(in, buffer, buffer_elements, timeout, ec)
        ccall((:lsl_pull_sample_l, liblsl), Cdouble, (lsl_inlet, Ptr{Clong}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
    end
end

function lsl_pull_sample_i(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_i, liblsl), Cdouble, (lsl_inlet, Ptr{Int32}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

function lsl_pull_sample_s(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_s, liblsl), Cdouble, (lsl_inlet, Ptr{Int16}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

function lsl_pull_sample_c(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_c, liblsl), Cdouble, (lsl_inlet, Ptr{Cchar}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

function lsl_pull_sample_str(in, buffer, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_str, liblsl), Cdouble, (lsl_inlet, Ptr{Cstring}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_elements, timeout, ec)
end

function lsl_pull_sample_buf(in, buffer, buffer_lengths, buffer_elements, timeout, ec)
    ccall((:lsl_pull_sample_buf, liblsl), Cdouble, (lsl_inlet, Ptr{Cstring}, Ptr{UInt32}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_lengths, buffer_elements, timeout, ec)
end

function lsl_pull_sample_v(in, buffer, buffer_bytes, timeout, ec)
    ccall((:lsl_pull_sample_v, liblsl), Cdouble, (lsl_inlet, Ptr{Cvoid}, Int32, Cdouble, Ptr{Int32}), in, buffer, buffer_bytes, timeout, ec)
end

function lsl_pull_chunk_f(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_f, liblsl), Culong, (lsl_inlet, Ptr{Cfloat}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_pull_chunk_d(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_d, liblsl), Culong, (lsl_inlet, Ptr{Cdouble}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

@static if !Sys.iswindows() && Sys.WORD_SIZE == 64
    function lsl_pull_chunk_l(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
        ccall((:lsl_pull_chunk_l, liblsl), Culong, (lsl_inlet, Ptr{Clong}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    end
end

function lsl_pull_chunk_i(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_i, liblsl), Culong, (lsl_inlet, Ptr{Int32}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_pull_chunk_s(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_s, liblsl), Culong, (lsl_inlet, Ptr{Int16}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_pull_chunk_c(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_c, liblsl), Culong, (lsl_inlet, Ptr{Cchar}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_pull_chunk_str(in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_str, liblsl), Culong, (lsl_inlet, Ptr{Cstring}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_pull_chunk_buf(in, data_buffer, lengths_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
    ccall((:lsl_pull_chunk_buf, liblsl), Culong, (lsl_inlet, Ptr{Cstring}, Ptr{UInt32}, Ptr{Cdouble}, Culong, Culong, Cdouble, Ptr{Int32}), in, data_buffer, lengths_buffer, timestamp_buffer, data_buffer_elements, timestamp_buffer_elements, timeout, ec)
end

function lsl_samples_available(in)
    ccall((:lsl_samples_available, liblsl), UInt32, (lsl_inlet,), in)
end

function lsl_was_clock_reset(in)
    ccall((:lsl_was_clock_reset, liblsl), UInt32, (lsl_inlet,), in)
end

function lsl_smoothing_halftime(in, value)
    ccall((:lsl_smoothing_halftime, liblsl), Int32, (lsl_inlet, Cfloat), in, value)
end

function lsl_first_child(e)
    ccall((:lsl_first_child, liblsl), lsl_xml_ptr, (lsl_xml_ptr,), e)
end

function lsl_last_child(e)
    ccall((:lsl_last_child, liblsl), lsl_xml_ptr, (lsl_xml_ptr,), e)
end

function lsl_next_sibling(e)
    ccall((:lsl_next_sibling, liblsl), lsl_xml_ptr, (lsl_xml_ptr,), e)
end

function lsl_previous_sibling(e)
    ccall((:lsl_previous_sibling, liblsl), lsl_xml_ptr, (lsl_xml_ptr,), e)
end

function lsl_parent(e)
    ccall((:lsl_parent, liblsl), lsl_xml_ptr, (lsl_xml_ptr,), e)
end

function lsl_child(e, name)
    ccall((:lsl_child, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_next_sibling_n(e, name)
    ccall((:lsl_next_sibling_n, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_previous_sibling_n(e, name)
    ccall((:lsl_previous_sibling_n, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_empty(e)
    ccall((:lsl_empty, liblsl), Int32, (lsl_xml_ptr,), e)
end

function lsl_is_text(e)
    ccall((:lsl_is_text, liblsl), Int32, (lsl_xml_ptr,), e)
end

function lsl_name(e)
    ccall((:lsl_name, liblsl), Cstring, (lsl_xml_ptr,), e)
end

function lsl_value(e)
    ccall((:lsl_value, liblsl), Cstring, (lsl_xml_ptr,), e)
end

function lsl_child_value(e)
    ccall((:lsl_child_value, liblsl), Cstring, (lsl_xml_ptr,), e)
end

function lsl_child_value_n(e, name)
    ccall((:lsl_child_value_n, liblsl), Cstring, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_append_child_value(e, name, value)
    ccall((:lsl_append_child_value, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring, Cstring), e, name, value)
end

function lsl_prepend_child_value(e, name, value)
    ccall((:lsl_prepend_child_value, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring, Cstring), e, name, value)
end

function lsl_set_child_value(e, name, value)
    ccall((:lsl_set_child_value, liblsl), Int32, (lsl_xml_ptr, Cstring, Cstring), e, name, value)
end

function lsl_set_name(e, rhs)
    ccall((:lsl_set_name, liblsl), Int32, (lsl_xml_ptr, Cstring), e, rhs)
end

function lsl_set_value(e, rhs)
    ccall((:lsl_set_value, liblsl), Int32, (lsl_xml_ptr, Cstring), e, rhs)
end

function lsl_append_child(e, name)
    ccall((:lsl_append_child, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_prepend_child(e, name)
    ccall((:lsl_prepend_child, liblsl), lsl_xml_ptr, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_append_copy(e, e2)
    ccall((:lsl_append_copy, liblsl), lsl_xml_ptr, (lsl_xml_ptr, lsl_xml_ptr), e, e2)
end

function lsl_prepend_copy(e, e2)
    ccall((:lsl_prepend_copy, liblsl), lsl_xml_ptr, (lsl_xml_ptr, lsl_xml_ptr), e, e2)
end

function lsl_remove_child_n(e, name)
    ccall((:lsl_remove_child_n, liblsl), Cvoid, (lsl_xml_ptr, Cstring), e, name)
end

function lsl_remove_child(e, e2)
    ccall((:lsl_remove_child, liblsl), Cvoid, (lsl_xml_ptr, lsl_xml_ptr), e, e2)
end

function lsl_create_continuous_resolver(forget_after)
    ccall((:lsl_create_continuous_resolver, liblsl), lsl_continuous_resolver, (Cdouble,), forget_after)
end

function lsl_create_continuous_resolver_byprop(prop, value, forget_after)
    ccall((:lsl_create_continuous_resolver_byprop, liblsl), lsl_continuous_resolver, (Cstring, Cstring, Cdouble), prop, value, forget_after)
end

function lsl_create_continuous_resolver_bypred(pred, forget_after)
    ccall((:lsl_create_continuous_resolver_bypred, liblsl), lsl_continuous_resolver, (Cstring, Cdouble), pred, forget_after)
end

function lsl_resolver_results(res, buffer, buffer_elements)
    ccall((:lsl_resolver_results, liblsl), Int32, (lsl_continuous_resolver, Ptr{lsl_streaminfo}, UInt32), res, buffer, buffer_elements)
end

function lsl_destroy_continuous_resolver(res)
    ccall((:lsl_destroy_continuous_resolver, liblsl), Cvoid, (lsl_continuous_resolver,), res)
end


text:
include("../Resistances/heat.jl")
include("../volumes.jl")
include("../../Utilities/summary.jl")

using Measurements, Modia
#ENV["MODIA_PLOT"] = "PyPlot"
Modia.usePlotPackage("PyPlot")
@usingModiaPlot

T_boundary = (20+273.15)u"K"
diameter = 0.1u"m"
th = 0.002u"m"
alpha = 15.0u"W/(m^2*K)"

surfaceArea = π*0.25*diameter^2

model = Model(
    boundary = Temperature | Map(T=T_boundary),
    resistor = Convection | Map(surfaceArea = surfaceArea, alpha=alpha),
    plasticDisk = UnitVolume | Map(V=surfaceArea*th, d = 1100.0u"kg/m^3", cp=1900.0u"J/(kg*K)"),
    connect =:[
        (boundary.port,resistor.port_b)
        (resistor.port_a,plasticDisk.port)
    ]
)

result = @instantiateModel(model, log=false)#,FloatType=Measurement{Float64})
@time simulate!(result, stopTime=500.0, tolerance=1e-4, log=false)
y_min, t_min, y_max, t_max = reportMinMax("plasticDisk.T","",result)
errorCheck(319.72586u"K"±0.0u"K", y_min)
plot(result, ["plasticDisk.T"], figure=1)