/*  ScianScales.c
 *  scale objects for sliders and other controls
 * 
 *  Jim Lyons
 *  2/3/93
 *
 *  "Rhapsody on a Theme of Pepke"
 */
 
 
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianFontSystem.h"
#include "ScianArrays.h"
#include "ScianStyle.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianHelp.h"
#include "ScianErrors.h"
#include "ScianControls.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianEvents.h"
#include "ScianScripts.h"
#include "ScianMethods.h"
#include "ScianDraw.h"
#include "ScianSliders.h"
#include "ScianTextBoxes.h"
#include "ScianSymbols.h"
#include "ScianScales.h"

/* static method declarations */
static ObjPtr DrawScale();
static ObjPtr PressScale();
static ObjPtr SetVal();
static ObjPtr MakeHelpString();

ObjPtr scaleClass;

void InitScales()
{
    scaleClass = NewObject(controlClass, 0);
    AddToReferenceList(scaleClass);
    
    SetVar(scaleClass, NAME, NewString("Scale"));
    SetVar(scaleClass, TEXTFONT, NewString(SCALEFONT));
    SetVar(scaleClass, TEXTSIZE, NewInt(SCALEFONTSIZE));
    SetVar(scaleClass, TEXTCOLOR, NewInt(SCALETEXTCOLOR));
    SetVar(scaleClass, COLOR, NewInt(SCALELINECOLOR));
    SetVar(scaleClass, LINEWIDTH, NewInt(SCALELINEWIDTH));
    SetVar(scaleClass, FORMAT, NewString(SCALEFORMAT));
    SetVar(scaleClass, BACKGROUND, NewInt(UIBACKGROUND));
    SetVar(scaleClass, TYPESTRING, NewString("scale"));
    
    SetMethod(scaleClass, DRAW, DrawScale);
    SetMethod(scaleClass, PRESS, PressScale);
    SetMethod(scaleClass, SETVAL, SetVal);
    SetMethod(scaleClass, MAKE1HELPSTRING, MakeHelpString);
}

void KillScales()
{
    DeleteThing(scaleClass);
}

#ifdef PROTO
ObjPtr NewScale(int left, int right, int bottom, int top, int orientation,
	Bool adj, char *name)
#else
ObjPtr NewScale(left, right, bottom, top, orientation, adj, name)
int left, right, bottom, top, orientation;
Bool adj;
char *name;
#endif
{
    ObjPtr newScale;
    
    newScale = NewObject(scaleClass, 0);
    Set2DIntBounds(newScale, left, right, bottom, top);

    SetVar(newScale, NAME, NewString(name));
    SetVar(newScale, HIGHLIGHTED, ObjFalse);
    SetVar(newScale, ACTIVATED, adj ? ObjTrue : ObjFalse);
    SetVar(newScale, ORIENTATION, NewInt(orientation));
    switch (orientation)
    {
	case SO_TOP:
	case SO_BOTTOM:
	    SetVar(newScale, ALIGNMENT, NewInt(CENTERALIGN));
	    SetVar(newScale, STEPPIXELS, NewInt(HSTEPPIXELS));
	    break;
	    
	case SO_LEFT:
	    SetVar(newScale, ALIGNMENT, NewInt(RIGHTALIGN));
	    SetVar(newScale, STEPPIXELS, NewInt(VSTEPPIXELS));
	    break;
	    
	case SO_RIGHT:
	    SetVar(newScale, ALIGNMENT, NewInt(LEFTALIGN));
	    SetVar(newScale, STEPPIXELS, NewInt(VSTEPPIXELS));
	    break;
    }
    return newScale;
}

#ifdef PROTO
void SetScaleRange(ObjPtr scale, real minVal, real maxVal)
#else
void SetScaleRange(scale, minVal, maxVal)
ObjPtr scale;
real minVal, maxVal;
#endif
{
    ObjPtr valArray;
    real *p;
    int numDiv, left, right, bottom, top, length, orientation, stepPixels;
    double majStep;
    
    Get2DIntBounds(scale, &left, &right, &bottom, &top);
    orientation = GetInt(GetVar(scale, ORIENTATION));
    stepPixels = GetInt(GetVar(scale, STEPPIXELS));

    if (orientation == SO_LEFT || orientation == SO_RIGHT) /* vertical scale */
    {
	length = top - bottom - 2*VSCALEINSET;
    }
    else /* horizontal scale */
    {
	length = right - left - 2*HSCALEINSET;
    }
    CalcGoodSteps((double) (maxVal - minVal), length, stepPixels, &majStep, &numDiv);
    valArray = NewRealArray(1, 4L);
    p = ELEMENTS(valArray);
    p[0] = minVal;
    p[1] = maxVal;
    p[2] = majStep;
    p[3] = (real) numDiv;
    
    SetVar(scale, VALUE, valArray);
    ImInvalid(scale);
    return;
}

#ifdef PROTO
void SetScaleFormat(ObjPtr scale, char *format)
#else
void SetScaleFormat(scale, format)
ObjPtr scale;
char *format;
#endif
{
    SetVar(scale, FORMAT, NewString(format));
    ImInvalid(scale);
    return;
}

#ifdef PROTO
void SetScaleStepPixels(ObjPtr scale, int stepPixels)
#else
void SetScaleStepPixels(ObjPtr scale, int stepPixels)
ObjPtr scale;
int stepPixels;
#endif
{
    ObjPtr valArray;

    SetVar(scale, STEPPIXELS, NewInt(stepPixels));
    
    if (valArray = GetVar(scale, VALUE)) /* recompute scale parameters */
    {
        real *p;

	p = ELEMENTS(valArray);
	SetScaleRange(scale, p[0], p[1]);
    }   
    return;
    
}

#ifdef PROTO
void LinkScale(ObjPtr scale, ObjPtr control)
#else
void LinkScale(scale, control)
ObjPtr scale, control;
#endif
/* sets the REPOBJ of the scale to control and THESCALE of control to scale */
{
    SetVar(scale, REPOBJ, control);
    SetVar(control, THESCALE, scale);
    return;
}

#ifdef PROTO
static ObjPtr MakeHelpString(ObjPtr scale,  ObjPtr class)
#else
static ObjPtr MakeHelpString(scale)
ObjPtr scale,  class;
#endif
{
    if (IsTrue(GetVar(scale, ACTIVATED)))
    {
	/* scale is adjustable */
	SetVar(class, HELPSTRING, NewString("\
This is an adjustable scale. This means that it may be adjusted. Unfortunately, \
there is no way to adjust it yet. So, technically, it is not really adjustable."));
    }
    else SetVar(class, HELPSTRING, NULLOBJ);
    return scale;
}

#ifdef PROTO
static ObjPtr SetVal(ObjPtr scale, ObjPtr valArray)
#else
static ObjPtr SetVal(scale, valArray)
ObjPtr scale;
ObjPtr valArray;
#endif
{
    if (IsRealArray(valArray) && RANK(valArray) == 1 && DIMS(valArray)[0] == 4)
	SetVar(scale, VALUE, valArray);
    else ReportError("Scale SETVAL", "Bad value array");
    return scale;
}

#ifdef PROTO
static ObjPtr DrawScale(ObjPtr scale)
#else
static ObjPtr DrawScale(scale)
ObjPtr scale;
#endif
{
#ifdef GRAPHICS
    int left, right, bottom, top, length, orientation;
    char *format, *textfont;
    int k, numDiv, hilite, alignment;
    int textsize, bgcolor, textcolor, linecolor, linewidth;
    ObjPtr valArray;
    real *p;
    double epsilon, curVal, minVal, maxVal, range, bigStep, lilStep;
    int curPt;

    Get2DIntBounds(scale, &left, &right, &bottom, &top);
    if (IsDrawingRestricted(left, right, bottom, top)) return NULLOBJ;
    
    right -= 1;
    top -= 1;

    orientation = GetInt(GetVar(scale, ORIENTATION));
    format = GetString(GetVar(scale, FORMAT));
    hilite = GetInt(GetVar(scale, HIGHLIGHTED));
    alignment = GetInt(GetVar(scale, ALIGNMENT));

    textfont = GetString(GetVar(scale, TEXTFONT));
    textsize = GetInt(GetVar(scale, TEXTSIZE));
    SetupFont(textfont, textsize);

    textcolor = GetInt(GetVar(scale, TEXTCOLOR));
    linecolor = GetInt(GetVar(scale, COLOR));

    linewidth = GetInt(GetVar(scale, LINEWIDTH));
    SetLineWidth(linewidth);

    valArray = GetVar(scale, VALUE);
    p = ELEMENTS(valArray);
    minVal = p[0];
    maxVal = p[1];
    bigStep = p[2];
    numDiv = (int) p[3];
    lilStep = bigStep/numDiv;
    length = (orientation == SO_RIGHT || orientation == SO_LEFT) ? 
	    top - bottom - 2* VSCALEINSET : right - left - 2*HSCALEINSET;
    range = maxVal - minVal;
    
    /* set curVal to the first big tic on or below the scale */
    curVal = ((long) (minVal/bigStep))*bigStep;
    while (curVal > minVal) curVal -= bigStep;
    
    /* now find the first tic on the scale */
    k = 0;
    while (curVal < minVal)
    {
	++k;
	if (k >= numDiv) k = 0;
	curVal += lilStep;
    }
    epsilon = range*1.0E-6;
    if (ABS(curVal) < epsilon) curVal = 0.0;
    
    /* draw scale according to orientation */
    switch (orientation)
    {
	case SO_LEFT:
	    DrawUILine(right, bottom + VSCALEINSET, right, top - VSCALEINSET, linecolor);
	    while (curVal <= maxVal + epsilon)
	    {
		curPt = bottom + VSCALEINSET + (curVal - minVal)*length/range + .5;
		if (k == 0)
		{
		    /* big tic and number */
		    DrawUILine(right, curPt, right - BIGTIC, curPt, linecolor);
		    sprintf(tempStr, format, curVal);
		    DrawAString(alignment, right - BIGTIC - 2, curPt - textsize/2, tempStr); 
		}
		else
		{
		    /* lil tic */
		    DrawUILine(right, curPt, right - LILTIC, curPt, linecolor);
		}
		curVal += lilStep;
		if (ABS(curVal) < epsilon) curVal = 0.0;
		++k;
		if (k >= numDiv) k = 0;
	    }
	    break;
	    
	case SO_RIGHT:
	    DrawUILine(left, bottom + VSCALEINSET, left, top - VSCALEINSET, linecolor);
	    while (curVal <= maxVal + epsilon)
	    {
		curPt = bottom + VSCALEINSET + (curVal - minVal)*length/range + .5;
		if (k == 0)
		{
		    /* big tic and number */
		    DrawUILine(left, curPt, left + BIGTIC, curPt, linecolor);
		    sprintf(tempStr, format, curVal);
		    DrawAString(alignment, left + BIGTIC + 2, curPt - textsize/2, tempStr); 
		}
		else
		{
		    /* lil tic */
		    DrawUILine(left, curPt, left + LILTIC, curPt, linecolor);
		}
		curVal += lilStep;
		if (ABS(curVal) < epsilon) curVal = 0.0;
		++k;
		if (k >= numDiv) k = 0;
	    }
	    break;
	    
	case SO_BOTTOM:
	    DrawUILine(left + HSCALEINSET, top, right - HSCALEINSET, top, linecolor);
	    while (curVal <= maxVal + epsilon)
	    {
		curPt = left + HSCALEINSET + (curVal - minVal)*length/range + .5;
		if (k == 0)
		{
		    /* big tic and number */
		    DrawUILine(curPt, top, curPt, top - BIGTIC, linecolor);
		    sprintf(tempStr, format, curVal);
		    DrawAString(alignment, curPt, top - textsize - BIGTIC - 2, tempStr); 
		}
		else
		{
		    /* lil tic */
		    DrawUILine(curPt, top, curPt, top - LILTIC, linecolor);
		}
		curVal += lilStep;
		if (ABS(curVal) < epsilon) curVal = 0.0;
		++k;
		if (k >= numDiv) k = 0;
	    }
	    break;
	    
	case SO_TOP:
	    DrawUILine(left + HSCALEINSET, bottom, right - HSCALEINSET, bottom, linecolor);
	    while (curVal <= maxVal + epsilon)
	    {
		curPt = left + HSCALEINSET + (curVal - minVal)*length/range + .5;
		if (k == 0)
		{
		    /* big tic and number */
		    DrawUILine(curPt, bottom, curPt, bottom + BIGTIC, linecolor);
		    sprintf(tempStr, format, curVal);
		    DrawAString(alignment, curPt, bottom + BIGTIC + 2, tempStr); 
		}
		else
		{
		    /* lil tic */
		    DrawUILine(curPt, bottom, curPt, bottom + LILTIC, linecolor);
		}
		curVal += lilStep;
		if (ABS(curVal) < epsilon) curVal = 0.0;
		++k;
		if (k >= numDiv) k = 0;
	    }
	    break;
    }
    SetLineWidth(1);
#endif
    return ObjTrue;
}

#ifdef PROTO
static ObjPtr PressScale(ObjPtr scale, int mouseX, int mouseY, int flags)
#else
static ObjPtr PressScale(scale, mouseX, mouseY, flags)
ObjPtr scale;
int mouseX, mouseY;
long flags;
#endif
{
#ifdef INTERACTIVE
    int left, right, bottom, top;

    Get2DIntBounds(scale, &left, &right, &bottom, &top);

    /* return if mouse outside slider rectangle */
    if (mouseX < left - 1 || mouseX > right + 1 || mouseY < bottom -1
		    || mouseY > top + 1) return ObjFalse;
		    
    if (TOOL(flags) == T_HELP)
    {
	ContextHelp(scale);
	ContextHelp(GetVar(scale, REPOBJ));
	return ObjTrue;
    }

    /* return if not active */
    if (!GetPredicate(scale, ACTIVATED)) return ObjTrue;
    
    /*** adjust the scale! ***/

#endif    
    return ObjTrue;
}

