%module{Slic3r::XS};

%{
#include <xsinit.h>
#include "libslic3r/Print.hpp"
#include "libslic3r/PlaceholderParser.hpp"
%}

%package{Slic3r::Print::State};
%{

IV
_constant()
  ALIAS:
    STEP_SLICE              = posSlice
    STEP_PERIMETERS         = posPerimeters
    STEP_PREPARE_INFILL     = posPrepareInfill
    STEP_INFILL             = posInfill
    STEP_SUPPORTMATERIAL    = posSupportMaterial
    STEP_SKIRT              = psSkirt
    STEP_BRIM               = psBrim
    STEP_WIPE_TOWER         = psWipeTower
  PROTOTYPE:
  CODE:
    RETVAL = ix;
  OUTPUT: RETVAL

%}

%name{Slic3r::Print::Region} class PrintRegion {
    // owned by Print, no constructor/destructor

    Ref<StaticPrintConfig> config()
        %code%{ RETVAL = &THIS->config(); %};
    Ref<Print> print();
    
    Clone<Flow> flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object)
        %code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %};
};

%name{Slic3r::Print::Object} class PrintObject {
    // owned by Print, no constructor/destructor

    int region_count()
        %code%{ RETVAL = THIS->print()->regions().size(); %};

    Ref<Print> print();
    Ref<ModelObject> model_object();
    Ref<StaticPrintConfig> config()
        %code%{ RETVAL = &THIS->config(); %};
    Points copies();
    Clone<BoundingBox> bounding_box();
    
    Points _shifted_copies()
        %code%{ RETVAL = THIS->copies(); %};

    size_t layer_count();
    Ref<Layer> get_layer(int idx);

    size_t support_layer_count();
    Ref<SupportLayer> get_support_layer(int idx);

    bool step_done(PrintObjectStep step)
        %code%{ RETVAL = THIS->is_step_done(step); %};

    void slice();
};

%name{Slic3r::Print} class Print {
    Print();
    ~Print();

    Ref<StaticPrintConfig> config()
        %code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %};
    Ref<PlaceholderParser> placeholder_parser()
        %code%{ RETVAL = &THIS->placeholder_parser(); %};
    Ref<ExtrusionEntityCollection> skirt()
        %code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->skirt()); %};
    Ref<ExtrusionEntityCollection> brim()
        %code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->brim()); %};
    std::string estimated_normal_print_time()
        %code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %};
    std::string estimated_silent_print_time()
        %code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %};
    double total_used_filament()
        %code%{ RETVAL = THIS->print_statistics().total_used_filament; %};
    double total_extruded_volume()
        %code%{ RETVAL = THIS->print_statistics().total_extruded_volume; %};
    double total_weight()
        %code%{ RETVAL = THIS->print_statistics().total_weight; %};
    double total_cost()
        %code%{ RETVAL = THIS->print_statistics().total_cost; %};
    double total_wipe_tower_cost()
        %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_cost; %};
    double total_wipe_tower_filament()
        %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_filament; %};
    int wipe_tower_number_of_toolchanges()
        %code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %};
    PrintObjectPtrs* objects()
        %code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
    Ref<PrintObject> get_object(int idx)
        %code%{ RETVAL = THIS->objects()[idx]; %};
    void reload_object(int idx);
    size_t object_count()
        %code%{ RETVAL = THIS->objects().size(); %};

    PrintRegionPtrs* regions()
        %code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->regions()); %};
    Ref<PrintRegion> get_region(int idx)
        %code%{ RETVAL = THIS->regions()[idx]; %};
    size_t region_count()
        %code%{ RETVAL = THIS->regions().size(); %};
    
    bool step_done(PrintStep step)
        %code%{ RETVAL = THIS->is_step_done(step); %};
    bool object_step_done(PrintObjectStep step)
        %code%{ RETVAL = THIS->is_step_done(step); %};
    
    SV* filament_stats()
        %code%{
            HV* hv = newHV();
            for (std::map<size_t,float>::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) {
                // stringify extruder_id
                std::ostringstream ss;
                ss << it->first;
                std::string str = ss.str();
                
                (void)hv_store( hv, str.c_str(), str.length(), newSViv(it->second), 0 );
                RETVAL = newRV_noinc((SV*)hv);
            }
        %};
    double max_allowed_layer_height() const;
    bool has_support_material() const;
    void auto_assign_extruders(ModelObject* model_object);
    std::string output_filepath(std::string path = "")
        %code%{
            try {
                RETVAL = THIS->output_filepath(path);
            } catch (std::exception& e) {
                croak("%s\n", e.what());
            }
        %};
        
    void add_model_object(ModelObject* model_object, int idx = -1);
    bool apply_config(DynamicPrintConfig* config)
        %code%{ RETVAL = THIS->apply_config(*config); %};
    bool has_infinite_skirt();
    std::vector<unsigned int> extruders() const;
    int validate() %code%{ 
            std::string err = THIS->validate(); 
            if (! err.empty())
                croak("Configuration is not valid: %s\n", err.c_str()); 
            RETVAL = 1;
        %};
    Clone<BoundingBox> bounding_box();
    Clone<BoundingBox> total_bounding_box();
    Clone<Point>       size() %code%{ RETVAL = THIS->bounding_box().size(); %};

    void set_callback_event(int evt) %code%{
        %};
    void set_status_silent();
    void set_status(int percent, const char *message);

    void process() %code%{
            try {
                THIS->process();
            } catch (std::exception& e) {
                croak(e.what());
            }
        %};

    void export_gcode(char *path_template) %code%{
            try {
                THIS->export_gcode(path_template, nullptr);
            } catch (std::exception& e) {
                croak(e.what());
            }
        %};

};