%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_SKIRTBRIM          = psSkirtBrim
    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(); %};
};

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

    Ref<Print> print();
    Ref<ModelObject> model_object();
    Ref<StaticPrintConfig> config()
        %code%{ RETVAL = &THIS->config(); %};
    Clone<BoundingBox> bounding_box();
    
    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<Model> model()
        %code%{ RETVAL = const_cast<Model*>(&THIS->model()); %};
    Ref<StaticPrintConfig> config()
        %code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %};
    Ref<PlaceholderParser> placeholder_parser()
        %code%{ RETVAL = const_cast<PlaceholderParser*>(&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_mutable()); %};
    Ref<PrintObject> get_object(int idx)
        %code%{ RETVAL = THIS->objects_mutable()[idx]; %};
    size_t object_count()
        %code%{ RETVAL = THIS->objects().size(); %};

    PrintRegionPtrs* regions()
        %code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->print_regions_mutable()); %};
    
    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,double>::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);
            }
        %};
    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());
            }
        %};
        
    bool apply(Model *model, DynamicPrintConfig* config)
        %code%{ 
            // Touching every config as the Perl bindings does not correctly export ModelConfig,
            // therefore the configs have often invalid timestamps.
            for (auto obj : model->objects) {
                obj->config.touch();
                for (auto vol : obj->volumes)
                    vol->config.touch();
            }
            for (auto mat : model->materials)
                mat.second->config.touch();
            RETVAL = THIS->apply(*model, *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;
        %};

    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("%s\n", e.what());
            }
        %};

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

};