#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_picture(char *filename,double llx,double lly,
		   double width,double height,int depth, int flags);


void write_line(char *p){
  int i,off,pt,inc,open,filled,flipped,debug;
  int obj,sub,line_style,thickness,pen_colour,fill_colour,depth;
  int pen_style,fill,join,cap,radius,arrow_f,arrow_b,npoints;
  double style,width;
  double *x,*y,v01[2],v12[2],xtmp;
  const double magic=1-0.5522847498; /* This number is arguably wrong */
  struct arrow forward,backward;
  char filename[180],*f,*buffer;
  unsigned buff_len=1000;

  debug=flags&DEBUG_MASK;


  buffer=malloc(buff_len);

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

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

  if (sub>5){
    fprintf(stderr,"Unsupported line subtype skipped\n");
    return;
  }

  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);
  }

  if (sub==5){ /* A picture */
    p=fgets(buffer,180,fig);
    i=sscanf(p,"%d %s %n",&flipped,filename,&off);
  }

  open=1; /* An open curve */
  if ((sub==2)||(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 line 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]);
  }

  /* Scale everything */

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

  radius*=72.0/80;
  style*=72.0/80;

  /* 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;
  }

/* Update bounding box as necessary */

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

  if (sub==5){ /* A picture */
    if (npoints!=5) {
      fprintf(stderr,"Unexpected number of points for picture. Quitting.\n");
      exit(1);
    }

    /* Points are in the order ur, ul, lr, ll, _unless_ the
     * image is rotated, in which case they might not even be
     * clockwise. */

    if (debug>2)
      for(pt=0;pt<4;pt++)
	fprintf(stderr,"%d %.2f %.2f\n",pt,x[pt],y[pt]);

    /* Are these points clockwise? Check by turning x[1]-x[0]
    * clockwise and dotting with x[2]-x[1] */

    v01[0]=y[1]-y[0];
    v01[1]=-(x[1]-x[0]);
    v12[0]=x[2]-x[1];
    v12[1]=y[2]-y[1];
    xtmp=v01[0]*v12[0]+v01[1]*v12[1];

    if (flipped==0)
      if (xtmp>0)
	write_picture(filename,x[3],y[3],x[1]-x[3],y[1]-y[3],depth,0);
      else
	write_picture(filename,x[1],y[1],x[3]-x[1],y[3]-y[1],depth,0);
    else
      if (xtmp>0)
	write_picture(filename,x[1],y[1],x[3]-x[1],y[3]-y[1],depth,1);
      else
	write_picture(filename,x[3],y[3],x[1]-x[3],y[1]-y[3],depth,1);

    return;
  }

  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 (join) i+=sprintf(buffer+i,"%d j\n",join); /* 0 miter,
                                                   1 round,
                                                   2 bevel,
                                                same as PDF */
  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);

  switch(sub){
  case 1:  /* polyline */
  case 2:  /* box */
  case 3:  /* polygon */
    i+=sprintf(buffer+i,"%.2f %.2f m\n",x[0],y[0]);
    if (open){
    for(pt=1;pt<npoints;pt++)
      i+=sprintf(buffer+i,"%.2f %.2f l\n",x[pt],y[pt]);
      if (filled) 
	i+=sprintf(buffer+i,"B\n");
      else
	i+=sprintf(buffer+i,"S\n");
      if (i>(buff_len-500)){
        buff_len+=2000;
        buffer=realloc(buffer,buff_len);
      }
    }
    else{
    for(pt=1;pt<npoints-1;pt++)
      i+=sprintf(buffer+i,"%.2f %.2f l\n",x[pt],y[pt]);
      if (filled)
	i+=sprintf(buffer+i,"b\n");
      else
	i+=sprintf(buffer+i,"s\n");
      if (i>(buff_len-500)){
        buff_len+=2000;
        buffer=realloc(buffer,buff_len);
      }
    }

    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);
    i+=sprintf(buffer+i,"Q");
    break;
  case 4: /* arc-box */
    /* Find unit vectors along two edges NB v23=-v01 and v30=-v12 */
    v01[0]=x[1]-x[0];
    v01[1]=y[1]-y[0];
    xtmp=sqrt(v01[0]*v01[0]+v01[1]*v01[1]);
    v01[0]/=xtmp;
    v01[1]/=xtmp;

    v12[0]=x[2]-x[1];
    v12[1]=y[2]-y[1];
    xtmp=sqrt(v12[0]*v12[0]+v12[1]*v12[1]);
    v12[0]/=xtmp;
    v12[1]/=xtmp;

    i+=sprintf(buffer+i,"%.2f %.2f m\n",x[1]-radius*v01[0],y[1]-radius*v01[1]);

    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
	       x[1]-radius*v01[0]*magic,y[1]-radius*v01[1]*magic,
	       x[1]+radius*v12[0]*magic,y[1]+radius*v12[1]*magic,
               x[1]+radius*v12[0],y[1]+radius*v12[1]);

    i+=sprintf(buffer+i,"%.2f %.2f l\n",x[2]-radius*v12[0],y[2]-radius*v12[1]);

    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
	       x[2]-radius*v12[0]*magic,y[2]-radius*v12[1]*magic,
	       x[2]-radius*v01[0]*magic,y[2]-radius*v01[1]*magic,
               x[2]-radius*v01[0],y[2]-radius*v01[1]);

    i+=sprintf(buffer+i,"%.2f %.2f l\n",x[3]+radius*v01[0],y[3]+radius*v01[1]);

    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
	       x[3]+radius*v01[0]*magic,y[3]+radius*v01[1]*magic,
	       x[3]-radius*v12[0]*magic,y[3]-radius*v12[1]*magic,
               x[3]-radius*v12[0],y[3]-radius*v12[1]);

    i+=sprintf(buffer+i,"%.2f %.2f l\n",x[0]+radius*v12[0],y[0]+radius*v12[1]);

    i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f c\n",
	       x[0]+radius*v12[0]*magic,y[0]+radius*v12[1]*magic,
	       x[0]+radius*v01[0]*magic,y[0]+radius*v01[1]*magic,
               x[0]+radius*v01[0],y[0]+radius*v01[1]);

    if (filled)
      i+=sprintf(buffer+i,"b");
    else
      i+=sprintf(buffer+i,"h");

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

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

  add_obj(cref,depth);

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

}

void write_arrow(struct arrow *a,double x,double y,double dx,double dy,
		 int colour,char *buffer, int *i_in){
  double xtmp,theta,width,sc=1.0;
  int i;

  i=*i_in;
  xtmp=sqrt(dx*dx+dy*dy);
  dx/=xtmp;
  dy/=xtmp;

  theta=atan2(dy,dx);

  if (a->thickness>1)
    width=(a->thickness-1)*72.0/80;
  else
    width=a->thickness*72.0/160;

  i+=sprintf(buffer+i,"q %.2f w\n",width);
  if (a->style==0)
    i+=sprintf(buffer+i,"1 1 1 rg\n");
  else
  if ((colour>=0)&&(colour<=31))
    i+=sprintf(buffer+i,"%s rg\n",fig_colours[colour]);

  i+=sprintf(buffer+i,"%.2f %.2f %.2f %.2f %.2f %.2f cm\n",cos(theta),sin(theta),
	     -sin(theta),cos(theta),x,y);

  if (a->type==2) sc=1.25;
  if (a->type==3) sc=0.75;

  i+=sprintf(buffer+i,"%.2f %.2f m 0 0 l ",-sc*a->height,0.5*a->width);
  i+=sprintf(buffer+i,"%.2f %.2f l\n",-sc*a->height,-0.5*a->width);

  bb_fix(x-sc*a->height*cos(theta)+0.5*a->width*sin(-theta),
	 y+0.5*a->width*cos(theta)+sc*a->height*sin(-theta),width);
  bb_fix(x-sc*a->height*cos(theta)-0.5*a->width*sin(-theta),
	 y-0.5*a->width*cos(theta)+sc*a->height*sin(-theta),width);

  switch(a->type){
  case 0:
    i+=sprintf(buffer+i,"S Q\n");
    break;
  case 1:
    i+=sprintf(buffer+i,"b Q\n");
    break;
  case 2:
  case 3:
    i+=sprintf(buffer+i,"%.2f 0 l b Q\n",-a->height);
    break;
  default: /* What?! */
    i+=sprintf(buffer+i,"Q\n");
  }

  *i_in=i;

}

void write_dash(int dash, double len, char *buffer, int *i){
  /* The following magic numbers have little to do with the Xfig documentation,
   * and much to do with what fig2dev actually does.
   */
  switch(dash){
  case -1:
  case 0:
    return;
    break;
  case 1:
    *i+=sprintf(buffer+*i,"[%.2f] 0 d\n",len);
    break;
  case 2:
    *i+=sprintf(buffer+*i,"[%.2f %.2f] %.2f d\n",0.9,len,len);
    break;
  case 3:
    *i+=sprintf(buffer+*i,"[%.2f %.2f %.2f %.2f] 0 d\n",
		len,0.5*len,0.9,0.5*len);
    break;
  case 4:
    *i+=sprintf(buffer+*i,"[%.2f %.2f %.2f %.2f %.2f %.2f] 0 d\n",
	       len,0.45*len,0.9,0.333*len,0.9,0.45*len);
    break;
  case 5:
    *i+=sprintf(buffer+*i,"[%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f] 0 d\n",
	       len,0.4*len,0.9,0.3*len,0.9,0.3*len,0.9,0.4*len);
    break;
  default:
    fprintf(stderr,"Invalid dash type of %d ignored\n",dash);
    break;
  }
}
