#include "snd.h"

/* create Postscript version of graph */

#define PRINT_BUFFER_SIZE 256
static char *pbuf = NULL;
static int bbx,bby,bx0,by0;
static int ps_fd;

static int start_ps_graph(char *output, char *title) 
{ 
  time_t ts;
  ps_fd = creat(output,0666);
  if (ps_fd == -1) return(-1);
  if (!pbuf) pbuf = (char *)calloc(PRINT_BUFFER_SIZE,sizeof(char));
  bbx = 0;
  bby = 0;
  sprintf(pbuf,"%%!PS-Adobe-2.0 EPSF-2.0\n%%%%Title: %s\n%%%%Creator: Snd: %s\n%%%%CreationDate: ",title,SND_VERSION);
  write(ps_fd,pbuf,strlen(pbuf));
  time(&ts);
  strftime(pbuf,PRINT_BUFFER_SIZE,"%a %d-%b-%y %H:%M %Z",localtime(&ts));
  write(ps_fd,pbuf,strlen(pbuf));
  sprintf(pbuf,"\n%%%%BoundingBox:(atend)\n%%%%EndComments\n%%%%EndProlog\n%%%%Page: 1 1\n\n");
  write(ps_fd,pbuf,strlen(pbuf));
  return(0);
}

static void ps_graph(chan_info *cp, int x0, int y0)
{
  cp->printing = 1;
  cp->ps_fd = ps_fd;
  bx0 = x0;
  by0 = y0;
  display_channel_data(cp,cp->sound,cp->state);
  cp->printing = 0;
  cp->ps_fd = 0;
}

static void end_ps_graph(void)
{
  sprintf(pbuf,"\nshowpage\n%%%%Trailer\n%%%%BoundingBox: %d %d %d %d\n",0,0,bbx+10,bby+10);
  write(ps_fd,pbuf,strlen(pbuf));
  close(ps_fd);
}

/* the x and y values in the "points" are relative to grf_x/y:
 *
 *  x: ap->x_axis_x0 + (val-ap->x0) * ap->x_scale
 *  y: ap->y_axis_y0 + (val*clm_sndflt - ap->y0) * ap->y_scale
 *
 * kept here in full precision since normally printers have much higher resolution than screens 
 */

static int reflect_y(chan_info *cp, int y)
{
  return(((axis_info *)(cp->axis))->height - y);
}

static float *xpts = NULL;
static float *ypts = NULL;
static float *ypts1 = NULL;

void ps_allocate_grf_points(void)
{
  if (!xpts) xpts = (float *)calloc(POINT_BUFFER_SIZE,sizeof(float));
  if (!ypts) ypts = (float *)calloc(POINT_BUFFER_SIZE,sizeof(float));
  if (!ypts1) ypts1 = (float *)calloc(POINT_BUFFER_SIZE,sizeof(float));
}

void ps_set_grf_points(double x, int j, float ymin, float ymax) 
{
  xpts[j] = x;
  ypts[j] = ymin;
  ypts1[j] = ymax;
}

void ps_set_grf_point(double x, int j, float y) 
{
  xpts[j]=x;
  ypts[j]=y;
}

static float ps_grf_x(axis_info *ap, float val)
{
  return(ap->x_axis_x0 + bx0 + (val - ap->x0) * ap->x_scale);
}

static float ps_grf_y(axis_info *ap, float val)
{
  return(by0 + ap->height - (ap->y_axis_y0 + (val - ap->y0) * ap->y_scale));
}

static void ps_draw_lines(chan_info *cp, axis_info *ap, int j, float *xpts, float *ypts)
{
  int i;
  sprintf(pbuf," %.2f %.2f moveto\n",ps_grf_x(ap,xpts[0]),ps_grf_y(ap,ypts[0]));
  write(cp->ps_fd,pbuf,strlen(pbuf));
  for (i=1;i<j;i++)
    {
      sprintf(pbuf," %.2f %.2f lineto\n",ps_grf_x(ap,xpts[i]),ps_grf_y(ap,ypts[i]));
      write(cp->ps_fd,pbuf,strlen(pbuf));
    }
  sprintf(pbuf," stroke\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_draw_grf_points(snd_state *ss, chan_info *cp, axis_info *ap, int j) 
{
  int i;
  if (ss->graph_style == graph_lines) 
    {
      ps_draw_lines(cp,ap,j,xpts,ypts);
    }
  else
    {
      for (i=0;i<j;i++)
	{
	  sprintf(pbuf," %.2f %.2f .25 0 360 newpath arc fill\n",ps_grf_x(ap,xpts[i]),ps_grf_y(ap,ypts[i]));
	  write(cp->ps_fd,pbuf,strlen(pbuf));
	}
    }
}

void ps_draw_both_grf_points(snd_state *ss,chan_info *cp, axis_info *ap, int j) 
{
  int i;
  if (ss->graph_style == graph_lines) 
    {
      ps_draw_lines(cp,ap,j,xpts,ypts);
      ps_draw_lines(cp,ap,j,xpts,ypts1);
    }
  else
    {
      for (i=0;i<j;i++)
	{
	  sprintf(pbuf," %.2f %.2f .25 0 360 newpath arc fill\n",ps_grf_x(ap,xpts[i]),ps_grf_y(ap,ypts[i]));
	  write(cp->ps_fd,pbuf,strlen(pbuf));
	}
      for (i=0;i<j;i++)
	{
	  sprintf(pbuf," %.2f %.2f .25 0 360 newpath arc fill\n",ps_grf_x(ap,xpts[i]),ps_grf_y(ap,ypts1[i]));
	  write(cp->ps_fd,pbuf,strlen(pbuf));
	}
    }
  
}

static int last_color = -1;

void ps_draw_sono_rectangle(chan_info *cp, axis_info *ap, int color, float x, float y, float width, float height)
{
  snd_state *ss;
  int r,g,b;
  ss = cp->state;
  if (last_color != color)
    {
      get_current_color(ss->spectrogram_color,color,&r,&g,&b);
      last_color = color;
      sprintf(pbuf," %.2f %.2f %.2f setrgbcolor\n",(float)r/65535.0,(float)g/65535.0,(float)b/65535.0);
      write(cp->ps_fd,pbuf,strlen(pbuf));
    }
  sprintf(pbuf," %.2f %.2f %.2f %.2f rectfill\n",ps_grf_x(ap,x),ps_grf_y(ap,y),width,height);
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_reset_color(chan_info *cp)
{
  sprintf(pbuf," 0 setgray\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
  last_color = -1;
}

/* the rest are in real coordinates except upsidedown from PS point of view */
void ps_draw_line (chan_info *cp, int x0,int y0,int x1,int y1) 
{
  int py0,py1,px0,px1;
  px0 = x0+bx0;
  px1 = x1+bx0;
  py0 = reflect_y(cp,y0) + by0;
  py1 = reflect_y(cp,y1) + by0;
  if (px0>bbx) bbx=px0;
  if (px1>bbx) bbx=px1;
  if (py0>bby) bby=py0;
  if (py1>bby) bby=py1;
  sprintf(pbuf," 0 setlinewidth %d %d moveto %d %d lineto stroke\n",px0,py0,px1,py1);
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_draw_spectro_line(chan_info *cp, int color, float x0, float y0, float x1, float y1)
{
  /* these are in local coords */
  snd_state *ss;
  int r,g,b;
  ss = cp->state;
  if (last_color != color)
    {
      get_current_color(ss->spectrogram_color,color,&r,&g,&b);
      last_color = color;
      sprintf(pbuf," %.2f %.2f %.2f setrgbcolor\n",(float)r/65535.0,(float)g/65535.0,(float)b/65535.0);
      write(cp->ps_fd,pbuf,strlen(pbuf));
    }
  ps_draw_line(cp,x0,y0,x1,y1);
}

void ps_fill_rectangle (chan_info *cp, int x0, int y0, int width, int height) 
{
  int py0,py1,px0,px1;
  px0 = x0+bx0;
  px1 = x0+bx0+width;
  py0 = reflect_y(cp,y0) + by0;
  py1 = reflect_y(cp,y0+height) + by0;
  if (px0>bbx) bbx=px0;
  if (px1>bbx) bbx=px1;
  if (py0>bby) bby=py0;
  if (py1>bby) bby=py1;
  sprintf(pbuf," %d %d %d %d rectfill\n",px0,py0,width,-height);
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_draw_string (chan_info *cp, int x0, int y0, char *str) 
{
  int px0,py0;
  px0 = x0+bx0;
  py0 = reflect_y(cp,y0)+by0;
  if (px0>bbx) bbx=px0;
  if (py0>bby) bby=py0;
  sprintf(pbuf," %d %d moveto (%s) show\n",px0,py0,str);
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_set_number_font(chan_info *cp) 
{
  sprintf(pbuf," /Courier findfont 15 scalefont setfont\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_set_label_font(chan_info *cp) 
{
  sprintf(pbuf," /Times-Roman findfont 20 scalefont setfont\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_set_bold_peak_numbers_font(chan_info *cp) 
{
  sprintf(pbuf," /Times-Bold findfont 14 scalefont setfont\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
}

void ps_set_peak_numbers_font(chan_info *cp) 
{
  sprintf(pbuf," /Times-Roman findfont 14 scalefont setfont\n");
  write(cp->ps_fd,pbuf,strlen(pbuf));
}


#define PRINTED_VERTICAL_SPACING 25 

void snd_print(snd_state *ss, char *output, int syncing)
{
  int j,i,err;
  int *offsets;
  sync_info *si;
  if (syncing) si = snd_sync(ss); else si=NULL;
  if (!si) si = make_simple_sync(current_channel(ss),0);
  offsets = (int *)calloc(si->chans,sizeof(int));
  for (j=0,i=(si->chans-1);i>=0;i--)
    {
      offsets[i] = j;
      j += ((((axis_info *)((si->cps[i])->axis))->height) + PRINTED_VERTICAL_SPACING);
    }
  err = start_ps_graph(output,snd_NAME(si->cps[0]));
  if (err == 0)
    {
      for (i=0;i<si->chans;i++)
	{
	  ps_graph(si->cps[i],0,offsets[i]);
	}
      end_ps_graph();
    }
  else snd_printf((void *)ss,strerror(errno));
  free_sync_info(si);
}

void region_print(char *output, char* title, chan_info *cp)
{
  int err;
  err = start_ps_graph(output,title);
  if (err == 0)
    {
      ps_graph(cp,0,0.0);
      end_ps_graph();
    }
  else snd_printf((void *)(cp->state),strerror(errno));
}

void enved_print(char *output, chan_info *cp, int y0)
{
  int err;
  err = start_ps_graph(output,"Envelope Editor");
  if (err == 0)
    {
      cp->printing = 1;
      cp->ps_fd = ps_fd;
      bx0 = 0;
      by0 = y0;
      env_redisplay(cp->state);
      cp->printing = 0;
      cp->ps_fd = 0;
      end_ps_graph();
    }
  else snd_printf((void *)(cp->state),strerror(errno));
}
