// ----------------------------------------------------------------------------
//
// Copyright (C) Columbia University, 1994. All Rights Reserved.
// Sameer A. Nene, Shree K. Nayar, Hiroshi Murase
//
// See file LICENSE for details of software license agreement.
//
// ----------------------------------------------------------------------------
//
// postscript.C
//
// Author:               Sameer Nene
// Date:                 07/29/94
// Version:              1.0
//
// Bugs:
//
// ToDo:
//
// Classes:
//   PEXtoPSstream
//
// Notes:
//   This module contains implementation of classes declared in postscript.h
//   
// ----------------------------------------------------------------------------

#include "errorscope.h"
#include "postscript.h"
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <X11/PEX5/PEXlib.h>

void PEXtoPSstream::mapToPage(const PEXCoord *pi, PEXCoord *po, int n)
{
  for(int i = 0; i < n; ++i) {
    po[i].x = pi[i].x * d_sx + d_tx;
    po[i].y = pi[i].y * d_sy + d_ty;
  }
}

PEXtoPSstream::PEXtoPSstream(FILE *file, double width, double height,
			     const PEXCoord &min, const PEXCoord &max) :
			     d_file_p(file), d_markerType(PEXMarkerAsterisk),
			     d_aTextHt(1.), d_lineWidth(1.), d_markerScale(1.),
			     d_pgWidth(width), d_pgHeight(height)
{
  double rx, ry, rz, r, t;
  PEXColorRGB col;
  
  col.red = col.blue = col.green = 1.;
  d_textCol.rgb = d_lineCol.rgb = d_markerCol.rgb = d_surfCol.rgb = col;
  
  rx = max.x - min.x;
  ry = max.y - min.y;
  rz = max.z - min.z;
  r = (t = rx > ry ? rx : ry) > rz ? t : rz;
  
  if(width < height)
    d_sx = d_sy = (width * 72.) / (r * 2.);
  else
    d_sx = d_sy = (height * 72.) / (r * 2.);
  
  d_tx = width * 72. / 2.;
  d_ty = height * 72. / 2.;
}

ErrorScope::Error PEXtoPSstream::writeHeader()
{
  if(fprintf(d_file_p, "%%!PS-Adobe-2.0\n%%%%Creator: SLAM V1.0 Revision 08/02/94\n%%%%BoundingBox: 0 0 %u %u\n%%%%Pages: 1 1\n%%%%DocumentFonts: Times-Roman\n%%%%EndComments\n\n/circle\n{0 360 arc} def\n\n%%%%Page: 1 1\n", int(d_pgWidth * 72.), int (d_pgHeight * 72.)) < 0)
    return FILE_WRITE_ERROR;

  return OK;
}

void PEXtoPSstream::setView(const PEXViewEntry &view)
{
  memcpy(d_ori, view.orientation, sizeof(view.orientation));
}

ErrorScope::Error PEXtoPSstream::render(Display *display,
					const PEXStructure &sid, int elemNum)
{
  char *raw_data, *p1;
  unsigned long num_elements, raw_length;
  int float_format = PEXGetProtocolFloatFormat(display), l, i, j, k;
  PEXOCData *ocdata;

  PEXFetchElements(display, sid, PEXBeginning, elemNum, PEXBeginning, elemNum,
		   float_format, &num_elements, &raw_length, &raw_data);
  ocdata = PEXDecodeOCs(float_format, num_elements, raw_length,	raw_data);
  
  switch(ocdata -> oc_type) {
  case PEXOCATextHeight:
    d_aTextHt = ocdata -> data.SetATextHeight.height;
    break;
  case PEXOCTextColor:
    d_textCol = ocdata -> data.SetTextColor.color;
    break;
  case PEXOCAnnotationText:
    {
      PEXCoord po;
      PEXTransformPoints(d_ori, 1, &(ocdata -> data.EncodedAnnoText.origin),
			 &po);
      po.x += ocdata -> data.EncodedAnnoText.offset.x;
      po.y += ocdata -> data.EncodedAnnoText.offset.y;
      mapToPage(&po, &po, 1);
      l = ocdata -> data.EncodedAnnoText.encoded_text[0].length;
      strncpy(p1 = new char[l + 1],
	      ocdata -> data.EncodedAnnoText.encoded_text[0].ch, l);
      p1[l] = '\0';
      if(fprintf(d_file_p, "newpath\n/Times-Roman findfont\n%f scalefont\nsetfont\n%f %f moveto\n(%s) show\n\n", d_aTextHt * 1000., po.x, po.y, p1) < 0) {
	delete[] p1;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      delete[] p1;
      break;
    }
  case PEXOCLineColor:
    d_lineCol = ocdata -> data.SetLineColor.color;
    break;
  case PEXOCLineWidth:
    d_lineWidth = ocdata -> data.SetLineWidth.width;
    break;
  case PEXOCPolylineSetWithData:
    {
      for(i = 0; i < ocdata -> data.PolylineSetWithData.count; ++i) {
	PEXCoord *po = new PEXCoord[j = int(ocdata -> data.PolylineSetWithData.vertex_lists[i].count)];
	PEXTransformPoints(d_ori, j, ocdata -> data.PolylineSetWithData.vertex_lists[i].vertices.no_data, po);
	mapToPage(po, po, j);
	if(fprintf(d_file_p, "newpath\n%f %f moveto\n", po[0].x, po[0].y) < 0) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
	for(k = 1; k < j; ++k)
	  if(fprintf(d_file_p, "%f %f lineto\n", po[k].x, po[k].y) < 0) {
	    delete[] po;
	    XFree(raw_data);
	    PEXFreeOCData(num_elements, ocdata);
	    return FILE_WRITE_ERROR;
	  }
	if(fprintf(d_file_p, "stroke\n\n") < 1) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
	delete[] po;
      }
    }
    break;
  case PEXOCMarkerColor:
    d_markerCol = ocdata -> data.SetMarkerColor.color;
    break;
  case PEXOCMarkerType:
    d_markerType = ocdata -> data.SetMarkerType.marker_type;
    break;
  case PEXOCMarkerScale:
    d_markerScale = ocdata -> data.SetMarkerScale.scale;
    break;
  case PEXOCMarkers:
    {
      PEXCoord *po = new PEXCoord[ocdata -> data.Markers.count];
      PEXTransformPoints(d_ori, ocdata -> data.Markers.count,
			 ocdata -> data.Markers.points, po);
      mapToPage(po, po, ocdata -> data.Markers.count);
      for(k = 0; k < ocdata -> data.Markers.count; ++k)
	if(fprintf(d_file_p, "newpath\n%f %f %f circle\nfill\n", po[k].x, po[k].y, d_markerScale * 2.) < 0) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
      if(fprintf(d_file_p, "\n") < 1) {
	delete[] po;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      delete[] po;
    }
    break;
  case PEXOCPolyline:
    {
      PEXCoord *po = new PEXCoord[ocdata -> data.Polyline.count];
      PEXTransformPoints(d_ori, ocdata -> data.Polyline.count,
			 ocdata -> data.Polyline.points, po);
      mapToPage(po, po, ocdata -> data.Polyline.count);
      if(fprintf(d_file_p, "newpath\n%f %f moveto\n", po[0].x, po[0].y) < 0) {
	delete[] po;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      for(k = 1; k < ocdata -> data.Polyline.count; ++k)
	if(fprintf(d_file_p, "%f %f lineto\n", po[k].x, po[k].y) < 0) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
      if(fprintf(d_file_p, "stroke\n\n") < 1) {
	delete[] po;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      delete[] po;
    }
    break;
  case PEXOCSurfaceColor:
    d_surfCol = ocdata -> data.SetSurfaceColor.color;
    break;
  case PEXOCQuadrilateralMesh:
    {
      int n;
      PEXCoord *po = new PEXCoord[n = ocdata -> data.QuadrilateralMesh.col_count * ocdata -> data.QuadrilateralMesh.row_count];
      PEXTransformPoints(d_ori, n,
			 ocdata -> data.QuadrilateralMesh.vertices.no_data,
			 po);
      mapToPage(po, po, n);
      if(fprintf(d_file_p, "newpath\n") < 0) {
	delete[] po;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      for(i = 0, n = 0; i < ocdata -> data.QuadrilateralMesh.row_count; ++i) {
	if(fprintf(d_file_p, "%f %f moveto\n", po[n].x, po[n].y) < 0) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
	for(j = 1, ++n; j < ocdata -> data.QuadrilateralMesh.col_count; ++j, ++n)
	  if(fprintf(d_file_p, "%f %f lineto\n", po[n].x, po[n].y) < 0) {
	    delete[] po;
	    XFree(raw_data);
	    PEXFreeOCData(num_elements, ocdata);
	    return FILE_WRITE_ERROR;
	  }
      }
      for(i = 0; i < ocdata -> data.QuadrilateralMesh.col_count; ++i) {
	if(fprintf(d_file_p, "%f %f moveto\n", po[i].x, po[i].y) < 0) {
	  delete[] po;
	  XFree(raw_data);
	  PEXFreeOCData(num_elements, ocdata);
	  return FILE_WRITE_ERROR;
	}
	for(j = 1, n = i + ocdata -> data.QuadrilateralMesh.col_count; j < ocdata -> data.QuadrilateralMesh.row_count; ++j, n += ocdata -> data.QuadrilateralMesh.col_count)
	  if(fprintf(d_file_p, "%f %f lineto\n", po[n].x, po[n].y) < 0) {
	    delete[] po;
	    XFree(raw_data);
	    PEXFreeOCData(num_elements, ocdata);
	    return FILE_WRITE_ERROR;
	  }
      }
      if(fprintf(d_file_p, "stroke\n\n") < 1) {
	delete[] po;
	XFree(raw_data);
	PEXFreeOCData(num_elements, ocdata);
	return FILE_WRITE_ERROR;
      }
      delete[] po;
    } 
  }

  XFree(raw_data);
  PEXFreeOCData(num_elements, ocdata);

  return OK;
}

ErrorScope::Error PEXtoPSstream::showPage()
{
  if(fprintf(d_file_p, "showpage\n%%%%EOF\n") < 0)
    return FILE_WRITE_ERROR;
  
  return OK;
}
