
// PathSegmentProcessor.hpp - path processing

#ifndef __PathSegmentProcessor_hpp__
#define __PathSegmentProcessor_hpp__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <Cg/double.hpp>
#include <Cg/vector.hpp>

#include <Cg/cos.hpp>
#include <Cg/sin.hpp>

#include "sg/PathPtr.hpp"

using namespace Cg;

#include "EndPointArc.hpp"
#include "CenterPointArc.hpp"

class PathSegmentProcessor {
private:
    // Keep track of the last path command type in order to handle
    // smooth path commands that reference the prior (quadratic or
    // cubic Bezier) command's coordinates.  LCT_NONE is used for
    // the PostScript circular arc commands because they behave
    // differently (don't draw a line segment) when there is no
    // valid current position.
    enum {
        LCT_CUBIC,      // any cubic path command
        LCT_QUADRATIC,  // any quadratic path command
        LCT_NONE,       // no path commands yet (initial or reset state)
        LCT_SPLINE_NEEDING_END_TANGENT,  // spline command not followed by a spline
                                         // so needs an end tangent
        LCT_OTHER       // none of the above
    } last_cmd_type;

    enum SplinePointUsage {
        USE_AS_CONTROL_POINT,
        USE_AS_TANGENT
    };

    void closeHelper(bool next_cmd_is_spline);
    inline void implicitCloseHelper(bool next_cmd_is_spline);
    inline void restartPathState();

    static bool commandProvidesSplineControlPoint(const PathPtr path, size_t ndx);
    static bool commandSetsPrimedPoint(const PathPtr path, size_t ndx);
    static bool isPrimeSplineToCommand(const PathPtr path, size_t ndx);
    void centripetalCatmullRomSpline(const float2 catmullrom_point[4], SplinePointUsage start_usage, SplinePointUsage end_usage, bool more);
    void handlePendingSpline(const float2 &start_tangent);

    bool implicit_close;

protected:
    float2 initial_contour_point;  // where last moveTo contour started
    float2 current;                // current position along path
    float2 prior_curve_point;      // prior quadratic/cubic control point for smooth commands

    // Spline state
    float2 pending_spline[4];      // Spline missing an "end tangent" for LCT_SPLINE_NEEDING_END_TANGENT
    float2 primed_point;           // primed control point for spline
    SplinePointUsage start_usage;
    float2 spline_start_tangent;   // end tangent to be used to as "start tangent" for spline-to commands
    // Closed spline state
    bool within_closed_spline;
    float2 second_closed_spline_point;
    float2 end_closed_spline_point;
    bool needs_last_end_tangent;
    float2 last_end_tangent;
    SplinePointUsage last_end_usage;

public:
    virtual void beginPath(PathPtr& p);  // defaults to no-op
    virtual void moveTo(const float2 p[2]) = 0;
    virtual void lineTo(const float2 p[2]) = 0;
    virtual void quadraticCurveTo(const float2 p[3]) = 0;
    virtual void cubicCurveTo(const float2 p[4]) = 0;
    virtual void splineCubicCurveTo(const float2 p[4], bool more);  // defaults to calling cubicCurveTo
    virtual void conicCurveTo(const float2 p[4], float w) = 0;
    virtual void arcTo(const CenterPointArc &arc) = 0;
    // Sometimes arcs are defined by SVG to be degenerate, in which case
    // the degenerate handler is used instead of arcTo.
    virtual void degenerate();  // defaults to no-op
    virtual void setSharedEdge() {};
    virtual void primeSplineTo(const float2 &p) {};

    // For PostScript arcs only...
    // arcn, arc, and arct commands may generate an initial line segment to the arc.
    // The intial line is from p[0] to p[1] and p[1] will be the same as arc.p[0].
    virtual void lineToArcTo(const float2 p[2], const CenterPointArc &arc);

    virtual void rect(float x, float y, float w, float h);  // for PDF rect commands
    virtual void rrect(float x, float y, float w, float h, const float radius[8]);

    virtual void close(const float2 p[2]) = 0;
    virtual void restart();  // defaults to no-op

    virtual void endPath(PathPtr& p);  // defaults to no-op
    virtual ~PathSegmentProcessor() {}

    void processSegments(PathPtr path, bool implicit_close_policy = false);
};

#endif // __PathSegmentProcessor_hpp__
