#define INKSCAPE_LPE_SPIRO_C

/*
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include "live_effects/lpe-spiro.h"

#include "display/curve.h"
#include <typeinfo>
#include <2geom/pathvector.h>
#include <2geom/matrix.h>
#include <2geom/bezier-curve.h>
#include <2geom/hvlinesegment.h>
#include <2geom/isnan.h>
#include "helper/geom-nodetype.h"
#include "helper/geom-curves.h"

#include "live_effects/bezctx.h"
#include "live_effects/bezctx_intf.h"
#include "live_effects/spiro.h"

// For handling un-continuous paths:
#include "message-stack.h"
#include "inkscape.h"
#include "desktop.h"

#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS

typedef struct {
    bezctx base;
    SPCurve *curve;
    int is_open;
} bezctx_ink;

void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
{
    bezctx_ink *bi = (bezctx_ink *) bc;
    if ( IS_FINITE(x) && IS_FINITE(y) ) {
        bi->curve->moveto(x, y);
    }
#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
    else {
        g_message("lpe moveto not finite");
    }
#endif
}

void bezctx_ink_lineto(bezctx *bc, double x, double y)
{
    bezctx_ink *bi = (bezctx_ink *) bc;
    if ( IS_FINITE(x) && IS_FINITE(y) ) {
        bi->curve->lineto(x, y);
    }
#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
    else {
        g_message("lpe lineto not finite");
    }
#endif
}

void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
{
    bezctx_ink *bi = (bezctx_ink *) bc;

    if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
        bi->curve->quadto(xm, ym, x3, y3);
    }
#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
    else {
        g_message("lpe quadto not finite");
    }
#endif
}

void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
		    double x3, double y3)
{
    bezctx_ink *bi = (bezctx_ink *) bc;
    if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
        bi->curve->curveto(x1, y1, x2, y2, x3, y3);
    }
#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
    else {
        g_message("lpe curveto not finite");
    }
#endif
}

bezctx *
new_bezctx_ink(SPCurve *curve) {
    bezctx_ink *result = g_new(bezctx_ink, 1);
    result->base.moveto = bezctx_ink_moveto;
    result->base.lineto = bezctx_ink_lineto;
    result->base.quadto = bezctx_ink_quadto;
    result->base.curveto = bezctx_ink_curveto;
    result->base.mark_knot = NULL;
    result->curve = curve;
    return &result->base;
}




namespace Inkscape {
namespace LivePathEffect {

LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
    Effect(lpeobject)
{
}

LPESpiro::~LPESpiro()
{
}

void
LPESpiro::doEffect(SPCurve * curve)
{
    using Geom::X;
    using Geom::Y;

    // Make copy of old path as it is changed during processing
    Geom::PathVector const original_pathv = curve->get_pathvector();
    guint len = curve->get_segment_count() + 2;

    curve->reset();
    bezctx *bc = new_bezctx_ink(curve);
    spiro_cp *path = g_new (spiro_cp, len);
    int ip = 0;

    for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
        if (path_it->empty())
            continue;

        // start of path
        {
            Geom::Point p = path_it->front().pointAt(0);
            path[ip].x = p[X];
            path[ip].y = p[Y];
            path[ip].ty = '{' ;  // for closed paths, this is overwritten
            ip++;
        }

        // midpoints
        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());         // outgoing curve

        Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
        if (path_it->closed()) {
            // if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
            const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type Geom::LineSegment.
            if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
                // closingline.isDegenerate() did not work, because it only checks for *exact* zero length, which goes wrong for relative coordinates and rounding errors...
                // the closing line segment has zero-length. So stop before that one!
                curve_endit = path_it->end_open();
            }
        }

        while ( curve_it2 != curve_endit )
        {
            /* This deals with the node between curve_it1 and curve_it2.
             * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
             * and then curve_it1 points to end or closing segment */
            Geom::Point p = curve_it1->finalPoint();
            path[ip].x = p[X];
            path[ip].y = p[Y];

            // Determine type of spiro node this is, determined by the tangents (angles) of the curves
            // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
            bool this_is_line = is_straight_curve(*curve_it1);
            bool next_is_line = is_straight_curve(*curve_it2);

            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);

            if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
            {
                if (this_is_line && !next_is_line) {
                    path[ip].ty = ']';
                } else if (next_is_line && !this_is_line) {
                    path[ip].ty = '[';
                } else {
                    path[ip].ty = 'c';
                }
            } else {
                path[ip].ty = 'v';
            }

            ++curve_it1;
            ++curve_it2;
            ip++;
        }

        // add last point to the spiropath
        Geom::Point p = curve_it1->finalPoint();
        path[ip].x = p[X];
        path[ip].y = p[Y];
        if (path_it->closed()) {
            // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
            switch (nodetype) {
                case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
                    path[ip].ty = '}';
                    ip++;
                    break;
                case Geom::NODE_CUSP:
                    path[0].ty = path[ip].ty = 'v';
                    break;
                case Geom::NODE_SMOOTH:
                case Geom::NODE_SYMM:
                    path[0].ty = path[ip].ty = 'c';
                    break;
            }
        } else {
            // set type to path closer
            path[ip].ty = '}';
            ip++;
        }

        // run subpath through spiro
        int sp_len = ip;
        spiro_seg *s = run_spiro(path, sp_len);
        spiro_to_bpath(s, sp_len, bc);
        free(s);
        ip = 0;
    }

    g_free (path);
}

}; //namespace LivePathEffect
}; /* namespace Inkscape */

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
