
// polar_shader_state.hpp - GLSL shader global state for polar stroking

#ifndef __polar_shader_state_hpp__
#define __polar_shader_state_hpp__

#include <unordered_map>
#if defined(_MSC_VER) && _MSC_VER < 1600
using std::tr1::unordered_map;  // Visual Studio 2008 hides unordered_map in the std::tr1 namespace.
#else
using std::unordered_map;
#endif

extern int uniform_segments;  // XXX deal with me

extern void resetTessellation();  // Reset underlying tessellation shaders to force lazy recompilation.
extern void initPolarTessellation();

#include "StrokerShaderConfig.hpp"

class StrokerContext : protected StrokeConfigTypes, protected StrokeSettingTypes {
    GLfloat current_color[4];  // In RGBA order.
    GLfloat gradient_coeff[3][3];
    CompositeMode composite_mode;
    ShadingMode shading_mode;
    float viewport_scale[4];
    StrokingSpace stroking_space;

    float arc_length_offset;
    float arc_length_texgen_scale;
    int draw_inside_of_round_joins;

public:
    StrokerContext();

    void updateMinWindowStrokeRadiusUniform();
    void updateTrivialCulling(bool trivial_culling);
    void updateTrivialCulling();
    void updateMaxTurningAngleDelta();
    void updateAllViewportScaleUniforms(int w, int h);

    void updateInsideRoundJoinMode(InsideRoundJoinEnum mode);
    void updateInsideRoundJoinMode();

    void resetTessellation();

    void prepViewportScale(int window_width, int window_height);

    void initPolarTessellation();

    const GLfloat* CurrentColor() const {
        return current_color;
    }
    GLfloat CurrentOpacity() const {
        return current_color[3];  // Return alpha.
    }
    void setCurrentColor(const GLfloat* color);
    void setCurrentOpacity(float opacity);

    // set*State methods change shading mode + related parameters
    void setGradientState(ShadingMode mode, int coords, const GLfloat coeffs[], float opacity);
    void setCurrentColorState(const GLfloat* color);

    inline void setLinearGradientState(const GLfloat coeffs[], float opacity) {
        setGradientState(StrokeConfigTypes::LINEAR_GRADIENT, 1, coeffs, opacity);
    }
    inline void setCenterRadialGradientState(const GLfloat coeffs[], float opacity) {
        setGradientState(StrokeConfigTypes::CENTER_RADIAL_GRADIENT, 2, coeffs, opacity);
    }
    inline void setFocalRadialGradientState(const GLfloat coeffs[], float opacity) {
        setGradientState(StrokeConfigTypes::FOCAL_RADIAL_GRADIENT, 3, coeffs, opacity);
    }

    const GLfloat* getGradientCoeffs(int row) const {
        return gradient_coeff[row];
    }

    void setArcLengthOffset(float offset);
    void setArcLengthTexGenScale(float scale);
    float getArcLengthOffset() const {
        return arc_length_offset;
    }
    float getArcLengthTexGenScale() const {
        return arc_length_texgen_scale;
    }

    void setDrawInsideOfRoundJoins(int inside) {
        assert(inside == 1 || inside == 0);
        draw_inside_of_round_joins = inside;
    }
    int getDrawInsideOfRoundJoins() const {
        return draw_inside_of_round_joins;
    }

    bool usesArcLengthExtension() const;

    ShadingMode getShadingMode() const {
        return shading_mode;
    }
    void setShadingMode(ShadingMode);
    const char* getShadingModeString() const;

    CompositeMode getCompositeMode() const {
        return composite_mode;
    }
    void setCompositeMode(CompositeMode mode);
    const char* getCompositeModeString() const;

    StrokingSpace getStrokingSpace() const {
        return stroking_space;
    }
    bool isWindowSpace() const {
        return stroking_space == WINDOW_SPACE;
    }
    void toggleWindowSpace() {
        if (isWindowSpace()) {
            stroking_space = OBJECT_SPACE;
        } else {
            stroking_space = WINDOW_SPACE;
        }
    }
    void setStrokingSpace(StrokingSpace new_value) {
        stroking_space = new_value;
    }
    void setNonScalingStroke(bool needs_non_scaling_stroke) {
        const StrokingSpace new_space = needs_non_scaling_stroke ? StrokeConfigTypes::WINDOW_SPACE
                                                                 : StrokeConfigTypes::OBJECT_SPACE;
        stroking_space = new_space;
    }
};

extern StrokerContext g_stroker_context;

#endif // __polar_shader_state_hpp__
