/*
 * Copyright (C) 1997-2005, R3vis Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; 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/gpl.html.
 *
 * Contributor(s):
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/* 
 * $Id: text2d.c,v 1.12 2005/06/08 16:16:55 wes Exp $
 * $Revision: 1.12 $
 * $Name: OpenRM-1-6-0-2-c $
 * $Log: text2d.c,v $
 * Revision 1.12  2005/06/08 16:16:55  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.11  2003/04/13 18:13:23  wes
 * Updated copyright dates.
 *
 * Revision 1.10  2003/01/27 05:07:07  wes
 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
 *
 * Revision 1.9  2003/01/16 22:22:45  wes
 * Updated all source files to reflect new organization of header files: all
 * headers that were formerly located in include/rmaux, include/rmv and
 * include/rmi are now located in include/rm.
 *
 * Revision 1.8  2002/06/17 00:43:21  wes
 * updated copyright line.
 *
 * Revision 1.7  2001/07/15 22:33:19  wes
 * Added rmPipeDelete to the end of all demo progs. For those that use
 * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
 *
 * Revision 1.6  2001/06/03 19:44:05  wes
 * Add calls to new rmaux routines to handle window resize events, and
 * for keyboard event handling.
 *
 * Revision 1.5  2001/03/31 16:55:18  wes
 * Added procmode.h, which defines an RMpipe processing mode used in
 * most demonstration programs. The default processing mode is
 * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
 *
 * Revision 1.4  2000/12/02 17:24:32  wes
 * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
 * a summary of changes. With this checkin, these demo programs
 * are no longer compatible with versions of the OpenRM API that
 * are pre-1.4.0.
 *
 * Revision 1.3  2000/08/28 01:38:18  wes
 * Updated rmaux* interfaces - rmauxEventLoop now takes two additional
 * parameters (keypress and window resize app callbacks); replaced
 * rmauxUI with rmauxSetGeomTransform and, where appropriate,
 * rmauxSetCamera3DTransform.
 *
 * Revision 1.2  2000/04/20 18:04:34  wes
 * Minor tweaks and reorg for OpenRM 1.2.1.
 *
 * Revision 1.1.1.1  2000/02/28 21:55:30  wes
 * OpenRM 1.2 Release
 *
 * Revision 1.9  2000/02/28 17:21:55  wes
 * RM 1.2, pre-OpenRM
 *
 */

#include <rm/rm.h>   
#include <rm/rmaux.h>
#include "procmode.h"

static RMnode *MyRoot;
int img_width=600,img_height=480;

typedef struct tstruct
{
    RMvertex2D xy;
    char string[RM_MAX_STRING_LENGTH];
    int font,italic,bold,size;
    RMcolor3D color;
} tstruct;

static RMenum hJustify=RM_LEFT, vJustify = RM_BOTTOM;

#define NUM_TEXT_STRINGS 14

static tstruct pile_o_text[NUM_TEXT_STRINGS] =
{
    {
       {0.1,0.55},
       "Serif XS NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_XS,
       {1.0,0.8,0.8}
    },
    {
       {0.1,0.5},
       "Serif XXS NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_XXS,
       {1.0,1.0,1.0}
    },
    {
       {0.1,0.6},
       "Serif S NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_S,
       {1.0,0.6,0.6}
    },
    {
       {0.1,0.65},
       "Serif M NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_M,
       {1.0,0.4,0.4}
    },
    {
       {0.1,0.7},
       "Serif L NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_L,
       {1.0,0.2,0.2}
    },
    {
       {0.1,0.75},
       "Serif XL NonItalic NonBold",
       RM_FONT_SERIF, RM_FALSE, RM_FALSE, RM_FONT_XL,
       {1.0,0.0,0.0}
    },
    {
       {0.1,0.8},
       "Serif XXL NonItalic NonBold", 
       RM_FONT_SERIF, RM_TRUE, RM_TRUE, RM_FONT_XXL,
       {1.0,0.0,0.0}
    },
    
    {
       {0.3,0.1},
       "Sans XXS NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_XXS,
       {1.0,1.0,1.0}
    },
    {
       {0.3,0.15},
       "Sans XS NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_XS,
       {0.8,0.8,1.0}
    },
    {
       {0.3,0.2},
       "Sans S NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_S,
       {0.6,0.6,1.0}
    },
    {
       {0.3,0.25},
       "Sans M NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_M,
       {0.4,0.4,1.0}
    },
    {
       {0.3,0.3},
       "Sans L NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_L,
       {0.2,0.2,1.0}
    },
    {
       {0.3,0.35},
       "Sans XL NonItalic NonBold",
       RM_FONT_SANS, RM_FALSE, RM_FALSE, RM_FONT_XL,
       {0.,0.,1.0}
    },
    {
       {0.3,0.4},
       "Sans XXL NonItalic NonBold",
       RM_FONT_SYMBOL, RM_FALSE, RM_TRUE, RM_FONT_XXL,
       {0.,0.,1.0}
    }
};


void
my_set_scene(RMnode *camNode)
{
    RMcamera2D *c = rmCamera2DNew();;

    rmDefaultCamera2D(c);	/* assign it some default values. */

    /* change the default scene camera from -1..1 to 0..1  */
    rmCamera2DSetExtents(c,0.0F,0.F,1.0F,1.0F);

    /* add the camera to "my root's" scene parms. */
    rmNodeSetSceneCamera2D(camNode,c);
}

void
my_build_objs(void)
{
    int i,limit;
    RMprimitive *tlist;
    RMnode *textRoot;
    RMnode *textNode;

    MyRoot = rmNodeNew("MyRoot", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    textRoot = rmNodeNew("textRoot",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    
    rmNodeAddChild(rmRootNode(),MyRoot); 
    rmNodeAddChild(MyRoot,textRoot);

    limit = NUM_TEXT_STRINGS;

    /*
     * each text string is assumed to be of a different font, size, etc.
     * so each text string gets a unique node in the scene graph. the
     * scene graph node contains the font, size, italic, embolden
     * enumerators, while the underlying scene graph node primitive
     * contains the actual text string itself.
     */
    for (i=0;i<limit;i++) 
    {
	char name[32];
	RMtextProps *tp;
	char *strings[1];

	sprintf(name,"text-%d",i);
	
	textNode = rmNodeNew(name,RM_RENDERPASS_2D,RM_RENDERPASS_OPAQUE);
	tlist = rmPrimitiveNew(RM_TEXT);
	rmNodeAddPrimitive(textNode, tlist);
	rmNodeAddChild(textRoot, textNode);

	tp = rmTextPropsNew();
	rmTextPropsSetAttribs(tp,
			      pile_o_text[i].font,
			      pile_o_text[i].size,
			      pile_o_text[i].bold,
			      pile_o_text[i].italic,
			      hJustify,
			      vJustify);
	
	rmNodeSetSceneTextProps(textNode, tp);
	rmTextPropsDelete(tp);

	rmPrimitiveSetVertex2D(tlist,1,
			       &(pile_o_text[i].xy), /* pointer to stuff */
			       RM_COPY_DATA, /* let RM manage this data */
			       NULL); /* don't need a freefunc for RM_COPY_DATA */
	
	/* now, we need to add the stuff into the text
	 primitive so that the PS driver can deal with it ok. */

	strings[0] = pile_o_text[i].string;
	rmPrimitiveSetText(tlist,1,strings);

	rmPrimitiveSetColor3D(tlist,1,
			      &(pile_o_text[i].color),
			      RM_COPY_DATA,
			      NULL);
    }

    /*
     * set text justification parameter, controls justification for all
     * text prims at the given node.
     */

    {
        RMvertex3D bmin,bmax,center;

        rmNodeUnionAllBoxes(rmRootNode()); 

	rmNodeGetBoundingBox(rmRootNode(),&bmin,&bmax);
	center.x = bmin.x + 0.5F *(bmax.x - bmin.x);
	center.y = bmin.y + 0.5F *(bmax.y - bmin.y);
	center.z = bmin.z + 0.5F *(bmax.z - bmin.z);

	rmNodeSetCenter(rmRootNode(),&center);
    }
    {
	RMcolor4D bgcolor={0.2,0.2,0.3,1.0};
	/*
	 * assign a background color to take effect at "MyRoot"
	 */
	rmNodeSetSceneBackgroundColor(MyRoot,&bgcolor);
    }
}

void
myinitfunc(RMpipe *p,
	   RMnode *n)
{
    my_build_objs();
    my_set_scene(MyRoot);
    rmauxSetGeomTransform(n, p);
    
    /*
     * set handler to reset aspect ratio when the window is resized.
     */
    rmauxSetResizeFunc(p, MyRoot, rmauxDefaultResizeFunc);
    
    if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
	rmFrame(p, n);
    
    rmFrame(p, n);
}


#ifdef RM_WIN
int WINAPI WinMain (HINSTANCE hInstance,
		    HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
    MSG      msg; 
    HWND     hWnd; 
    void *fptr;
    RMenum targetPlatform = RM_PIPE_WGL;
#else  /* assume RM_X */
int
main()
{
    void *msg;			/* needed for rmauxEventLoop
				 win32/unix API consistency */
    RMenum targetPlatform = RM_PIPE_GLX;
#endif
    RMpipe *lone_pipe=NULL;
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */

    /* 
     * first stage of RM initialization.
     */
    rmInit();

    /* 
     * create the rendering pipe. this step is required in both
     * Win32 and X.
     */
    lone_pipe = rmPipeNew(targetPlatform);

    rmPipeSetProcessingMode(lone_pipe, processingMode);
    
#ifdef RM_WIN
    {
        /*
	 * Win32: when a window is created, we have to tell windows the
	 * name of the "WndProc," the procedure that gets called by
	 * windows with events (the event loop) (contrast to the X model 
	 * where the name of the event loop is not part of the window). 
	 * Since we're using RMaux, we know about the event handling 
	 * procedure named "rmauxWndProc" and we provide that here. 
	 */

        fptr = (void *)(rmauxWndProc);
	hWnd = rmauxCreateW32Window(lone_pipe,
			       NULL, /* no parent window */
			       20,20,img_width,img_height,"RM for Windows",
			       hInstance,fptr);
	if (hWnd == 0)
	  exit(-1);

	/* 
	 * assign the new window handle to the rendering pipe.
	 */
	rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
    }
#endif
    
#ifdef RM_X
    {
	Window w;

	w = rmauxCreateXWindow(lone_pipe,
			       (Window)NULL, /* parent window */
			       0,0,img_width,img_height,
			       "RM for X-Windows","icon-title",RM_TRUE);
	/* 
	 * assign the window to the rendering pipe.
	 */
	rmPipeSetWindow(lone_pipe,w,img_width,img_height);
    }
#endif
    /* 
     * specify the name of the "init" function. the "init" function is
     * mandatory in the Win32 world, and optional in the X world. 
     *
     * in Win32, we don't want to call RM services until OpenGL is
     * ready. we can be assured of readiness by using an init function
     * with RMaux. 
     *
     * in X, at this point, the window is mapped and OpenGL is ready,
     * and we could call our init function directly.
     */

    rmauxSetInitFunc(myinitfunc); 

    /* 
     * X-ism: once the window is created and assigned to the 
     * rendering pipe, rmUsePipe makes the OpenGL rendering context
     * current for the pipe+window combination. 
     *
     * this step is required for X. in these demo programs, it is not 
     * strictly required by Win32, as the newly created context is made
     * current as part of the OpenGL initialization sequence.
     */
    rmPipeMakeCurrent(lone_pipe);	

    /*
     * set key handler function so this prog will exit on "q" key.
     */
    rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
    
    rmauxEventLoop(lone_pipe,rmRootNode(), &msg);

    rmPipeDelete(lone_pipe);
    rmFinish();

#ifdef RM_WIN
    return( msg.wParam );
#else
    return(1);
#endif
}
