%module{Slic3r::XS};

#include <xsinit.h>
#include "slic3r/GUI/GLShader.hpp"
#include "slic3r/GUI/3DScene.hpp"

%name{Slic3r::GUI::_3DScene::GLShader} class GLShader {
    GLShader();
    ~GLShader();

    bool load(const char *fragment_shader, const char *vertex_shader);
    void release();

    int  get_attrib_location(const char *name) const;
    int  get_uniform_location(const char *name) const;

    bool set_uniform(const char *name, float value) const;

    void enable() const;
    void disable() const;

    std::string last_error() const
        %code%{ RETVAL = THIS->last_error; %};
};

%name{Slic3r::GUI::_3DScene::GLVolume} class GLVolume {
    GLVolume();
    ~GLVolume();

    std::vector<double> color()
        %code%{ RETVAL.reserve(4); RETVAL.push_back(THIS->color[0]); RETVAL.push_back(THIS->color[1]); RETVAL.push_back(THIS->color[2]); RETVAL.push_back(THIS->color[3]); %};

    int                 select_group_id()
        %code%{ RETVAL = THIS->select_group_id; %};
    int                 drag_group_id()
        %code%{ RETVAL = THIS->drag_group_id; %};
    int                 selected()
        %code%{ RETVAL = THIS->selected; %};
    void                set_selected(int i)
        %code%{ THIS->selected = i; %};
    int                 hover()
        %code%{ RETVAL = THIS->hover; %};
    void                set_hover(int i)
        %code%{ THIS->hover = i; %};
    int                 zoom_to_volumes()
        %code%{ RETVAL = THIS->zoom_to_volumes; %};

    int                 object_idx() const;
    int                 volume_idx() const;
    int                 instance_idx() const;
    Clone<Pointf3>      origin() const
        %code%{ RETVAL = THIS->origin; %};
    void                translate(double x, double y, double z)
        %code%{ THIS->origin.translate(x, y, z); %};
    Clone<BoundingBoxf3> bounding_box() const
        %code%{ RETVAL = THIS->bounding_box; %};
    Clone<BoundingBoxf3> transformed_bounding_box() const;

    bool                empty() const;
    bool                indexed() const;

    void                render() const;

    bool                has_layer_height_texture();
    int                 layer_height_texture_width();
    int                 layer_height_texture_height();
    int                 layer_height_texture_cells();
    void*               layer_height_texture_data_ptr_level0();
    void*               layer_height_texture_data_ptr_level1();
    double              layer_height_texture_z_to_row_id() const; 
    void                generate_layer_height_texture(PrintObject *print_object, bool force);
};


%name{Slic3r::GUI::_3DScene::GLVolume::Collection} class GLVolumeCollection {
    GLVolumeCollection();
    ~GLVolumeCollection();

    std::vector<int> load_object(ModelObject *object, int obj_idx, std::vector<int> instance_idxs, std::string color_by, std::string select_by, std::string drag_by, bool use_VBOs);

    int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs);

    void erase()
        %code{% THIS->clear(); %};

    int count()
        %code{% RETVAL = THIS->volumes.size(); %};
        
    std::vector<double> get_current_print_zs()
        %code{% RETVAL = THIS->get_current_print_zs(); %};

        
    void set_range(double low, double high);

    void render_VBOs() const;
    void render_legacy() const;
    void finalize_geometry(bool use_VBOs);
    void release_geometry();

    bool move_volume_up(int idx)
        %code%{ 
            if (idx > 0 && idx < int(THIS->volumes.size())) {
                std::swap(THIS->volumes[idx-1], THIS->volumes[idx]);
                std::swap(THIS->volumes[idx-1]->composite_id,    THIS->volumes[idx]->composite_id);
                std::swap(THIS->volumes[idx-1]->select_group_id, THIS->volumes[idx]->select_group_id);
                std::swap(THIS->volumes[idx-1]->drag_group_id,   THIS->volumes[idx]->drag_group_id);
                RETVAL = true;
            } else
                RETVAL = false;
        %};
    bool move_volume_down(int idx)
        %code%{ 
            if (idx >= 0 && idx + 1 < int(THIS->volumes.size())) {
                std::swap(THIS->volumes[idx+1], THIS->volumes[idx]);
                std::swap(THIS->volumes[idx+1]->composite_id,    THIS->volumes[idx]->composite_id);
                std::swap(THIS->volumes[idx+1]->select_group_id, THIS->volumes[idx]->select_group_id);
                std::swap(THIS->volumes[idx+1]->drag_group_id,   THIS->volumes[idx]->drag_group_id);
                RETVAL = true;
            } else
                RETVAL = false;
        %};

%{

SV*
GLVolumeCollection::arrayref()
    CODE:
        AV* av = newAV();
        av_fill(av, THIS->volumes.size()-1);
        int i = 0;
        for (GLVolume *v : THIS->volumes) {
            av_store(av, i++, perl_to_SV_ref(*v));
        }
        RETVAL = newRV_noinc((SV*)av);
    OUTPUT:
        RETVAL

%}
};

%package{Slic3r::GUI::_3DScene};
%{


void
_glew_init()
    CODE:
        _3DScene::_glew_init();

unsigned int
finalize_legend_texture()
    CODE:
        RETVAL = _3DScene::finalize_legend_texture();
    OUTPUT:
        RETVAL

unsigned int
get_legend_texture_id()
    CODE:
        RETVAL = _3DScene::get_legend_texture_id();
    OUTPUT:
        RETVAL

unsigned int
get_legend_texture_width()
    CODE:
        RETVAL = _3DScene::get_legend_texture_width();
    OUTPUT:
        RETVAL

unsigned int
get_legend_texture_height()
    CODE:
        RETVAL = _3DScene::get_legend_texture_height();
    OUTPUT:
        RETVAL
             
void
reset_legend_texture()
    CODE:
        _3DScene::reset_legend_texture();
             
void
_load_print_toolpaths(print, volumes, tool_colors, use_VBOs)
        Print               *print;
        GLVolumeCollection  *volumes;
        std::vector<std::string> tool_colors;
        int                  use_VBOs;
    CODE:
        _3DScene::_load_print_toolpaths(print, volumes, tool_colors, use_VBOs != 0);

void 
_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs)
        PrintObject        *print_object;
        GLVolumeCollection *volumes;
        std::vector<std::string> tool_colors;
        int                 use_VBOs;
    CODE:
        _3DScene::_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs != 0);

void
_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs)
        Print               *print;
        GLVolumeCollection  *volumes;
        std::vector<std::string> tool_colors;
        int                  use_VBOs;
    CODE:
        _3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0);

void 
load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs)
        Print                    *print;
        GCodePreviewData         *preview_data;
        GLVolumeCollection       *volumes;
        std::vector<std::string> str_tool_colors;
        int                      use_VBOs;
    CODE:
        _3DScene::load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs != 0);
        
%}
