/* the idea for this object is that it latches on to a flow of numbers when a certain number is received which is set by the first argument. 
 The second argument sets a tolerance either side of this number at which the object will pick up the flow of data. It is intended to be used with game
 controllers such as playstation joypads or phidget joysticks, with which I often use a button to route the incoming data to another function 
 (a shift button if you like) and so this object is to avoid a jump in data values when switching incoming messages between destinations. */

// Benny Reibel 11/11/2011

#include "ext.h"							// standard Max include, always required
#include "ext_obex.h"						// required for new style Max object

////////////////////////// object struct
typedef struct _latch 
{
	t_object					ob;			// the object itself (must be first)
	
	void*		latch_output;		//creates an outlet
	
} t_latch;

///////////////////////// function prototypes
//// standard set
void *latch_new(t_symbol *s, long argc, t_atom *argv);
void latch_free(t_latch *x);
void latch_assist(t_latch *x, void *b, long m, long a, char *s);
void latch_int(t_latch *x, int i_incomingFloat);
void latch_float(t_latch *x, double d_incomingFloat);
int follow_int(int i_valueToFollow);
//////////////////////// global class pointer variable
void *latch_class;

//variable declairations
int i_outputValue = 0;
int i_comparisonValue = 10;
int i_incomingInt = 0;
int i_latchRange = 1;



int main(void)
{	
	// object initialization, OLD STYLE
	// setup((t_messlist **)&latch_class, (method)latch_new, (method)latch_free, (short)sizeof(t_latch), 
	//		0L, A_GIMME, 0);
    // addmess((method)latch_assist,			"assist",		A_CANT, 0);  
	
	// object initialization, NEW STYLE
	t_class *c;
	
	c = class_new("latch", (method)latch_new, (method)latch_free, (long)sizeof(t_latch), 
				  0L /* leave NULL!! */, A_GIMME, 0);
	
	/* you CAN'T call this from the patcher */
    class_addmethod(c, (method)latch_assist,			"assist",		A_CANT, 0);  
	
	class_addmethod(c, (method)latch_int, "int", A_LONG, 0);
	class_addmethod(c, (method)latch_float, "float", A_FLOAT, 0);
	
	class_register(CLASS_BOX, c); /* CLASS_NOBOX */
	latch_class = c;

	//post("I am the latch object");
	return 0;
}

void latch_assist(t_latch *x, void *b, long m, long a, char *s)
{
	if (m == ASSIST_INLET) { // inlet
		sprintf(s, "number in", a);
	} 
	else {	// outlet
		sprintf(s, "I am outlet %ld", a); 			
	}
}

void latch_free(t_latch *x)
{
	;
}

/*
 A_GIMME signature =
	t_symbol	*s		objectname
	long		argc	num additonal args
	t_atom		*argv	array of t_atom structs
		 type = argv->a_type
		 if (type == A_LONG) ;
		 else if (type == A_FLOAT) ;
		 else if (type == A_SYM) ;
*/
/*
	t_symbol {
		char *s_name;
		t_object *s_thing;
	}
*/
void *latch_new(t_symbol *s, long argc, t_atom *argv)
{
	t_latch *x = NULL;
    long i;
    
	// object instantiation, OLD STYLE
	// if (x = (t_latch *)newobject(latch_class)) {
	// 	;
	// }
	
	// object instantiation, NEW STYLE
	if (x = (t_latch *)object_alloc(latch_class)) {
        object_post((t_object *)x, "a new %s object was instantiated: 0x%X", s->s_name, x);
        object_post((t_object *)x, "it has %ld arguments", argc);
        
        for (i = 0; i < argc; i++) {
            if ((argv + i)->a_type == A_LONG) {
                object_post((t_object *)x, "arg %ld: long (%ld)", i, atom_getlong(argv+i));
				
				i_comparisonValue = atom_getlong(argv+0);	/*the first object argument initialises the comparison 
															value at which the latch will start to follow the incoming numbers*/
				
					i_latchRange = atom_getlong(argv+1);	/*the second argument sets the range either side of the comparison value at which the 
															 latch will take efect - a tolerance as such*/
        
			
			} else if ((argv + i)->a_type == A_FLOAT) {
                object_post((t_object *)x, "arg %ld: float (%f)", i, atom_getfloat(argv+i));
            } else if ((argv + i)->a_type == A_SYM) {
                object_post((t_object *)x, "arg %ld: symbol (%s)", i, atom_getsym(argv+i)->s_name);
            } else {
                object_error((t_object *)x, "forbidden argument");
            }
        }
	}
	x->latch_output = intout((t_object *)x);
	
	return (x);
}

/* the latch_int function will deal with incoming itegers. It will compare the incoming value to the last incoming value and if
 they are equal then it will follow the incoming value and pass it through to the outlet 
 
 the i_latchRange is provided by the second argument within the object and represents the range within which the latch object 
 will start to follow an incoming stream of numbers: if the range is 10 and the i_comparisonValue is 100 then it will 
 latch on to the stream at either 90 or 110*/


void latch_int(t_latch *x, int i_incomingInt)
{
	
	if (i_incomingInt > 0		// this is latching on to positive numbers
		&& i_incomingInt + i_latchRange >= i_comparisonValue && i_incomingInt - i_latchRange <= i_comparisonValue)
	{
		
		outlet_int(x->latch_output, i_incomingInt);
		 i_comparisonValue = i_incomingInt;
		}
	else 
	{
		if(i_incomingInt < 0	// this is latching on to negative numbers
		   && i_incomingInt - i_latchRange <= i_comparisonValue && i_incomingInt + i_latchRange >= i_comparisonValue)
		{
			outlet_int(x->latch_output, i_incomingInt);
			i_comparisonValue = i_incomingInt;
		}
	}
}


// This function is being called from within the latch_int() function and is simply returning the number to be sent to outlet 0.
int follow_int(int i_valueToFollow)
{
	i_comparisonValue = i_valueToFollow;
	return i_valueToFollow;
}








/* the latch_float function will compare the incoming value to the last incoming value and if 
 these are equal then the latch object will follow the incoming data stream and pass it through the outlet */
void latch_float(t_latch *x, double d_incomingFloat)
{

}