/******************************************************************************/
/* Copyright 1995 MBARI                                                       */
/******************************************************************************/
/* Summary  : CTD Data Display Graphics Module                                */
/* Filename : displayGraphics.c                                               */
/* Author   : Janice Tarrant                                                  */
/* Project  : Tiburon                                                         */
/* Version  : Version 1.0                                                     */
/* Created  : 02/13/95                                                        */
/* Modified :                                                                 */
/* Archived :                                                                 */
/******************************************************************************/
/* Modification History :                                                     */
/* $Header: /usr/tiburon/unix/gui/ctd/RCS/displayGraphics.c,v 1.1 1998/03/12 17:52:41 oreilly Exp $
 * $Log: displayGraphics.c,v $
 * Revision 1.1  1998/03/12 17:52:41  oreilly
 * Initial revision
 *
 *
 */
/******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Xm/Xm.h>
#include <mbari/types.h>
#include <dmUnix.h>             /* unix data manager definitions              */
#include <semLib.h>             /* semaphore library                          */
#include <time.h>               /* MBARI time declarations                    */
#include <datamgr.h>            /* data manager declarations                  */
#include <dm_errno.h>           /* data manager error declarations            */
#include "ctdDisplay.h"

void  initGraphics(Widget w, plotParameters *plotParams);
void  setXParams(varNum x, Display *display, Colormap *colourMap,
		 Pixel *foreground, plotParameters *plotParams);
Int16 countToken(char *string);
void  makeTickLabel(varNum var, plotParameters *plotParams);
void  initAxesGeom(plotParameters *plotParams);
Int16 odd(Int16 n);

extern configurationParameters configParams;

extern MBool plotScrolling;
extern Int32 plotStart;
extern Int32 plotEnd;
extern Int32 plotMax;
extern Int32 plotSpan;
extern Flt32 minInSecs;				/* min time value in seconds  */
extern Flt32 maxInSecs;				/* max time value in seconds  */
extern Flt32 yTimeUnitsScale;			/* time units y scale         */


/******************************************************************************/
/* Function : initGraphics                                                    */
/* Purpose  : Initializes the data display graphics.                          */
/* Inputs   : Drawing widget, plotting data and display data.                 */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
initGraphics(Widget w, plotParameters *plotParams)
{
    char      buffer[AXIS_LABEL_LENGTH];
    char      *tmpString;
    Int16     i, var;
    Int16     largestYLabel;
    Display   *display = XtDisplay(w);
    Int16     screen = DefaultScreen(w);
    Colormap  colourMap = DefaultColormap(display, screen);
    Pixel     foreground;

    plotParams->displayData.display = w;
    XtVaGetValues(w, XmNforeground, &foreground, NULL);

/* set x variable plot parameters                                             */
    for (var = 0; var < configParams.numXVariables; var++)
    	setXParams(var, display, &colourMap, &foreground, plotParams);

/* set y variable plot parameters                                             */
    plotParams->displayData.colours[Y] = foreground;
    strcpy(plotParams->axisLabel[Y], configParams.yVariable.axisLabel);
    plotParams->labelLength[Y] = strlen(configParams.yVariable.axisLabel);
    plotParams->majorDivisions[Y] = configParams.yVariable.majorDivisions;
    plotParams->numTicks[Y] = configParams.yVariable.majorDivisions - 1;
    plotParams->displayData.minValues[Y] = configParams.yVariable.minValue;
    plotParams->displayData.maxValues[Y] = configParams.yVariable.maxValue;

    makeTickLabel(Y, plotParams);

/* find position of words in y axis label                                     */
    for (i = 0; i < NUM_Y_WORDS; i++)
	 plotParams->yLabelSpace[i] = 0;
    strcpy(buffer, configParams.yVariable.axisLabel);
    if ( (tmpString = strtok(buffer, " ")) !=0)
    {
    	plotParams->yLabelSpace[0] = strlen(tmpString) + 1;
	largestYLabel = plotParams->yLabelSpace[0];
    	plotParams->numYWords = 1;

    	for (;;)
    	{
	    if ( (tmpString = strtok(0, " ")) == 0)
	    	break;

	    plotParams->yLabelSpace[plotParams->numYWords] = 
		plotParams->yLabelSpace[plotParams->numYWords-1] +
		strlen(tmpString) + 1;
	    largestYLabel = MAX(largestYLabel, strlen(tmpString) + 1);
	    plotParams->numYWords++;
    	}
    }
    else 
    {
    	plotParams->numYWords = 0;
	largestYLabel = 0;
    }

    largestYLabel = MAX(largestYLabel, plotParams->firstTickLabelLength[Y]);
    largestYLabel = MAX(largestYLabel, plotParams->lastTickLabelLength[Y]);

    plotParams->displayData.xAxisStart = AXES_OFFSET3 +
					 largestYLabel * OFFSET_PER_CHAR;

    if (configParams.items[Y] == SCAN_NUMBER)
    {
    	plotStart = configParams.yVariable.minValue;
	plotEnd = plotStart;
    	plotSpan = configParams.yVariable.maxValue -
		   configParams.yVariable.minValue;
    	plotMax = configParams.yVariable.minValue + plotSpan;
    }
    else if (configParams.items[Y] == TIME)
    {
	if (configParams.yVariable.timeUnits == SECONDS)
	{
	    minInSecs = configParams.yVariable.minValue;
	    maxInSecs = configParams.yVariable.maxValue;
	}
	else if (configParams.yVariable.timeUnits == MINUTES)
	{
	    minInSecs = configParams.yVariable.minValue *
			(Flt64) SECONDS_PER_MINUTE;
	    maxInSecs = configParams.yVariable.maxValue *
			(Flt64) SECONDS_PER_MINUTE;
	}
	else	/* configParams.yVariable.timeUnits == HOURS */
	{
	    minInSecs = configParams.yVariable.minValue *
			(Flt64) SECONDS_PER_HOUR;
	    maxInSecs = configParams.yVariable.maxValue *
			(Flt64) SECONDS_PER_HOUR;
	}
    	plotStart = -1;
	plotEnd = plotStart;
    	plotSpan = (maxInSecs - minInSecs) * DATA_UPDATE_RATE;
    	plotMax = plotStart;
    }
    else
    {
    	plotStart = -1;
	plotEnd = plotStart;
    	plotSpan = 0;
    	plotMax = plotEnd;
    }

    plotParams->numMarks = 0;
    plotParams->inDisplayWindow = FALSE;

    initAxesGeom(plotParams);

} /* initGraphics */


/******************************************************************************/
/* Function : setXParams                                                      */
/* Purpose  : Sets the x item plotting parameters.                            */
/* Inputs   : X variable, display, colour map, foreground pixel colour and    */
/*            plotting data.                                                  */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
setXParams(varNum x, Display *display, Colormap *colourMap, Pixel *foreground,
	   plotParameters *plotParams)
{
    XColor colour, ignore;

    if (XAllocNamedColor(display, *colourMap,
	configParams.xVariables[x].colourName, &colour, &ignore))
    {
	plotParams->displayData.colours[x] = colour.pixel;
	plotParams->xColours[x] = configParams.xVariables[x].colour;
    }
    else
    {
	plotParams->displayData.colours[x] = *foreground;
	plotParams->xColours[x] = WHITE;
    }

    strcpy(plotParams->axisLabel[x], configParams.xVariables[x].axisLabel);
    plotParams->labelLength[x] = strlen(configParams.xVariables[x].axisLabel);

    plotParams->majorDivisions[x] = configParams.xVariables[x].majorDivisions;
    plotParams->numTicks[x] = configParams.xVariables[x].majorDivisions + 1;

    plotParams->displayData.minValues[x] = configParams.xVariables[x].minValue;
    plotParams->displayData.maxValues[x] = configParams.xVariables[x].maxValue;

    makeTickLabel(x, plotParams);

} /* setXParams */


/******************************************************************************/
/* Function : makeTickLabel                                                   */
/* Purpose  : Makes a tick label.                                             */
/* Inputs   : X variable and plotting data.                                   */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
makeTickLabel(varNum var, plotParameters *plotParams)
{
    char   label[20];
    Int16  i;
    Int16  lenLabel, lastNonZero;
    Int16  pointPosition1, pointPosition2;
    Int16  sigDigits1, sigDigits2;
    Int16  fieldLength1, fieldLength2, sigDigits;

    sprintf(label, "%f", plotParams->displayData.minValues[var]);
    lenLabel = strlen(label);
    for (i = 1; i < lenLabel; i++)
    {
	if (memchr(label, '.', i) != NULL)
	{
	    pointPosition1 = i;
	    break;
	}
    }
    for (i = 0; i < lenLabel; i++)
    {
	if (memchr(label+i, '0', 1) == NULL)
	    lastNonZero = i + 1;
    }
    sigDigits1 = lastNonZero - pointPosition1;

    sprintf(label, "%f", plotParams->displayData.maxValues[var]);
    lenLabel = strlen(label);
    for (i = 1; i < lenLabel; i++)
    {
	if (memchr(label, '.', i) != NULL)
	{
	    pointPosition2 = i;
	    break;
	}
    }
    for (i = 0; i < lenLabel; i++)
    {
	if (memchr(label+i, '0', 1) == NULL)
	    lastNonZero = i + 1;
    }
    sigDigits2 = lastNonZero - pointPosition2;

    sigDigits = MAX(sigDigits1, sigDigits2);
    if (var == Y)
    	if ((configParams.items[Y] != SCAN_NUMBER) && plotScrolling)
	{
	    sigDigits = 2;
	    if ((configParams.items[Y] == TIME) &&
		(configParams.yVariable.timeUnits == HOURS))
	        sigDigits = 3;
	}

    if (sigDigits == 0)
    {
        fieldLength1 = pointPosition1 - 1;
        fieldLength2 = pointPosition2 - 1;
    }
    else
    {
        fieldLength1 = pointPosition1 + sigDigits;
        fieldLength2 = pointPosition2 + sigDigits;
    }

    if (fieldLength1 > TICK_LABEL_LENGTH)
    	plotParams->firstTickLabelLength[var] = TICK_LABEL_LENGTH;
    else
    	plotParams->firstTickLabelLength[var] = fieldLength1;
    if (fieldLength2 > TICK_LABEL_LENGTH)
    	plotParams->lastTickLabelLength[var] = TICK_LABEL_LENGTH;
    else
    	plotParams->lastTickLabelLength[var] = fieldLength2;

    sprintf(plotParams->firstTickLabel[var], "%*.*f", fieldLength1, sigDigits,
	    plotParams->displayData.minValues[var]);
    sprintf(plotParams->lastTickLabel[var], "%*.*f", fieldLength2, sigDigits,
	    plotParams->displayData.maxValues[var]);

} /* makeTickLabel */


/******************************************************************************/
/* Function : initAxesGeom                                                    */
/* Purpose  : Initializes the display axes geometry.                          */
/* Inputs   : Plotting data.                                                  */
/* Outputs  : None.                                                           */
/******************************************************************************/
    void
initAxesGeom(plotParameters *plotParams)
{
    Dimension width, height;			/* X window dimensions        */
    Int16     var, tick;			/* loop counters              */
    Int16     dx[NUM_X_VARS];			/* x axis tick spacing        */
    Int16     dy;				/* y axis tick spacing        */

    XtVaGetValues(plotParams->displayData.display, XmNwidth, &width, XmNheight,
		  &height, NULL);

    plotParams->xAxisLength = width - plotParams->displayData.xAxisStart -
			      AXES_OFFSET1;
    plotParams->displayData.xAxisEnd = plotParams->displayData.xAxisStart +
				       plotParams->xAxisLength;

    plotParams->segments[LEFT].x1 = plotParams->displayData.xAxisStart;
    plotParams->segments[LEFT].x2 = plotParams->displayData.xAxisStart;
    plotParams->segments[RIGHT].x1 = plotParams->displayData.xAxisEnd;
    plotParams->segments[RIGHT].x2 = plotParams->displayData.xAxisEnd;
    plotParams->segments[TOP].x1 = plotParams->displayData.xAxisStart;
    plotParams->segments[TOP].x2 = plotParams->displayData.xAxisEnd;
    plotParams->segments[BOTTOM].x1 = plotParams->displayData.xAxisStart;
    plotParams->segments[BOTTOM].x2 = plotParams->displayData.xAxisEnd;

    plotParams->xLabelPosition[X1] = plotParams->displayData.xAxisStart +
				     AXES_OFFSET2;
    plotParams->xLabelPosition[X2] = plotParams->displayData.xAxisStart +
				     AXES_OFFSET2;
    plotParams->xLabelPosition[X3] = plotParams->displayData.xAxisStart +
				     AXES_OFFSET2;
    plotParams->xLabelPosition[X4] = plotParams->displayData.xAxisStart +
				     AXES_OFFSET2;
    plotParams->xLabelPosition[Y] = AXES_OFFSET3;

    switch(configParams.numXVariables)
    {
	case 3:
	    plotParams->numSegments = 5; 
    	    plotParams->displayData.yAxisStart = AXES_OFFSET2;
	    plotParams->yAxisLength = height - AXES_OFFSET2 - AXES_OFFSET2; 
    	    plotParams->segments[TOP2].x1 = plotParams->displayData.xAxisStart;
    	    plotParams->segments[TOP2].y1 = AXES_OFFSET1;
    	    plotParams->segments[TOP2].x2 = plotParams->displayData.xAxisEnd;
    	    plotParams->segments[TOP2].y2 = AXES_OFFSET1;
	    break;

	case 4:
	    plotParams->numSegments = 6; 
    	    plotParams->displayData.yAxisStart = AXES_OFFSET2;
	    plotParams->yAxisLength = height - AXES_OFFSET1 - 2 * AXES_OFFSET2; 
    	    plotParams->segments[TOP2].x1 = plotParams->displayData.xAxisStart;
    	    plotParams->segments[TOP2].y1 = AXES_OFFSET1;
    	    plotParams->segments[TOP2].x2 = plotParams->displayData.xAxisEnd;
    	    plotParams->segments[TOP2].y2 = AXES_OFFSET1;
    	    plotParams->segments[BOTTOM2].x1 =
		plotParams->displayData.xAxisStart;
    	    plotParams->segments[BOTTOM2].y1 = height - AXES_OFFSET2;
    	    plotParams->segments[BOTTOM2].x2 = plotParams->displayData.xAxisEnd;
    	    plotParams->segments[BOTTOM2].y2 = height - AXES_OFFSET2;
	    break;

	default :
	    plotParams->numSegments = 4; 
    	    plotParams->displayData.yAxisStart = AXES_OFFSET1;
	    plotParams->yAxisLength = height - AXES_OFFSET1 - AXES_OFFSET2; 
    }

    plotParams->displayData.yAxisEnd = plotParams->displayData.yAxisStart +
				       plotParams->yAxisLength;
    plotParams->segments[LEFT].y1 = plotParams->displayData.yAxisStart;
    plotParams->segments[LEFT].y2 = plotParams->displayData.yAxisEnd;
    plotParams->segments[RIGHT].y1 = plotParams->displayData.yAxisStart;
    plotParams->segments[RIGHT].y2 = plotParams->displayData.yAxisEnd;
    plotParams->segments[TOP].y1 = plotParams->displayData.yAxisStart;
    plotParams->segments[TOP].y2 = plotParams->displayData.yAxisStart;
    plotParams->segments[BOTTOM].y1 = plotParams->displayData.yAxisEnd;
    plotParams->segments[BOTTOM].y2 = plotParams->displayData.yAxisEnd;

    plotParams->yLabelPosition[X1] = plotParams->segments[TOP].y1 -
                                     AXES_OFFSET3;
    plotParams->yLabelPosition[X2] = plotParams->segments[BOTTOM].y1 +
                                     AXES_OFFSET4;
    plotParams->yLabelPosition[X3] = plotParams->segments[TOP2].y1 -
                                     AXES_OFFSET3;
    plotParams->yLabelPosition[X4] = plotParams->segments[BOTTOM2].y1 +
                                     AXES_OFFSET4;
    plotParams->yLabelPosition[Y] = height / 2;

    plotParams->tickLength = (height + width) / 200;
    for (var = 0; var < configParams.numXVariables; var++)
    {
	dx[var] = plotParams->xAxisLength / (plotParams->numTicks[var] - 1);
	for (tick = 0; tick < plotParams->numTicks[var]; tick++)
	{
	    plotParams->tickMarks[var][tick].x1 =
	    plotParams->tickMarks[var][tick].x2 =
	        plotParams->displayData.xAxisStart + tick * dx[var];
	    plotParams->tickMarks[var][tick].y1 =
	        plotParams->segments[var+2].y1;
	    plotParams->tickMarks[var][tick].y2 = (odd(var)) ?
	        plotParams->segments[var+2].y1 - plotParams->tickLength :
	        plotParams->segments[var+2].y1 + plotParams->tickLength;
	}
    	plotParams->tickMarks[var][plotParams->numTicks[var]-1].x1 =
	plotParams->tickMarks[var][plotParams->numTicks[var]-1].x2 =
	    plotParams->displayData.xAxisEnd;
    }

    dy = plotParams->yAxisLength / (plotParams->numTicks[Y] + 1);
    for (tick = 0; tick < plotParams->numTicks[Y]; tick++)
    {
        plotParams->tickMarks[Y][tick].x1 = plotParams->displayData.xAxisStart;
        plotParams->tickMarks[Y][tick].x2 = plotParams->displayData.xAxisStart +
					    plotParams->tickLength;
        plotParams->tickMarks[Y][tick].y1 =
        plotParams->tickMarks[Y][tick].y2 = plotParams->displayData.yAxisStart +
					    (tick + 1) * dy;
    }

    for (var = 0; var < configParams.numXVariables; var++)
    {
    	plotParams->xFirstTickLabelPos[var] =
	    plotParams->displayData.xAxisStart + AXES_OFFSET3;
    	plotParams->xLastTickLabelPos[var] = plotParams->displayData.xAxisEnd -
	    plotParams->lastTickLabelLength[var] * OFFSET_PER_CHAR;
    }
    plotParams->yFirstTickLabelPos[X1] = plotParams->yLastTickLabelPos[X1]
				       = plotParams->tickMarks[X1][0].y1 -
					 AXES_OFFSET3;
    plotParams->yFirstTickLabelPos[X2] = plotParams->yLastTickLabelPos[X2]
				       = plotParams->tickMarks[X2][0].y1 +
					 AXES_OFFSET4;
    plotParams->yFirstTickLabelPos[X3] = plotParams->yLastTickLabelPos[X3]
				       = plotParams->tickMarks[X3][0].y1 -
					 AXES_OFFSET3;
    plotParams->yFirstTickLabelPos[X4] = plotParams->yLastTickLabelPos[X4]
				       = plotParams->tickMarks[X4][0].y1 +
					 AXES_OFFSET4;
    plotParams->xFirstTickLabelPos[Y] = plotParams->displayData.xAxisStart -
				        plotParams->firstTickLabelLength[Y] *
					OFFSET_PER_CHAR;
    plotParams->xLastTickLabelPos[Y] = plotParams->displayData.xAxisStart -
				       plotParams->lastTickLabelLength[Y] *
				       OFFSET_PER_CHAR;
    plotParams->yFirstTickLabelPos[Y] = plotParams->displayData.yAxisStart +
					AXES_OFFSET4;
    plotParams->yLastTickLabelPos[Y] = plotParams->displayData.yAxisEnd -
				       AXES_OFFSET3;

    if (configParams.yVariable.timeUnits == SECONDS)
        yTimeUnitsScale = 1.0;			/* seconds per second */
    else if (configParams.yVariable.timeUnits == MINUTES)
        yTimeUnitsScale = SECONDS_PER_MINUTE;
    else if (configParams.yVariable.timeUnits == HOURS)
        yTimeUnitsScale = SECONDS_PER_HOUR;

    if (configParams.items[Y] == TIME)
    {
    	plotParams->displayData.scales[Y] = plotParams->yAxisLength /
            ( (plotParams->displayData.maxValues[Y] -
	       plotParams->displayData.minValues[Y]) *
	       yTimeUnitsScale * DATA_UPDATE_RATE );
    }
    else
    	plotParams->displayData.scales[Y] = plotParams->yAxisLength /
            (plotParams->displayData.maxValues[Y] -
	     plotParams->displayData.minValues[Y]);
    for (var = 0; var < configParams.numXVariables; var++)
    	plotParams->displayData.scales[var] = plotParams->xAxisLength /
	(plotParams->displayData.maxValues[var] -
	 plotParams->displayData.minValues[var]);

    plotParams->displayData.xStatusLinePos = plotParams->displayData.xAxisStart;
    plotParams->displayData.yStatusLinePos = height - AXES_OFFSET4;

} /* initAxesGeom */


/******************************************************************************/
/* Function : odd                                                             */
/* Purpose  : Determines if an integer number is odd.                         */
/* Inputs   : Number.                                                         */
/* Outputs  : Returns 1 if integer is odd, 0 otherwise.                       */
/******************************************************************************/
    Int16
odd(Int16 n)
{

    return(n&1);

}  /* odd */

