
// BakeIndexedStrokedPath.hpp - interface for baking a stroked path for polar stroking

#ifndef __BakeIndexedStrokedPath_hpp__
#define __BakeIndexedStrokedPath_hpp__

#include <GL/glew.h>
#include <vector>
#include "sg/path.hpp"
#include "sg/PathSegmentProcessor.hpp"
#include "sg/CenterPointArc.hpp"  // for CenterPointArc

#include "cpu/CPUStrokeTessellator.hpp"

#include "bake/DashingState.hpp"

#include "countof.h"

using std::vector;

#include "IndexedStrokedPath.hpp"

struct DashedSegmentState {
    const vector<float> &dash_array;
    DashStyle::DashOffsetReset dash_offset_reset;

    struct CurveProgress {
        float t;
        double arc_length;
        double cummulative_arc_length;
    };

    vector<CurveProgress> progress;

    CPUStrokeTessellator stroker;  // used for dashing curved segments

    // Dash processing state
    DashingState initial_dashing_state;  // copied to dashing_state on dash phase resets
    DashingState dashing_state;

    float2 previous_p;

    static const size_t BOGUS_INDEX = size_t(~0);
    size_t needs_terminal_dash_cap;
    bool needs_initial_dash_cap;

    DashedSegmentState(const DashStyle& style);

    double tForDistanceIntoSegment(double distance, size_t& initial_index) const;
    double tForDistanceIntoConicSegment(double distance, float w, size_t& initial_index) const;
};

struct BakeIndexedStrokedPath : PathSegmentProcessor, IndexedStrokedPath, DashedSegmentState {
public:
    enum CapPlacement {
        INITIAL_CAP,
        TERMINAL_CAP,
        INITIAL_DASH_CAP,
        TERMINAL_DASH_CAP
    };

private:
    // Whether to add links for caps and joins.
    bool include_caps;
    bool include_joins;

    bool verbose;
    bool native_quadratics;
    std::vector<float2>* catmull_rom_extra_points_ptr;

    size_t moveTo_index;
    size_t needs_initial_cap;
    bool has_nontrivial_conic;
    bool needs_cubic_stabilization;
    bool join_needed;

    void addCap(CapPlacement placement, GLuint anchor, GLuint heading);
    void addJoin(const StrokeLink &s);
    void possiblyAddInitialDashCap(const StrokeLink &s);
    void addStrokeLink(const StrokeLink& s);
    void addInitialControlPoint(const float2& p);

    void commonQuadraticConicIndexProcessing(StrokeLink &s, size_t before_size);
    void addPseudoCusp();
    void addTinyWConicCurveTo(const float2 p[3]);
    void addHugeWConicCurveTo(const float2 p[3]);

    void addLineToLink(const float2 p[2]);
    void addQuadraticCurveTo(const float2 p[3]);
    void addCubicCurveTo(const float2 p[4]);
    void addConicCurveTo(const float2 p[3], float w);

    void stabilizeCubics();

    void nextRibSegment(const float2& p, float t);
    static void nextRibSegmentFunc(int rib_index, const float2& p, float t, void* handle);

    // Helper to dashed*To routines.
    double segmentArcLength() const;

    void dashedLineTo(const double2 p[2]);
    void dashedQuadraticCurveTo(const double2 cp[3]);
    void forceDashingProgress(size_t initial_index, double& end_dash_t, double& arc_length_progress, double& segment_distance_left) const;
    void dashedConicCurveTo(const double2 p[3], float w);
    void dashedCubicCurveTo(const double2 p[4]);

    // Helper for arcTo
    static void conicFunc(const float2 plist[3], float w, void *handle);

    void lineToHelper(const float2 p[2]);
    void quadraticCurveToHelper(const float2 p[3]);
    void conicCurveToHelper(const float2 p[3], float w);
    void cubicCurveToHelper(const float2 p[4]);

    // Helper for close
    void addClosingDashCaps();

public:
    // Implement PathSegmentProcessor virtual functions.
    void beginPath(PathPtr &p);
    void moveTo(const float2 p[2]);
    void lineTo(const float2 p[2]);
    void quadraticCurveTo(const float2 p[3]);
    void conicCurveTo(const float2 p[3], float w);
    void cubicCurveTo(const float2 p[4]);
    void splineCubicCurveTo(const float2 p[4], bool more);
    void arcTo(const CenterPointArc &arc);
    void endPath(PathPtr &p);
    void close(const float2 p[2]);

    bool hasNontrivialConic() { return has_nontrivial_conic; }

    void setVerbose(bool v);
    void setNativeQuadratics(bool v);
    void setCatmullRomExtraPointsVector(std::vector<float2>*);

    BakeIndexedStrokedPath(const DashStyle& style);
    BakeIndexedStrokedPath(const DashStyle& style, bool include_caps, bool include_joins);
};

#endif // __BakeIndexedStrokedPath_hpp__
