/* $Id: bezctx_ff.c 4532 2015-12-22 13:18:53Z mskala $ */
/* This file written by George Williams to provide a gateway to fontanvil */
/* it it a modification of Raph's bezctx_ps.c */
#include "fontanvil.h"
#include <stdio.h>

#ifndef _NO_LIBSPIRO
#   include "bezctx_ff.h"
#   include "fontanvilvw.h"	/* For LogError, else splinefont.h */
#   ifdef HAVE_IEEEFP_H
#      include <ieeefp.h>	/* Solaris defines isnan in ieeefp rather than math.h */
#   endif
#   include <math.h>

typedef struct {
   bezctx base;			/* This is a superclass of bezctx, and this is the entry for the base */
   int is_open;
   int gotnans;			/* Sometimes spiro fails to converge and we get NaNs. */
   /* Complain the first time this happens, but not thereafter */
   SplineSet *ss;		/* The fontanvil contour which we build up as we go along */
} bezctx_ff;

static void nancheck(bezctx_ff *bc) {

   if (!bc->gotnans) {		/* Called when we get passed a NaN. Complain the first time that happens */
      ErrorMsg(2,"Spiros did not converge\n");
      bc->gotnans=true;
   }
}

/* This routine starts a new contour */
/* So we allocate a new SplineSet, and then add the first point to it */
static void bezctx_ff_moveto(bezctx *z,double x,double y,int is_open) {
   bezctx_ff *bc=(bezctx_ff *) z;

   if (!isfinite(x) || !isfinite(y)) {	/* Protection against NaNs */
      nancheck(bc);
      x=y=0;
   }
   if (!bc->is_open) {
      SplineSet *ss;

      if ((ss=(SplineSet *) calloc(1, sizeof(SplineSet)))==NULL)
	 return;
      ss->next=bc->ss;
      bc->ss=ss;
   }
   bc->ss->first=bc->ss->last=SplinePointCreate(x, y);
   bc->is_open=is_open;
}

/* This routine creates a linear spline from the previous point specified to this one */
static void bezctx_ff_lineto(bezctx *z,double x,double y) {
   bezctx_ff *bc=(bezctx_ff *) z;
   SplinePoint *sp;

   if (!isfinite(x) || !isfinite(y)) {
      nancheck(bc);
      x=y=0;
   }
   sp=SplinePointCreate(x, y);
   if (sp != NULL && SplineMake3(bc->ss->last, sp) != NULL)
      bc->ss->last=sp;
}

/* This could create a quadratic spline, except FontAnvil only is prepared */
/* to deal with cubics, so convert the quadratic into the equivalent cubic */
static void
bezctx_ff_quadto(bezctx * z, double xm, double ym, double x3, double y3) {
   bezctx_ff *bc=(bezctx_ff *) z;
   double x0, y0;
   double x1, y1;
   double x2, y2;
   SplinePoint *sp;

   if (!isfinite(xm) || !isfinite(ym) || !isfinite(x3) || !isfinite(y3)) {
      nancheck(bc);
      xm=ym=x3=y3=0;
   }
   if ((sp=SplinePointCreate(x3, y3)) != NULL) {
      x0=bc->ss->last->me.x;
      y0=bc->ss->last->me.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);
      bc->ss->last->nextcp.x=x1;
      bc->ss->last->nextcp.y=y1;
      bc->ss->last->nonextcp=false;
      sp->prevcp.x=x2;
      sp->prevcp.y=y2;
      sp->noprevcp=false;
      if (SplineMake3(bc->ss->last, sp) != NULL)
	 bc->ss->last=sp;
   }
}

/* And this creates a cubic */
static void
bezctx_ff_curveto(bezctx * z, double x1, double y1, double x2, double y2,
		  double x3, double y3) {
   bezctx_ff *bc=(bezctx_ff *) z;
   SplinePoint *sp;

   if (!isfinite(x1) || !isfinite(y1) ||
       !isfinite(x2) || !isfinite(y2) ||
       !isfinite(x3) || !isfinite(y3)) {
      nancheck(bc);
      x1=y1=x2=y2=x3=y3=0;
   }
   if ((sp=SplinePointCreate(x3, y3)) != NULL) {
      bc->ss->last->nextcp.x=x1;
      bc->ss->last->nextcp.y=y1;
      bc->ss->last->nonextcp=false;
      sp->prevcp.x=x2;
      sp->prevcp.y=y2;
      sp->noprevcp=false;
      if (SplineMake3(bc->ss->last, sp) != NULL)
	 bc->ss->last=sp;
   }
}

/* Allocates and initializes a new FontAnvil bezier context */
bezctx *new_bezctx_ff(void) {
   bezctx_ff *result;

   if ((result=(bezctx_ff *) calloc(1, sizeof(bezctx_ff)))==NULL)
      return NULL;

   result->base.moveto=bezctx_ff_moveto;
   result->base.lineto=bezctx_ff_lineto;
   result->base.quadto=bezctx_ff_quadto;
   result->base.curveto=bezctx_ff_curveto;
   result->base.mark_knot=NULL;
   result->is_open=0;
   result->gotnans=0;
   result->ss=NULL;
   return &result->base;
}

/* Finishes an old FontAnvil bezier context, and returns the contour which was created */
struct splinepointlist *bezctx_ff_close(bezctx * z) {
   bezctx_ff *bc=(bezctx_ff *) z;
   SplineSet *ss=bc->ss;

   if (!bc->is_open && ss != NULL) {
      if (ss->first != ss->last &&
	  RealNear(ss->first->me.x, ss->last->me.x) &&
	  RealNear(ss->first->me.y, ss->last->me.y)) {
	 ss->first->prevcp=ss->last->prevcp;
	 ss->first->noprevcp=ss->last->noprevcp;
	 ss->first->prev=ss->last->prev;
	 ss->first->prev->to=ss->first;
	 SplinePointFree(ss->last);
	 ss->last=ss->first;
      } else {
	 if (SplineMake3(ss->last, ss->first) != NULL)
	    ss->last=ss->first;
      }
   }
   free(bc);
   return (ss);
}
#endif
