/* PFunction.m  by Paul Kunz	November 1992
 * Class to manage a plot function
 *
 * Copyright (C)  1992  The Board of Trustees of
 * The Leland Stanford Junior University. All Rights Reserved.
 */

#import "Plot.h"

#import "PFunction.h"
const char PFunction_h_rcsid[] = PFUNCTION_H_ID;
const char PFunction_m_rcsid[] = "$Id: PFunction.m,v 2.14.2.1 1994/02/08 20:30:05 rensing Exp $";

#import <objc/hashtable.h>
#import <appkit/nextstd.h>

#define	PARM_MAX	0
#define PARM_VALUE	1
#define PARM_MIN	2
#define PARM_STEP	3

#define BWG_VERSION 1
#define NONBWG_VERSION 2
#define TYPES_VERSION 3
#define ONETYPE_VERSION 4
#define LEAVEIN_VERSION 5
#define STORAGE_VERSION 6
#define EMPTYBIN_VERSION 7
#define CURRENT_VERSION EMPTYBIN_VERSION

@implementation PFunction

static void writeParms( NXTypedStream *ts, Storage *store )
{
    fitParm	*parm;
    int		i, count;
    
    count = [store count];
    NXWriteType( ts, "i", &count);
    for ( i = 0; i < count; i++ ) {
        parm = (fitParm *)[store elementAt:i];
	NXWriteType( ts, "{[7d]*cc}", parm );
    }
    return;
}
static Storage *readParms( NXTypedStream *ts )
{
    Storage	*store;
    fitParm	parm;
    int		i, count;
    
    store = [[Storage alloc] initCount:0 
			elementSize:sizeof(fitParm)
			description:"[7d]*cc"];
    NXReadType( ts, "i", &count);
    for ( i = 0; i < count; i++ ) {
        NXReadType( ts, "{[7d]*cc}", &parm );
	[store addElement:&parm];
    }
    return store;
}
/* Methods to initialize the function */
+ initialize
{
    [self setVersion:CURRENT_VERSION];
    return self;
}
- setTitle:(const char *)string
{
    title = NXCopyStringBuffer(string);
    return self;
}

- (const char *)title
{
    return title;
}

- setFunctionPtr:(void *)ptr
{
    fp = ptr;
    if ( hfunc ) {
        hfunc->funcPtr = ptr;
    }
    return self;
}

- (void *) functionPtr
{
    return fp;
}
- registerFunc
{
    h_func_reg( title, fp );
    return self;
}
- setNumberArgs:(int) value
{
    nargs = (value > 0) ? value : 0;
    if ( !nargs ) {
      return self;
    }
    if ( names ) {
      names = (char **) NXZoneRealloc( [self zone], names, 
                                       nargs*sizeof( char *) );
    } else {
      names = (char **) NXZoneMalloc( [self zone], 
				       nargs*sizeof( char*) );
      memset(names, 0, nargs*sizeof(char*) );  // protect against missing names
    }
    return self;
}

- (int)numberArgs
{
    return nargs;
}

- setArgName:(const char *)string at:(int) i
{
    if ( i >= 0 && i < nargs ) {
      names[i] = NXCopyStringBuffer( string );
    }
    return self;
}

- (const char *) argNameAt:(int) i
{
    if ( i < 0 || i >= nargs ) {
        return NULL;
    }
    return names[i];
}
- setFitType:(int *)ivalue
{
    fitType = *ivalue;
    return self;
}
- (int) fitType
{
    return fitType;
}

- setEbType:(int *)ivalue
{
    ebType = *ivalue;
    return self;
}
- (int) ebType
{
    return ebType;
}

- setFuncLineStyle:(int *)ivalue
{
    linestyle_t style;
    style = *ivalue;
    
    h_setPFuncLineStyle(hfunc, style);
    return self;
}
- (int) funcLineStyle
{
    return (int)h_getPFuncLineStyle(hfunc);
}
  
- resetAfterRead: aPlot
{
     int i;
     func_id func;
     
     if (hfunc_index < 0)
	  [self addToPlot: aPlot];
     else
     {
	  disp = [aPlot histDisplay];
	  if ( !disp ) return self;
	  
	  hfunc = NULL;
	  func=h_nextPlotFunc(disp,NULL);
	  for (i=0; func != NULL && hfunc == NULL; i++ )
	  {
	       if (i == hfunc_index) hfunc = func;
	       func = h_nextPlotFunc(disp, func);
	  }
	  parmblk = hfunc->paramBlk;
	  if (fitType < 0 ) fitType = 0;
     }

     [self resetParms];		/* put values in parmblk */
     
     return self;
}     

- addToPlot:aPlot
{   
    disp = [aPlot histDisplay];
    if ( !disp ) return self;
    
    NX_ZONEMALLOC( [aPlot zone], parmblk, double, nargs );
    hfunc = h_addPlotFunc( disp, title, parmblk, nargs, SOLID);
    h_setPlotFuncs( disp, YES);
    
    if (fitType < 0 ) fitType = 0;

    return self;
}
- removeFromPlot:(Plot *)aPlot
{    
    disp = [aPlot histDisplay];
    h_deletePlotFunc( disp, hfunc );
    NX_FREE( parmblk );
    return self;
}
- setInitialValues
{
    fitParm             parm;
    unsigned int	i;
    
    if ( !variedParms ) {
    	variedParms = [[Storage alloc] initCount:0 
		           elementSize:sizeof(fitParm)
	        	   description:"[7d]*cc"];
    }
    for ( i = 0; i < nargs; i++ ) {
        parm.value = 1.0;
	parm.lower_limit = 0.0;
	parm.upper_limit = 10.0; 
	parm.step_size = 0.1;
	parm.name = NXCopyStringBuffer(names[i]);
	parm.fixedValue = NO;
	parm.fixedLimits = NO;
	parm.error = 1.0;
	[variedParms addElement:&parm];
    }
    /* user routine to override values */
    [self initialValues];
    [startParms free];
    startParms = [variedParms copyFromZone:[self zone]];
    [fittedParms free];
    fittedParms = [variedParms copyFromZone:[self zone]];
    return self;
}

- initialValues
{
    return self;
}

- copyToVaried:(parm_t *)type
{
    Storage		*stor;
    
    switch (*type) {
    case INITIAL_VALUES:
    	stor = startParms;
	break;
    case FIT_VALUES:
        stor = fittedParms;
	break;
    default:
        stor = nil;
	break;
    }
    if ( stor == nil ) return self;
    
    [variedParms free];
    variedParms = [stor copyFromZone:[self zone]];
    return self;
}
  
- copyToFitted:(parm_t *)type
{
    Storage		*stor;
    
    switch (*type) {
    case INITIAL_VALUES:
    	stor = startParms;
	break;
    case PLAY_VALUES:
        stor = variedParms;
	break;
    default:
        stor = nil;
	break;
    }
    if ( stor == nil ) return self;
    
    [fittedParms free];
    fittedParms = [stor copyFromZone:[self zone]];
    return self;
}
- copyToInitial:(parm_t *)type
{
    Storage		*stor;
    
    switch (*type) {
    case PLAY_VALUES:
        stor = variedParms;
	break;
    case FIT_VALUES:
        stor = fittedParms;
	break;
    default:
        stor = nil;
	break;
    }
    if ( !stor ) return self;
    
    [startParms free];
    startParms = [stor copyFromZone:[self zone]];
    return self;
}
- resetParms
{
    [self updateFromType:&currentParms];
    return self;
}
- updateFromType:(parm_t *)type
{
    Storage	*stor;
    fitParm	*parm;
    int		i;
    
    stor = [self parmListOfType:*type];
    i = [stor count];
    while ( i-- ) {
        parm = (fitParm *)[stor elementAt:i];
	parmblk[i] = parm->value;
    }
    currentParms = *type; 
    return self;
}
- (parm_t) currentType
{
    return currentParms;
}

- updateParm:(double) value at:(int) index
{
    parmblk[index] = value;
    return self;
}
- setFloatValue:(float) aValue at:(int) index
{
    fitParm		*parm;
    
    if ( (index < 0) || (index >= nargs) ) return self;
    
    parmblk[index] = aValue;
    parm = [variedParms elementAt:index];
    parm->value = aValue;
    return self;
}
- (Storage *) parmListOfType:(parm_t) type
{
    Storage	*stor;
    
    switch (type) {
    case 0:
    	stor = startParms;
	break;
    case 1:
        stor = fittedParms;
	break;
    case 2:
    	stor = variedParms;
	break;
    default:
        stor = nil;
	break;
    }
    return stor;
}
- (const char *)filename
{
    return filename;
}
- readFile:(const char *)directory :(const char *)file
{
    NXStream	*input;
    char	path[MAXPATHLEN+1];
    char	*streambuf;
    int		len, maxlen;
    
    sprintf( path, "%s/%s", directory, file );
    input = NXMapFile( path, NX_READONLY );
    codeStream = NXOpenMemory(NULL, 0, NX_READWRITE);
    if ( input && codeStream ) {
	NXGetMemoryBuffer( input, &streambuf, &len, &maxlen );
	NXWrite( codeStream, streambuf, maxlen );
	NXCloseMemory( input, NX_FREEBUFFER);
	filename = NXCopyStringBufferFromZone( file, [self zone] );
    }
    return self;
}
- writeFile:(const char *)directory :(const char *) file
{
    char	path[MAXPATHLEN+1];
    
    if ( codeStream ) {
	sprintf( path, "%s/%s", directory, file );
	NXSaveToFile( codeStream, path );
    }
    return self;
}
- write:(NXTypedStream *)stream
{    
    int		i;
    func_id func;
    
    [super write:stream];
    
    NXWriteTypes( stream, "*i", &title, &nargs );
    for ( i = 0; i < nargs; i++ ) {
        NXWriteType( stream, "*", &names[i] );
    }
    NXWriteTypes( stream, "i", &currentParms );
    if ( CURRENT_VERSION < STORAGE_VERSION ) {
      /* 
       * Following doesn't work for NS3.1 pre-release
       * thus we need to work around it.
       */
       	NXWriteObject( stream, fittedParms );
	NXWriteObject( stream, startParms );
	NXWriteObject( stream, variedParms );
    } else {
       	writeParms( stream, fittedParms );
	writeParms( stream, startParms );
	writeParms( stream, variedParms );
    }
    NXWriteTypes( stream, "i", &fitType );
    if ( CURRENT_VERSION >= EMPTYBIN_VERSION ) {
    	NXWriteTypes( stream, "i", &ebType );
    }
    
    /* figure out the index of the func_id block and write that */
    hfunc_index = -1;
    func = h_nextPlotFunc(disp, NULL);
    for (i=0; func != NULL; i++ )
    {
        if (hfunc == func) hfunc_index = i;
	func = h_nextPlotFunc(disp, func);
    }
    NXWriteTypes( stream, "i", &hfunc_index );
    
    return self;
}
- read:(NXTypedStream *)stream
{    
    int		i;
    float 	f;
    
    [super read:stream];
    
    NXReadTypes( stream, "*i", &title, &nargs );
    names = 
       (char **) NXZoneRealloc( [self zone], names, nargs*sizeof( char *) );
    for ( i = 0; i < nargs; i++ ) {
        NXReadType( stream, "*", &names[i] );
    }
    if ( NXTypedStreamClassVersion(stream, "PFunction") > BWG_VERSION ) 
        NXReadTypes( stream, "i", &currentParms );
    else
        NXReadTypes( stream, "if", &currentParms, &f );
    if ( NXTypedStreamClassVersion(stream, "PFunction") < STORAGE_VERSION ) {
      /* The following was broke in NS3.1 pre-release */
	fittedParms = NXReadObject( stream );
	startParms  = NXReadObject( stream );
	variedParms = NXReadObject( stream );
    } else {
	fittedParms = readParms( stream );
	startParms  = readParms( stream );
	variedParms = readParms( stream );
    }

    if (NXTypedStreamClassVersion(stream,"PFunction") >= ONETYPE_VERSION) {
        NXReadTypes( stream, "i", &fitType);
    } 
    else if (NXTypedStreamClassVersion(stream,"PFunction") == TYPES_VERSION) {
        NXReadTypes( stream, "iii", &fitType, &i, &i );
    } else {
        fitType = 0;
    }

    if (NXTypedStreamClassVersion(stream,"PFunction") >= EMPTYBIN_VERSION) {
        NXReadTypes( stream, "i", &ebType);
    }

    if (NXTypedStreamClassVersion(stream,"PFunction") >= LEAVEIN_VERSION) {
	 NXReadTypes( stream, "i", &hfunc_index );
    }
    else
	 hfunc_index = -1;
    
    return self;
}
- free
{
   [fittedParms free];
   [startParms  free];
   [variedParms free];
   if ( filename ) {
       NXZoneFree( [self zone], filename );
       /* do not free codeStream; another object might point to it */
   }
   return [super free];
}
@end
