/* Generated by Interface Builder */
/* NeXTSTEP interface by Erik Sowa (sowa@netcom.com) */
/*  with help from Garance Alistair Drosehn (gad@eclipse.its.rpi.edu) */

#import <time.h>
#import <defaults.h>
#import <stdlib.h>
#import <strings.h>
#import <appkit/nextstd.h>
#import <appkit/Application.h>		/* for loading nibs, etc. */
#import <appkit/Panel.h>		/* for Info Panel stuff */
#import <appkit/Form.h> 		/* computeSCB89Debt writes to a
					   FormCell */
#import <appkit/TextFieldCell.h>	/* for Preference Panel */
#import <dpsclient/dpsclient.h>		/* for timed entry stuff */
#import "Controller.h"

/* declarations for xdebt-xcerpt */
typedef double debt_t, pop_t;
extern debt_t national_scb89debt_at();
extern debt_t national_aj92debt_at();
extern pop_t national_pop_at();
extern void debt_to_string();

#define MINUPDATEINTERVAL 1.0 	/* Do not update more than once a second. */
#define MAXUPDATEINTERVAL 999.9	/* This limit is arbitrary. String sizes
				   in setUpdateInterval depend on it. */

@implementation Controller

char buf[255];
void strtoRect( char *, NXRect * );

- appDidInit:sender
{
 NXDefaultsVector defaults =
    {
    {"updateInterval","1.0"},		/* seconds between auto updates */
    {"autoUpdate","1"},			/* 1=enabled, 0=disabled */
    {"DebtWindowFrame",NULL},		/* location of Debt window */
    {"PrefPanelFrame",NULL},		/* location of Preferences panel */
    {NULL,NULL}
    };
    NXRect	tframe;

    /* Initialize DebtField at startup */
    [self computeAJ92Debt:self];

    /* register and obtain valid app defaults */
    NXRegisterDefaults([NXApp appName],defaults);
    if ( NXGetDefaultValue([NXApp appName],"updateInterval") )
	{
	updateInterval =
		atof(NXGetDefaultValue([NXApp appName],"updateInterval"));
        updateInterval = MAX(updateInterval,MINUPDATEINTERVAL);
        updateInterval = MIN(updateInterval,MAXUPDATEINTERVAL);
	/* make sure pref panel reflects interval value.
	   there must be a better way than making two calls! */
	[updateIntervalPrefField setDoubleValue:updateInterval];
	[updateIntervalPrefSlider setDoubleValue:updateInterval];
        }
    if ( NXGetDefaultValue([NXApp appName],"autoUpdate") )
        {
        autoUpdate = atoi(NXGetDefaultValue([NXApp appName],"autoUpdate"));
	if (autoUpdate != 0)
	    autoUpdate = 1;
    	/* make sure pref panel reflects auto update state */
    	[autoUpdatePref setState:autoUpdate];
        }
    if ( NXGetDefaultValue([NXApp appName],"DebtWindowFrame") )
        {
        strcpy(buf, NXGetDefaultValue([NXApp appName],"DebtWindowFrame"));
	strtoRect(buf, &tframe);
	[debtWindow moveTo:tframe.origin.x:tframe.origin.y];
        }
    if ( NXGetDefaultValue([NXApp appName],"PrefPanelFrame") )
        {
	strcpy(buf, NXGetDefaultValue([NXApp appName],"PrefPanelFrame"));
	strtoRect(buf, &tframe);
	[prefPanel moveTo:tframe.origin.x:tframe.origin.y];
        }

   /* bring main window to front and make it key window.
      among other things, this makes it visible (now that we've
      processed the defaults setting for where it should appear) */
    [debtWindow makeKeyAndOrderFront:self];
    
    /* register timed entry with window server */
    updateEntry = DPSAddTimedEntry(updateInterval,(void *)_update,
        self,NX_BASETHRESHOLD);

    return self;
}

- appWillTerminate:sender
{
    if ( updateEntry )
        {
        DPSRemoveTimedEntry(updateEntry);
        updateEntry = (DPSTimedEntry)0;
        }
    return self;
}

- setUpdateInterval:sender
{
    /* get interval from pref panel field or slider and validate */
    updateInterval = [sender doubleValue]; /* from Pref field or slider */
    updateInterval = MAX(updateInterval,MINUPDATEINTERVAL);
    updateInterval = MIN(updateInterval,MAXUPDATEINTERVAL);
    sprintf(buf,"%.1f",updateInterval);
    /* %.1f restricts precision, reread it for consistency */
    updateInterval = atof(buf);
    NXWriteDefault([NXApp appName],"updateInterval",buf);

    /* make sure pref panel reflects interval value.
       needed because there are two ways to set it.
       there must be a better way than making two calls! */
    [updateIntervalPrefField setStringValue:buf];
    [updateIntervalPrefSlider setStringValue:buf];
    
    /* set up the timed entry */
    if (updateEntry)
        {
        DPSRemoveTimedEntry(updateEntry);
        updateEntry = (DPSTimedEntry)0;
        }
    if (autoUpdate)
        {
        updateEntry = DPSAddTimedEntry(updateInterval,(void *)_update,
	    self,NX_BASETHRESHOLD);
        }

    return self;
}

- setAutoUpdate:sender
{
    /* get value from toggle switch button */
    autoUpdate = [sender state]; /* check box state (0 or 1) */
    sprintf(buf,"%d",autoUpdate);
    NXWriteDefault([NXApp appName],"autoUpdate",buf);

    /* set up the timed entry */
    if (updateEntry)
        {
        DPSRemoveTimedEntry(updateEntry);
        updateEntry = (DPSTimedEntry)0;
        }
    if (autoUpdate)
        {
        updateEntry = DPSAddTimedEntry(updateInterval,(void *)_update,
	    self,NX_BASETHRESHOLD);
        }
    
    return self;
}

- showInfoPanel:sender
{
    if ( !infoPanel )
        [NXApp loadNibSection:"InfoPanel.nib" owner:self];
    [infoPanel makeKeyAndOrderFront:self];
    return self;
}

- setWindowLocation:sender
{
    NXRect debtFrame, prefFrame;
    
    [debtWindow getFrame:&debtFrame];
    sprintf(buf, "x%1.0f y%1.0f w%1.0f h%1.0f",
    	debtFrame.origin.x, debtFrame.origin.y,
    	debtFrame.size.width, debtFrame.size.height);
    NXWriteDefault([NXApp appName], "DebtWindowFrame", buf);

    [prefPanel getFrame:&prefFrame];
    sprintf(buf, "x%1.0f y%1.0f w%1.0f h%1.0f",
    	prefFrame.origin.x, prefFrame.origin.y,
    	prefFrame.size.width, prefFrame.size.height);
    NXWriteDefault([NXApp appName], "PrefPanelFrame", buf);

    return self;
}

- computeSCB89Debt:sender
{
    time_t now;
    debt_t debt;
    pop_t pop;
    
    now = time ((time_t*) 0);
    debt = national_scb89debt_at(now);
    pop = national_pop_at(now);

    debt_to_string (debt, buf);
    [SCB89DebtString setStringValue:buf];
    debt_to_string (debt/pop,buf);
    [SCB89ShareString setStringValue:buf];
    return self;
}

- computeAJ92Debt:sender
{
    time_t now;
    debt_t debt;
    pop_t pop;
    
    now = time ((time_t*) 0);
    debt = national_aj92debt_at(now);
    pop = national_pop_at(now);

    debt_to_string (debt, buf);
    [AJ92DebtString setStringValue:buf];
    debt_to_string (debt/pop,buf);
    [AJ92ShareString setStringValue:buf];
    return self;
}

@end

/* function to convert a character string (a defaults value) into
   a NXRect frame.  The string is pretty much assumed to be valid,
   though some minor amount of checking is done.  It accepts two
   formats, the one used by NXFontPanelFrame (eg: "68 104 322 329")
   and the one used by Cassandra (eg: "x68y104w322h329")
*/
void strtoRect(char * inputString, NXRect * frame)
{
    char	*start, *next;
    
    frame->origin.y = frame->origin.y = 0.0;
    frame->size.width = frame->size.height = 0.0;
    
    start = inputString;
    if ( *start == ' ' ) start++;
    if ( *start == 'x' ) start++;
    if ( *start == '\0' ) return;
    frame->origin.x = strtod(start, &next);
    if ( start == next ) return;

    start = next;
    if ( *start == ' ' ) start++;
    if ( *start == 'y' ) start++;
    if ( *start == '\0' ) return;
    frame->origin.y = strtod(start, &next);
    if ( start == next ) return;
    
    start = next;
    if ( *start == ' ' ) start++;
    if ( *start == 'w' ) start++;
    if ( *start == '\0' ) return;
    frame->size.width = strtod(start, &next);
    if ( start == next ) return;
    
    start = next;
    if ( *start == ' ' ) start++;
    if ( *start == 'h' ) start++;
    if ( *start == '\0' ) return;
    frame->size.height = strtod(start, &next);
    if ( start == next ) return;
    
    return;    
}

/* function called by timed entry */
void _update(DPSTimedEntry teNumber,double now,id self)
{
    [self computeAJ92Debt:self];
    return;
}
