#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 <libnr/n-art-bpath.h>
#include "nodepath.h"

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

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;
    sp_curve_moveto(bi->curve, x, y);
}

void bezctx_ink_lineto(bezctx *bc, double x, double y)
{
    bezctx_ink *bi = (bezctx_ink *) bc;
    sp_curve_lineto(bi->curve, x, y);
}

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

    double x0, y0;
    double x1, y1;
    double x2, y2;

    NR::Point last = sp_curve_last_point(bi->curve);
    x0 = last[NR::X];
    y0 = last[NR::Y];
    x1 = xm + (1./3) * (x0 - xm);
    y1 = ym + (1./3) * (y0 - ym);
    x2 = xm + (1./3) * (x3 - xm);
    y2 = ym + (1./3) * (y3 - ym);

    sp_curve_curveto(bi->curve, x1, y1, x2, y2, x3, y3);
}

void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
		    double x3, double y3)
{
    bezctx_ink *bi = (bezctx_ink *) bc;
    sp_curve_curveto(bi->curve, x1, y1, x2, y2, x3, y3);
}

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::setup_nodepath(Inkscape::NodePath::Path *np)
{
    sp_nodepath_show_handles(np, false);
    sp_nodepath_show_helperpath(np, false);
}

void
LPESpiro::doEffect(SPCurve * curve)
{
    SPCurve *csrc = sp_curve_copy(curve);
    sp_curve_reset(curve);
    bezctx *bc = new_bezctx_ink(curve);
    int len = SP_CURVE_LENGTH(csrc);
    spiro_cp *path = g_new (spiro_cp, len + 1);
    NArtBpath *bpath = csrc->_bpath;
    int ib = 0;
    int ip = 0;
    bool closed = false;
    NR::Point pt(0, 0);
    NArtBpath *first_in_subpath = NULL;
    while(ib <= len) {
        path [ip].x = bpath[ib].x3;
        path [ip].y = bpath[ib].y3;
        // std::cout << "==" << bpath[ib].code << "   ip" << ip << "  ib" << ib << "\n";
        if (bpath[ib].code == NR_END || bpath[ib].code == NR_MOVETO_OPEN || bpath[ib].code == NR_MOVETO) {
            if (ip != 0) { // run prev subpath
                int sp_len = 0;
                if (!closed) {
                     path[ip - 1].ty = '}';
                    sp_len = ip;
                } else {
                    sp_len = ip - 1;
                }
                spiro_seg *s = NULL;
                //for (int j = 0; j <= sp_len; j ++) printf ("%c\n", path[j].ty);
                s = run_spiro(path, sp_len);
                spiro_to_bpath(s, sp_len, bc);
                free(s);
                path[0].x = path[ip].x;
                path[0].y = path[ip].y;
                ip = 0;
            }
            if (bpath[ib].code == NR_MOVETO_OPEN) {
                closed = false;
                path[ip].ty = '{';
            } else {
                closed = true;
                if (ib  < len)
                    first_in_subpath = &(bpath[ib + 1]);
                path[ip].ty = 'c';
            }
        } else {
                // this point is not last, so makes sense to find a proper type for it
                NArtBpath *next = NULL;
                if (ib < len && (bpath[ib+1].code == NR_END || bpath[ib+1].code == NR_MOVETO_OPEN || bpath[ib+1].code == NR_MOVETO)) { // end of subpath
                    if (closed) 
                        next = first_in_subpath;
                } else {
                    if (ib < len)
                        next = &(bpath[ib+1]);
                    else if (closed)
                        next = first_in_subpath;
                }
                if (next) {
                    bool this_is_line = bpath[ib].code == NR_LINETO || 
                        (NR::L2(NR::Point(bpath[ib].x3, bpath[ib].y3) - NR::Point(bpath[ib].x2, bpath[ib].y2)) < 0.001);
                    bool next_is_line = next->code == NR_LINETO || 
                        (NR::L2(NR::Point(bpath[ib].x3, bpath[ib].y3) - NR::Point(next->x1, next->y1)) < 0.001);
                    double this_angle = NR_HUGE;
                    if (this_is_line) {
                        this_angle = atan2 (bpath[ib].x3 - pt[NR::X], bpath[ib].y3 - pt[NR::Y]);
                    } else if (bpath[ib].code == NR_CURVETO) {
                        this_angle = atan2 (bpath[ib].x3 - bpath[ib].x2, bpath[ib].y3 - bpath[ib].y2);
                    } 
                    double next_angle = NR_HUGE;
                    if (next_is_line) {
                        next_angle = atan2 (next->x3 - bpath[ib].x3, next->y3 - bpath[ib].y3);
                    } else if (next->code == NR_CURVETO) {
                        next_angle = atan2 (next->x1 - bpath[ib].x3, next->y1 - bpath[ib].y3);
                    } 
                    if (this_angle != NR_HUGE && next_angle != NR_HUGE && fabs(this_angle - next_angle) < 0.001) {
                        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';

                    }
                    if (closed && next == first_in_subpath) {
                        path[0].ty = path[ip].ty;
                    }
                }
        }
        pt  = NR::Point(bpath[ib].x3, bpath[ib].y3);
        ip++;
        ib++;
    }
    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 :
