/*----------------------------------------------------------------------*/
/* opengl.c --- routines defining graphics, whether OpenGL or X11	*/
/* Copyright (c) 2005  Tim Edwards, MultiGiG, Inc.			*/
/*----------------------------------------------------------------------*/

#ifdef OPENGL

/*----------------------------------------------------------------------*/
/*      written by Tim Edwards, 6/1/05    				*/
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>

/*----------------------------------------------------------------------*/
/* Local includes							*/
/*----------------------------------------------------------------------*/

#ifdef TCL_WRAPPER
#include <tk.h>
#endif

#include "xcircuit.h"
#include "colordefs.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                                      */
/*----------------------------------------------------------------------*/
#include "prototypes.h"

extern XtAppContext app;
extern Colormap cmap;
extern int *appcolors;

extern char STIPDATA[STIPPLES][4];

extern float gl_line_limit, gl_point_limit;

/*----------------------------------------------*/
/* Set color in OpenGL (this should be replaced	*/
/* for speed. . .)				*/
/*----------------------------------------------*/

void
SetForeground(Display *dpy, GC nullptr, int idx)
{
   XColor fgcolor;
   int red, green, blue;

   fgcolor.pixel = idx;
   fgcolor.flags = DoRed | DoGreen | DoBlue;

   XQueryColor(dpy, cmap, &fgcolor);

   glColor3us((GLushort)fgcolor.red, (GLushort)fgcolor.green,
		(GLushort)fgcolor.blue);
}

/*----------------------------------------------*/
/* Don't know if this is meaningful or not.	*/
/*----------------------------------------------*/

void
SetBackground(Display *dpy, GC nullptr, int idx)
{
   /* null proc */
}

/*------------------------------------------------------*/
/* Set drawing function					*/
/*------------------------------------------------------*/

void
SetFunction(Display *dpy, GC nullptr, int xfunction)
{
   switch (xfunction) {
      case GXcopy:
	 glDisable(GL_COLOR_LOGIC_OP);
	 /* Is it better to disable the logic op? */	
	 /*
	 glEnable(GL_COLOR_LOGIC_OP);
	 glLogicOp(GL_COPY);
	 */
	 break;
      case GXxor:
	 glEnable(GL_COLOR_LOGIC_OP);
	 glLogicOp(GL_XOR);
	 break;
   }
}

/*------------------------------------------------------*/
/* Set line attributes
/*------------------------------------------------------*/

void
SetLineAttributes(Display *dpy, GC nullptr, float lwidth,
	int ldash, int lcap, int lbevel)
{
   GLfloat glwidth = (lwidth <= 0) ? 1 : (GLfloat)lwidth;
   glLineWidth(glwidth);
   glPointSize(glwidth);
   if (lwidth > gl_line_limit)
      glDisable(GL_LINE_SMOOTH);
   else
      glEnable(GL_LINE_SMOOTH);
   if (lwidth > gl_point_limit)
      glDisable(GL_POINT_SMOOTH);
   else
      glEnable(GL_POINT_SMOOTH);
   switch (ldash) {
      case LineSolid:
	 glDisable(GL_LINE_STIPPLE);
	 break;
      case LineOnOffDash:
	 glEnable(GL_LINE_STIPPLE);
	 break;
   }
   /* Cap and Mitre not handled by OpenGL */
}

/*------------------------------------------------------*/
/* Set Line dash					*/
/*------------------------------------------------------*/

void
SetDashes(Display *dpy, GC nullptr, int offset, char dashlist[], int n)
{
   union {
      char dashbytes[2];
      unsigned short dashpat;
   } dashspec;

   dashspec.dashbytes[0] = dashlist[0];
   dashspec.dashbytes[1] = dashlist[1];

   glLineStipple((GLint)1, (GLushort)dashspec.dashpat);
}

/*------------------------------------------------------*/
/* Set Polygon fill stipple (optimize this!)		*/
/*------------------------------------------------------*/

void
SetStipple(Display *dpy, GC nullptr, int stipple)
{
   unsigned char stipdata[128];
   char *stipsrc = STIPDATA[stipple];
   char *stipdest = stipdata;
   int i;
   for (i = 0; i < 32; i++) {
      memcpy(stipdest, stipsrc, 4);
      stipdest += 4;
   }
   glPolygonStipple((GLubyte *)stipdata);
}

/*------------------------------------------------------*/
/* Set Polygon fill style				*/
/* (Currently there is no differentiating between the	*/
/* FillStippled and FillOpaqueStippled styles)		*/
/*------------------------------------------------------*/

void
SetFillStyle(Display *dpy, GC nullptr, int fillstyle)
{
   switch(fillstyle) {
      case FillSolid:
	 glDisable(GL_POLYGON_STIPPLE);
	 break;
      case FillStippled:
	 glEnable(GL_POLYGON_STIPPLE);
	 break;
      case FillOpaqueStippled:
	 glEnable(GL_POLYGON_STIPPLE);
	 break;
   }
}

/*------------------------------------------------------*/
/* Draw line (compatibility function for XDrawLine()	*/
/*------------------------------------------------------*/

void
DrawLine(Display *dpy, Window win, GC nullptr,
	int x1, int y1, int x2, int y2)
{
   glBegin(GL_LINES);
   glVertex2i(x1, y1);
   glVertex2i(x2, y2);
   glEnd();

   /* Produce round-cap ends by drawing points at the ends */
   glBegin(GL_POINTS);
   glVertex2i(x1, y1);
   glVertex2i(x2, y2);
   glEnd();
}

/*--------------------------------------------------------------*/
/* Draw multiple lines (compatibility function for XDrawLines()	*/
/* (This should be using a chained line segments function)	*/
/*--------------------------------------------------------------*/

void
DrawLines(Display *dpy, Window win, GC nullptr,
	XPoint *points, int npoints, int mode)
{
   int i;
   glBegin(GL_LINE_STRIP);
   for (i = 0; i < npoints; i++)
      glVertex2i(points[i].x, points[i].y);
   glEnd();

   /* Round-caps */
   glBegin(GL_POINTS);
   for (i = 0; i < npoints; i++)
      glVertex2i(points[i].x, points[i].y);
   glEnd();
}

/*------------------------------------------------------*/
/* Draw point (compatibility function for XDrawPoint()	*/
/*------------------------------------------------------*/

void
DrawPoint(Display *dpy, Window win, GC nullptr, int x, int y)
{
   glBegin(GL_POINTS);
   glVertex2i(x, y);
   glEnd();
}

/*----------------------------------------------------------------------*/
/* Draw a filled polygon (compatibility function for XFillPolygon()	*/
/*----------------------------------------------------------------------*/

void
FillPolygon(Display *dpy, Window win, GC nullptr, XPoint *points,
	int npoints, int shape, int mode)
{
   int i;
   static GLUtesselator *tess = NULL;
   GLdouble v[3];

/*
   glBegin(GL_POLYGON);
   for (i = 0; i < npoints; i++)
      glVertex2i(points[i].x, points[i].y);
   glEnd();
*/

   if (tess == NULL) {
      tess = gluNewTess();
      gluTessCallback(tess, GLU_BEGIN, glBegin);
      gluTessCallback(tess, GLU_VERTEX, glVertex3dv);
      gluTessCallback(tess, GLU_END, glEnd);
      v[2] = 0.0;
   }
   gluBeginPolygon(tess);
   for (i = 0; i < npoints; i++) {
      v[0] = (GLdouble)points[i].x;
      v[1] = (GLdouble)points[i].y;
      gluTessVertex(tess, v, v);
   }
   gluEndPolygon(tess);
}

/*------------------------------------------------------*/
/* Render a background image				*/
/*------------------------------------------------------*/

void
backgroundbbox(int b)
{
   /* null proc for now . . . */
}

void
readbackground(FILE *f)
{
   /* null proc for now . . . */
}

void
savebackground(FILE *f, char *c)
{
   /* null proc for now . . . */
}

void
loadbackground()
{
   /* null proc for now . . . */
}

int
renderbackground()
{
   /* null proc for now . . . */
}

int
copybackground()
{
   /* null proc for now . . . */
}

int
reset_gs()
{
   /* This is a null proc because OpenGL doesn't use	*/
   /* ghostscript, and it's easier to make a null proc	*/
   /* than to #ifdef the calls.				*/

}

int
exit_gs()
{
   /* ditto */
}

/*-------------------------------------------------------------------------*/

#endif /* OPENGL */
