//#include "commonget~.h"
#include "m_pd.h"


typedef struct _common {
	t_symbol*			name;
	t_sample 			a[64]; 
	t_sample 			b[64];
	t_sample*		w;
	t_sample* 		r; 
	//int				writers;
	//int				written;
	int 			users;
	struct _common* 	next; 
    struct _common* 	previous; 
	t_clock*		clock;
	int 			armed;
    
} t_common;


static t_common* common;


// This should be triggered by the clock
static void common_swap(t_common* pg) {
	//post("clock");
	
	t_sample* temp = pg->r;
	pg->r = pg->w;
	pg->w = temp;
	
	int i;
	t_sample* samples = pg->w;
	for (i=0;i<64;i++) {
		*samples++ = 0;
		
	}
	
	pg->armed = 0;
	
	//if (pg == NULL) post("ouc");
	//pg->written = 0;
	
}

static t_common* common_register(t_symbol* name) {
  
  t_common* new_common;
  
  //is_writer = is_writer ? 1 : 0;

  t_common* pg = common;
  
  if ( pg != NULL) {
		
	// Search for previous common
	while( pg ) {
			if ( pg->name == name) {
				//#ifdef PARAMDEBUG
				 // post("Found put/get with same name");
				//#endif
				//pg->writers = pg->writers + is_writer;
				pg->users = pg->users + 1;
				return pg;
			}
			if ( pg->next == NULL ) break;
			pg = pg->next; 
		}
	}
	
	
	//post("Appending new put/get");
	// Append new common
    new_common = getbytes(sizeof(*new_common));
	new_common->name = name;
	//new_common->writers = is_writer;
	new_common->users = 1; 
    new_common->armed = 0;
	
	new_common->clock = clock_new(new_common, (t_method)common_swap);
	
	new_common->r = new_common->a;
	new_common->w = new_common->b;
	
	new_common->previous = pg;
	if ( pg) {
		pg->next = new_common;
	} else {
		common = new_common;
	}
	
	return new_common;
	
}


static void common_unregister(t_common* pg) {
	
	//post("Trying to remove %s",pg->name->s_name);
	
	//if ( is_writer) pg->writers = pg->writers - 1;
	pg->users = pg->users - 1;
	if ( pg->users <= 0) {
		//post("Removing last put/get of this name");
		if (pg->previous) {
				pg->previous->next = pg->next;
				if (pg->next) pg->next->previous = pg->previous;
			} else {
				common = pg->next;
				if ( pg->next != NULL) pg->next->previous = NULL;
			}
		clock_free(pg->clock);
		freebytes(pg, sizeof *(pg) );
	}
}


static void common_arm(t_common* pg) {
	
	if (!pg->armed) {
		pg->armed = 1;
		clock_delay(pg->clock, 0);
	}
	
}

///////////////
// The class //
///////////////

static t_class *common_tilde_class;

typedef struct _common_tilde {
  t_object  		x_obj;
  //t_sample 			f_common;
  t_common*	pg; 
  t_sample 			f;
} t_common_tilde;

static t_int* common_tilde_perform(t_int *w)
{
	
  t_common_tilde *x = (t_common_tilde *)(w[1]);
  
   t_sample  *in =    (t_sample *)(w[2]);
   t_sample  *out=    (t_sample *)(w[3]);
   int          n =           (int)(w[4]);
   int          m;
   t_sample *samples;
  
  
  if ( x->pg) {
      
      // Do adding
      //if (x->pg->users > x->pg->writers) {
          samples = x->pg->w;
          m = n;
          while (m--) {
              *samples = *samples + *in;
              samples++; in++;
            }
                
        //}
       
	 
      // Do reading
	   samples = x->pg->r;
		m = n;
		while (m--) {
		  *out++ = *samples++;
		}
	
         // Arm for swaping
	   common_arm( x->pg);
		
	} else {
        
        while (n--) {
		  *out++ = 0;
		}
        
    }

  return (w+5);
}


static void common_tilde_set(t_common_tilde *x, t_symbol* s) {
	
	if (gensym("") != s ) {
		if ( x->pg ) {
			if ( x->pg->name != s) {
				 common_unregister(x->pg);
				 x->pg = common_register(s);
			}
		} else {
			x->pg = common_register(s);
		}
	}
	
}


static  void common_tilde_dsp(t_common_tilde *x, t_signal **sp)
{
	
	if ( (int) sp[0]->s_n == 64 ) {
		dsp_add(common_tilde_perform, 4, x,sp[0]->s_vec,sp[1]->s_vec, sp[0]->s_n);
		
	  } else {
		  error("common~ only works with a block size of 64");
	  }
	  
}

static void common_tilde_free( t_common_tilde *x) {

	 if (x->pg) common_unregister(x->pg);
}


static void *common_tilde_new(t_symbol* s)
{
  t_common_tilde *x = (t_common_tilde *)pd_new(common_tilde_class);

  if (gensym("") != s ) x->pg = common_register(s);
 
   outlet_new(&x->x_obj, &s_signal);
 
  return (void *)x;
}

void common_tilde_setup(void) {
  common_tilde_class = class_new(gensym("common~"),
        (t_newmethod)common_tilde_new,
        (t_method)common_tilde_free, sizeof(t_common_tilde),
        0, A_DEFSYM, 0);
  
  class_addmethod(common_tilde_class,
        (t_method)common_tilde_dsp, gensym("dsp"), 0);
		
  class_addmethod(common_tilde_class,
        (t_method)common_tilde_set, gensym("set"), A_SYMBOL, 0);
		
  CLASS_MAINSIGNALIN(common_tilde_class, t_common_tilde, f);
}