/* XFig has three spline formats.
 *
 * Approximated. Can be approximated by Bezier curves whose
 * end points are the mid point between the spline's control points,
 * and whose control points lie two thirds of the way from the Bezier's
 * end points to the XFig control point. Open curves start with a straight
 * line segment from the first point to the midpoint of the first two points,
 * and end similarly.
 *
 * Interpolated. Can be approximated by Bezier curves whose
 * end points are the spline's control points, and whose control points
 * are formed as follows. The Bezier curve from x(i) to x(i+1) has control
 * points x(i)+0.25*(x(i+1)-x(i-1)) and x(i+1)-0.25*(x(i+2)-x(i)).
 * Open curves start with the first control point lying on the end point,
 * and end similarly.
 *
 * X-spline. Too complicated to describe / code here!
 *
 * All two point splines (including X-splines) are straight lines.
 *
 * Well, it turns out that the above theories, found using Google, are
 * very gross approximations. For the moment they can stay.
 */

#include<stdio.h>
#include<stdlib.h>  /* exit() */
#include<stdarg.h>
#include<string.h>
#include<math.h>

#include "fig2pdf.h"

void write_arrow(struct arrow *a,double x,double y,double dx,double dy,
                 int colour,char *buffer, int *i);

void write_spline(char *p){
  int i,off,pt,inc,open,filled;
  int obj,sub,line_style,thickness,pen_colour,fill_colour,depth;
  int pen_style,fill,cap,arrow_f,arrow_b,npoints,debug;
  double style,width;
  double *x,*y,*s;
  struct arrow forward,backward;
  char *f,*buffer;
  unsigned buff_len=1000;

  buffer=malloc(buff_len);

  debug=flags&DEBUG_MASK;

  i=sscanf(p,"%d %d %d %d %d %d %d %d %d %lf %d %d %d %d %n",
	   &obj,&sub,&line_style,&thickness,&pen_colour,&fill_colour,
	   &depth,&pen_style,&fill,&style,&cap,
	   &arrow_f,&arrow_b,&npoints,&off);

  if (i!=14){
    fprintf(stderr,"Error parsing spline object\n");
    exit(1);
  }

  if (arrow_f){
    p=fgets(buffer,180,fig);
    i=sscanf(p,"%d %d %lf %lf %lf %n",&forward.type,&forward.style,
	     &forward.thickness,&forward.width,&forward.height,&off);
  }

  if (arrow_b){
    p=fgets(buffer,180,fig);
    i=sscanf(p,"%d %d %lf %lf %lf %n",&backward.type,&backward.style,
	     &backward.thickness,&backward.width,&backward.height,&off);
  }

  open=1; /* An open curve */
  if ((sub==1)||(sub==3)) open=0; /* A closed curve */

  filled=0;
  if ((fill>=0)&&(fill<=40)) filled=1;
  else if (fill!=-1) fprintf(stderr,"Unsupported fill type ignored\n");

  /* Read points as doubles, though are stored as ints */

  x=malloc(npoints*sizeof(double));
  y=malloc(npoints*sizeof(double));

  if (debug>1) fprintf(stderr,"Reading spline of %d points\n",npoints);

  for(pt=0;pt<npoints;pt++){
    i=sscanf(p+off,"%lf %lf %n",x+pt,y+pt,&inc);
    off+=inc;
    if (i!=2){
      p=fgets(buffer,180,fig);
      off=0;
      if (p==NULL||((p[0]!=' ')&&(p[0]!='\t'))){
	fprintf(stderr,"Error parsing line points\n");
	exit(1);
      }
      pt--;
    }
    //    fprintf(stderr,"Read %d points %f %f\n",pt,x[pt],y[pt]);
  }

  /* Read shape factors */

  s=malloc(npoints*sizeof(double));

  for(pt=0;pt<npoints;pt++){
    i=sscanf(p+off,"%lf %n",s+pt,&inc);
    off+=inc;
    if (i!=1){
      p=fgets(buffer,180,fig);
      off=0;
      if (p==NULL||((p[0]!=' ')&&(p[0]!='\t'))){
	fprintf(stderr,"Error parsing spline shape factors\n");
	exit(1);
      }
      pt--;
    }
  }

  if ((sub>3)&&(npoints>2)){
    fprintf(stderr,"Unsupported spline subtype skipped\n");
    return;
  }

  /* Scale everything */

  for(pt=0;pt<npoints;pt++){
    x[pt]*=72.0/1200;
    y[pt]=pg[1]-72*y[pt]/1200;
  }

  /* The following seems to be mad but true */ 

  if (thickness>1)
    width=(thickness-1)*72.0/80;
  else
    width=thickness*72.0/160;
      
  if (arrow_f){
    forward.width*=72.0/1200;
    forward.height*=72.0/1200;
    forward.thickness*=72.0/80;
  }

  if (arrow_b){
    backward.width*=72.0/1200;
    backward.height*=72.0/1200;
    backward.thickness*=72.0/80;
  }

  style*=72.0/80;

  if ((filled==0)&&(width==0)) return; /* Nothing to do */

/* Update bounding box as necessary */

  for(pt=0;pt<npoints;pt++) bb_fix(x[pt],y[pt],width);

  i=sprintf(buffer,"q");

  if ((pen_colour<-1)||(pen_colour>MAX_COL)) pen_colour=-1;

  if (pen_colour==-1)
    i+=sprintf(buffer+i," 0 0 0 RG");
  else if (fig_colours[pen_colour])
    i+=sprintf(buffer+i," %s RG",fig_colours[pen_colour]);
  else
    fprintf(stderr,"Invalid pen colour %d\n",pen_colour);

  f=fill_col(fill_colour,fill);
  if (f) i+=sprintf(buffer+i," %s rg",f);

  i+=sprintf(buffer+i," %.2f w\n",width);

  if (cap) i+=sprintf(buffer+i,"%d J\n",cap);   /* 0 butt,
                                                   1 round,
                                                   2 projecting,
						   same as PDF */

  if (line_style>0) write_dash(line_style,style,buffer,&i);

  if (npoints==2){ /* We have a simple straight line, which can't be filled */
    if (width==0) return;
    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f l\n",x[0],y[0],x[1],y[1]);
    i+=sprintf(buffer+i,"h S Q\n");

    if (arrow_f) write_arrow(&forward,x[1],y[1],
			     x[1]-x[0],y[1]-y[0],
			     pen_colour,buffer,&i);
    
    if (arrow_b) write_arrow(&backward,x[0],y[0],
			     x[0]-x[1],
			     y[0]-y[1],
			     pen_colour,buffer,&i);

    OBJ_PRN("<</Length %d>> stream\n%s\nendstream\nendobj\n\n",i,buffer);
    add_obj(cref,depth);
    return;
  }

  if ((sub==0)||(sub==1)){ /* Approximated splines */
    if (open)
      i+=sprintf(buffer+i,"%.2f %.2f m %.2f %.2f l\n",x[0],y[0],
		 0.5*(x[0]+x[1]),0.5*(y[0]+y[1]));
    else
      i+=sprintf(buffer+i,"%.2f %.2f m\n",0.5*(x[0]+x[1]),
		 0.5*(y[0]+y[1]));

    for(pt=1;pt<npoints-1;pt++){
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
		 x[pt-1]/6+5*x[pt]/6,y[pt-1]/6+5*y[pt]/6,
		 x[pt+1]/6+5*x[pt]/6,y[pt+1]/6+5*y[pt]/6,
		 0.5*(x[pt]+x[pt+1]),0.5*(y[pt]+y[pt+1]));
      if (i>(buff_len-500)){
	buff_len+=2000;
	buffer=realloc(buffer,buff_len);
      }
    }

    if (open)
      i+=sprintf(buffer+i,"%.2f %.2f l\n",x[npoints-1],y[npoints-1]);
    else{
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
		 x[npoints-2]/6+5*x[npoints-1]/6,
		 y[npoints-2]/6+5*y[npoints-1]/6,
		 x[0]/6+5*x[npoints-1]/6,y[0]/6+5*y[npoints-1]/6,
		 0.5*(x[npoints-1]+x[0]),0.5*(y[npoints-1]+y[0]));
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c h\n",
		 x[npoints-1]/6+5*x[0]/6,
		 y[npoints-1]/6+5*y[0]/6,
		 x[1]/6+5*x[0]/6,y[1]/6+5*y[0]/6,
		 0.5*(x[0]+x[1]),0.5*(y[0]+y[1]));
    }
  }
  else if ((sub==2)||(sub==3)){ /* Interpolated splines */
    i+=sprintf(buffer+i,"%.2f %.2f m\n",x[0],y[0]);
    if (open)
      i+=sprintf(buffer+i,"%.2f %.2f ",x[0],y[0]);
    else
      i+=sprintf(buffer+i,"%.2f %.2f ",x[0]+0.25*(x[1]-x[npoints-1]),
		 y[0]+0.25*(y[1]-y[npoints-1]));
    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f c\n",x[1]-0.25*(x[2]-x[0]),
	       y[1]-0.25*(y[2]-y[0]),x[1],y[1]);
    for(pt=2;pt<npoints-1;pt++){
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
		 x[pt-1]+0.25*(x[pt]-x[pt-2]),y[pt-1]+0.25*(y[pt]-y[pt-2]),
		 x[pt]-0.25*(x[pt+1]-x[pt-1]),y[pt]-0.25*(y[pt+1]-y[pt-1]),
                 x[pt],y[pt]);
      if (i>(buff_len-500)){
	buff_len+=2000;
	buffer=realloc(buffer,buff_len);
      }
    }
    if (open){
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
		 x[pt-1]+0.25*(x[pt]-x[pt-2]),y[pt-1]+0.25*(y[pt]-y[pt-2]),
		 x[pt],y[pt],x[pt],y[pt]);
    }
    else{
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
		 x[pt-1]+0.25*(x[pt]-x[pt-2]),y[pt-1]+0.25*(y[pt]-y[pt-2]),
		 x[pt]-0.25*(x[0]-x[pt-1]),y[pt]-0.25*(y[0]-y[pt-1]),
                 x[pt],y[pt]);
      pt++;
      i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c h\n",
		 x[pt-1]+0.25*(x[0]-x[pt-2]),y[pt-1]+0.25*(y[0]-y[pt-2]),
		 x[0]-0.25*(x[1]-x[pt-1]),y[0]-0.25*(y[1]-y[pt-1]),
                 x[0],y[0]);
    }

  }

  if (filled)
    if (width==0)
      i+=sprintf(buffer+i,"f\n");
    else
      i+=sprintf(buffer+i,"B\n");
  else
    i+=sprintf(buffer+i,"S\n");

  if (sub==0){
    if (arrow_f) write_arrow(&forward,x[npoints-1],y[npoints-1],
			     x[npoints-1]-x[npoints-2],
			     y[npoints-1]-y[npoints-2],
			     pen_colour,buffer,&i);
    
    if (arrow_b) write_arrow(&backward,x[0],y[0],
			     x[0]-x[1],
			     y[0]-y[1],
			     pen_colour,buffer,&i);
  }
  else if (sub==2){
    if (arrow_f) write_arrow(&forward,x[npoints-1],y[npoints-1],
			     x[npoints-1]-
			     (x[npoints-2]+0.25*(x[npoints-1]-x[npoints-3])),
			     y[npoints-1]-
			     (y[npoints-2]+0.25*(y[npoints-1]-y[npoints-3])),
			     pen_colour,buffer,&i);
    
    if (arrow_b) write_arrow(&backward,x[0],y[0],
			     x[0]-(x[1]-0.25*(x[2]-x[0])),
			     y[0]-(y[1]-0.25*(y[2]-y[0])),
			     pen_colour,buffer,&i);
  }

  i+=sprintf(buffer+i,"Q");

  OBJ_PRN("<</Length %d>> stream\n%s\nendstream\nendobj\n\n",i,buffer);

  add_obj(cref,depth);

  free(buffer);
  free(x);
  free(y);
  free(s);

}
