/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmxtext.c,v 1.9 2006/11/19 17:13:23 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.9 $
 * $Log: rmxtext.c,v $
 * Revision 1.9  2006/11/19 17:13:23  wes
 * When creating bitmap fonts for display on X-based systems, we need an
 * open X display in the RMpipe. Added code to try and open $DISPLAY when
 * the app hasn't set the display explicitly. This step saves some
 * additional app intitialization steps in some cases.
 *
 * Revision 1.8  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.7  2005/05/06 16:38:58  wes
 *  Added code inside private_rmPrepareBitmapFont to detect the absence
 * of an open X Display, and generate an error msg rather than just die
 * with a segfault.
 *
 * Revision 1.6  2005/02/19 16:40:20  wes
 * Distro sync and consolidation.
 * Repairs to fix memory leak associated with repeated calls to rmPipeNew,
 * rmPipeMakeCurrent, rmPipeClose.
 *
 * Revision 1.5  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.4  2004/01/16 16:49:50  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.3  2003/02/14 00:20:29  wes
 * Minor tweaks to avoid compile problems on SGI.
 *
 * Revision 1.2  2003/02/02 02:07:16  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.10  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.9  2002/12/04 14:50:33  wes
 * Cleanup SGI compiles.
 *
 * Revision 1.8  2002/08/29 22:20:32  wes
 *
 * Massive upgrade to accommodate dynamic object reallocation within
 * the component manager, and within the context cache. Use the
 * debug #define DEBUG_LEVEL DEBUG_REALLOC_TRACE to get a printf
 * whenever a realloc occurs. With this upgrade, there are no
 * OpenRM limits on the size of the scene graph. There will be external
 * limits, such as the amount of RAM and the amount of space available
 * to your OpenGL implementation.
 *
 * Revision 1.7  2002/04/30 19:37:09  wes
 * Updated copyright dates.
 *
 * Revision 1.6  2001/06/03 20:47:52  wes
 * Removed last vestiges of the notion of a "current pipe."
 *
 * Revision 1.5  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.4  2000/12/03 22:35:39  wes
 * Mods for thread safety.
 *
 * Revision 1.3  2000/05/17 14:25:56  wes
 * Fixed compile warnings on rmTextPropsSet/GetAttribs().
 *
 * Revision 1.2  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

#include <string.h>
#include <rm/rm.h>
#include "rmprivat.h"

/*
 * ----------------------------------------------------
 * @Name rmTextGetExtents
 @pstart
 RMenum rmTextGetExtents (const char *string,
		          int fontEnum,
			  int sizeEnum,
			  int boldEnum,
			  int italicEnum,
			  int *widthReturn,
			  int *heightReturn,
			  const RMpipe *renderPipe)
 @pend

 @astart
 const char *string - a character string (input).

 int fontEnum - an integer value specifying a font family. May be one
    of RM_FONT_SERIF, RM_FONT_SANS, RM_FONT_MONO, RM_FONT_SYMBOL or
    RM_FONT_DINGBATS (input).

 int sizeEnum - an integer font size enumerator. May be one of
    RM_FONT_XXS, RM_FONT_XS, RM_FONT_S, RM_FONT_M, RM_FONT_L,
    RM_FONT_XL, RM_FONT_XXL.

 int boldEnum, italicEnum - integer values indicating whether or not
    to the text string is in boldface or italicized. Valid values are
    RM_TRUE or RM_FALSE (input).

 int *widthReturn, *heightReturn - pointers to integers. The width and
    height of the rendered text string is returned in through these
    parameters.  Values of NULL are acceptable (return).

 const RMpipe *renderPipe - a handle to an RMpipe (input).
 @aend

 @dstart

 Use this routine to obtain the pixel dimensions of the bounding box
 of a text string. The text string may be rendered in one of a number
 of fonts, at one of a number of pre-specified sizes and with or
 without emboldening or italicization.

 This routine doesn't actually cause any rendering to occur on-screen,
 but does require that RM be completely initialized.

 10/2000 - Added the renderPipe parameter. 

 Returns RM_CHILL upon success, or RM_WHACKED upon failure.

 Either, or both, of widthReturn or heightReturn may be NULL,
 indicating the application is not interested in those values.

 @dend
 * ----------------------------------------------------
 */
RMenum
rmTextGetExtents (const char *string,
		  int fontEnum,
		  int sizeEnum,
		  RMenum boldEnum,
		  RMenum italicEnum,
		  int *widthReturn,
		  int *heightReturn,
		  const RMpipe *renderPipe)
{
    char        mystring[128];
    RMtextProps tp;
    RMtextPrim  p;
    RMfontRegistry *fr;

    if ((RM_ASSERT(string, "rmTextGetExtents() error: the input string is NULL") == RM_WHACKED) ||
	(RM_ASSERT(renderPipe, "rmTextGetExtents() error: the input RMpipe is NULL") == RM_WHACKED))
	return(RM_WHACKED);

    rmTextPropsSetAttribs(&tp, fontEnum, sizeEnum, boldEnum, italicEnum, RM_LEFT, RM_BOTTOM);
    private_rmPrepareBitmapFont(&tp, (RMpipe *)renderPipe);

    strcpy(mystring, string);
    p.string = mystring;
    
    fr = private_rmFontRegistryEntry(fontEnum, sizeEnum, italicEnum, boldEnum, renderPipe->contextCache->pipeFontRegistry);
    
    private_rmTextPrimComputeCW(&p, fr, renderPipe);

    if (widthReturn != NULL)
	*widthReturn = p.bw;

    if (heightReturn != NULL)
	*heightReturn = p.bh;

    return(RM_CHILL);
}


#ifdef RM_X
/*
 * The following four arrays are used to construct an X font name
 * from user parameters.  The user can specify an enumerated font family
 * name, an enumerated size, and boolean values for italic and embolden.
 *
 * The stuff in these four arrays is concatenated together to produce a
 * font name that X can digest.
 *
 * Tue Jun  3 17:15:38 PDT 1997
 * there's a really weird bug that appears when running on an SGI
 * machine but displaying on solaris/opengl.  it turns out that bitmaps
 * which are not an even multiple of 8 in width (ie, not on a byte boundary)
 * don't seem to work right.  i'm not sure if this bug is in the solaris
 * opengl ....  what i'm going to do is wait a little bit for the
 * next version of opengl from sun.  if that doesn't fix the problem, then
 * what we can do is alter the width of our bitmap mask so that we
 * have as a width a value which is an even multiple of 8.  the code
 * works fine on both SGI and SUn, the problem shows up when displaying
 * across a network onto a sun from an sgi.
 *
 * Thu Jun  5 12:53:06 MDT 1997
 * added code to call the scalable zapf dingbat font.  for the future, a
 * nice enhancement to this code would be to load ONLY scalable fonts. it
 * would streamline the code a little bit.
 *
 * Sat Jun 14 15:45:03 MDT 1997
 * use RMbitmaps rather than a char[][] inside the internal text primitive.
 *
 * Fri Nov 28 12:20:45 PST 1997
 * reorgs to isolate X dependancies.
 */

static char *font_families_x[] =
{ 
  "adobe-times",	/* RM_FONT_SERIF */
  "adobe-helvetica",	/* RM_FONT_SANS */
  "adobe-courier",	/* RM_FONT_MONO */
  "adobe-symbol",	/* RM_FONT_SYMBOL */
  "*-*dingbats"		/* RM_FONT_DINGBATS */
};

static char *font_sizes_x[] =
{
    "8",		/* RM_XXS */
    "10",		/* RM_XS */
    "12",		/* RM_S */
    "14",		/* RM_M */
    "18",		/* RM_L */
    "24",		/* RM_XL */
    "34"		/* RM_XXL */
};

/* hard to believe, but these are different */
static char *font_sans_italic_x[]=
{
    "r",		/* for RM_FALSE on italic */
    "o"			/* for RM_TRUE on italic */
};
static char *font_serif_italic_x[]=
{
    "r",		/* for RM_FALSE on italic */
    "i"			/* for RM_TRUE on italic */
};

static char *font_bold_x[] =
{
    "medium",		/* for RM_FALSE on bold */
    "bold"		/* for RM_TRUE on bold */
};

/* these fonts get used if the user doesn't explicitly ask for one, or
   if one of the requested fonts doesn't exist. the goal is to have
   something reasonable happen in the presence of an error condition. */
   
#define NFALLBACK_FONTS 3
char fallback_fonts[NFALLBACK_FONTS][80] =
{
    "*-times-*-r-*14*",	/* try to grab a 14 point times-roman font */
    "*times-roman*",	/* try to grab a plain old times-roman font */
    "fixed"		/* grab a really ugly font */
};


#define DepthOf(disp)	DefaultDepth(disp, DefaultScreen(disp))
#define PlanesOf(disp)	((1 << DepthOf(disp)) - 1)   /* or AllPlanes */


/* PRIVATE
 *
 * this private routine will check to see if a given font family/size/style
 * has been initialized for use. if not, it will prepare the given font
 * specification for use by the system.
 */
RMenum
private_rmPrepareBitmapFont (RMtextProps *t,
			     RMpipe *p)
{
   RMfontRegistry *fr;
   XFontStruct    *xfs;

   fr = private_rmFontRegistryEntry(t->fontEnum, t->sizeEnum, t->italicEnum, t->boldEnum, p->contextCache->pipeFontRegistry);
   
   if (fr == NULL)
       return(RM_WHACKED);

   if (fr->initialized == 0)	/* this font/size/style not yet initialized */
   {
       char    font[128];
       char   *typeface;
       int     rmtfont;
       int     height;
       RMenum     weight;		/* what's this supposed to be? */
       RMenum     italic;		/* what's this supposed to be? */
       int     iweight, iitalic;
       GLuint  base;
       RMenum  hjustify, vjustify;

       rmTextPropsGetAttribs(t, &rmtfont, &height, &weight, &italic, &hjustify, &vjustify);
       
       iweight = (weight == RM_TRUE) ? 1 : 0;
       iitalic = (italic == RM_TRUE) ? 1 : 0;
       typeface = font_families_x[rmtfont];
       
       /* build an X font name from user parameters. */
       memset(font, 0, 128);
       font[0] = '-';

       /* do the font family - note that rmtfont is an index - therefore the RM_FONT_*
	set of enums are really indices, not RMenums proper at this time.*/
       
       strcat(font,font_families_x[rmtfont]);
       strcat(font,"-");

       /* do the embolden. symbol fonts aren't emboldened */
       if ((rmtfont == RM_FONT_SYMBOL) || (rmtfont == RM_FONT_DINGBATS))
	   strcat(font, font_bold_x[RM_FALSE]);
       else
	   strcat(font, font_bold_x[iweight]);
       strcat(font,"-");

       /* do the italics. symbol fonts aren't emboldened */
       if (rmtfont == RM_FONT_SYMBOL)
	   strcat(font, font_serif_italic_x[RM_FALSE]);
       else 
	   if (rmtfont == RM_FONT_SERIF)
	       strcat(font, font_serif_italic_x[iitalic]);
	   else	/* assume all non-serif fonts behave the same*/
	       strcat(font, font_sans_italic_x[iitalic]);
	
       strcat(font,"-normal--");

       /* do the size */
       if (rmtfont != RM_FONT_DINGBATS)
       {
	   strcat(font, font_sizes_x[height]);
	   strcat(font,"-*");
       }
       else /* this is ugly...yep */
       {
	   char     buf[32];
	   int      screen, res_x, res_y;
	   int      points;
	   Display *dpy;

	   dpy = rmxPipeGetDisplay(p);
	   screen = DefaultScreen(dpy);
	    
	   /* calculate our screen resolution in dots per inch. 25.4mm = 1 inch */
	   res_x = DisplayWidth(dpy, screen) / (DisplayWidthMM(dpy, screen) / 25.4);
	   res_y = DisplayHeight(dpy, screen) / (DisplayHeightMM(dpy, screen) / 25.4);
	   sscanf(font_sizes_x[height], "%d", &points);
	   points *= 10;
	   memset(buf, 0, 32);
	   sprintf(buf, "%d", points);

	   strcat(font, "*-");
	   strcat(font, buf);
	   strcat(font, "-");
	
	   memset(buf, 0, 32);
	   sprintf(buf, "%d", res_x);
	   strcat(font, buf);
	   strcat(font, "-");
	   sprintf(buf, "%d", res_y);
	   strcat(font, buf);
	   strcat(font, "-p-0-*-fontspecific");
       }

       /* first, load a font...maybe... */
       if (rmxPipeGetDisplay(p) == NULL)
       {
	   /* if the app hasn't set the display, try to open one */
	   Display *d = XOpenDisplay(getenv("$DISPLAY"));

	   if (d == NULL)
	   {
	       rmError("private_rmPrepareBitmapFont fatal error: the input RMpipe does not have a valid, open XDisplay and RM's attempt to open one via XOpenDisplay(getenv($DISPLAY)) has failed. Please assign one using rmxPipeSetDisplay prior to attempting to draw text objects. ");
	       exit(-1);
	   }
	   else
	       rmxPipeSetDisplay(p, d);
       }
       
       xfs = XLoadQueryFont(rmxPipeGetDisplay(p), font);

       if (xfs == NULL)		/* cant find the requested font */
       {
	   char s[256];
	   int  i;

	   sprintf(s, "warning: private_rmPrepareBitmapFont() cannot find the X font named <%s> to honor your request. Will attempt to find a generic system font to use instead. \n", font);
	   rmWarning(s);

	   for (i = 0; i < NFALLBACK_FONTS; i++)
	   {
	       fprintf(stderr, " trying the font: <%s> \n", fallback_fonts[i]);
	       xfs = XLoadQueryFont(rmxPipeGetDisplay(p), fallback_fonts[i]);

	       if (xfs != NULL)
		  break;
	   }

	   /*
	    * if things are really bad, we can't get any fonts, even one of
	    * the ugly fallback fonts. if things are this bad, we wave the
	    * white flag and exit.
	    *
	    * i've never seen this code block get executed, but it's in
	    * here just in case...
	    */
	   if (i == NFALLBACK_FONTS)
	   {
	       rmError(" RM can't find any fonts on this system, and is unable continue.");
	       exit(-1); 
	   }
       }

       if ((base = glGenLists(96)) == 0)
	   return(RM_WHACKED);
       glXUseXFont(xfs->fid, 32, 96, base);

       fr->initialized = 1;
       fr->listbase = base;
       fr->listCount = 96;
       fr->refcount = 1;
       fr->fontinfo = (void *) xfs;
       fr->listoffset = 32;
   }
   else
       fr->refcount += 1;

#if 0
   /* removed display list stuff from the scene graph!*/
   t->listbase = fr->listbase;
   t->listoffset = 32;
   t->fontinfo = fr->fontinfo;
#endif

   return(RM_CHILL);
}


/* PRIVATE
 *
 * compute width/height of a text string using Xlib. this routine is used
 * by rmTextGetExtents, as well as by the text drawing routine.
 */
int
private_rmTextPrimComputeCW (RMtextPrim *p,
			     RMfontRegistry *fr,
			     const RMpipe *pipe)
{
    char        *s;
    int          n;
    int          direction_return;
    int          font_ascent_return, font_descent_return;
    XCharStruct  overall_return;
    XFontStruct *xfs;

    pipe = NULL; 		/* foil compiler warning */

    xfs = (XFontStruct *)(fr->fontinfo);
    s = p->string;
    n = strlen(s);
    
    XTextExtents(xfs, s, n, &direction_return, &font_ascent_return, &font_descent_return, &overall_return);

    p->bw = overall_return.width;
    p->bh = overall_return.ascent + overall_return.descent;

    return(RM_CHILL);
}

#endif /* RM_X */
/* EOF */
