#include <stdlib.h>
#include <stdio.h>
#include "spiketools_ext.h"

#define ADDSPIKES 	20
#define SHOWSPIKES      21
#define DELETESPIKES    22
#define READSPIKES      23

#define MINISI 0.0005

AddSpikeTime( struct SpikeEvent **first, struct SpikeEvent **last, float newtime )
{
  struct SpikeEvent *new = (struct SpikeEvent *)malloc(sizeof(struct SpikeEvent));
  struct SpikeEvent *ins;
 
  if ( newtime < 0 ) {
    printf("Negative spiketimes are not allowed!\n");
    return;
  }
  if ( new == NULL ) {
    fprintf(stderr,"Can not allocate memory for new spiketime!\n");
    return 1;
  } else {
    new->time = newtime;
    new->next = NULL;
    new->prev = NULL;

    if ( *first == NULL ) {
      *first = *last = new;
      new->next = new->prev = NULL; 
    } else {
      ins = *last;
      
      while ( (ins != NULL) && (ins->time > newtime)  )
        ins = ins->prev;
      
      if ( ins == NULL ) {
        if ( fabs((*first)->time-newtime) < MINISI ) {
          fprintf(stderr,"New time (%g) too close to existing time. Discarded!\n",newtime);
          free(new);
          return 0;
        } else {
          new->prev = NULL; new->next = *first;
          (*first)->prev = new;
          (*first) = new;
        }
      } else if ( ins == *last ) {
        if ( fabs(ins->time-newtime) < MINISI ) {
          fprintf(stderr,"New time (%g) too close to existing time. Discarded!\n",newtime);
          free(new);
          return 0;
        } else {
          new->next = NULL; new->prev = *last;
          (*last)->next = new;
          (*last) = new;
        } 
      } else {
        if ( (fabs(ins->time-newtime) < MINISI) || (fabs(ins->next->time-newtime) < MINISI) ) {
          fprintf(stderr,"New time (%g) too close to existing time. Discarded!\n",newtime);
          free(new);
          return 0;
        } else {
          new->prev = ins;       new->next = ins->next; 
          ins->next->prev = new; ins->next = new;
        }
      }
    }
  }
  return 1;
}

#define FirstSpike    (spiketrain->firstspike)
#define LastSpike     (spiketrain->lastspike)
#define NextSpike     (spiketrain->nextspike)
#define LastSpikeTime (spiketrain->lastspiketime)
#define NextSpikeTime (spiketrain->nextspiketime)
#define State         (spiketrain->state)
#define Periode       (spiketrain->periode) 
 
SpikeTrain_function(spiketrain,action)
     struct SpikeTrain_type *spiketrain;
     Action                  *action;
{
  
  struct SpikeEvent       *event,*help;
  struct SpikeTrain_type *copy;

  MsgIn  *mi;
  MsgOut *mo;

  int   i,c;
  int   l=1;
  int   line;
  FILE *fi;
  float st;

  if(debug > 1){
    ActionHeader("SpikeTrain",spiketrain,action);
  }

  SELECT_ACTION(action) {

    case CREATE:
      FirstSpike = NULL;
      LastSpike  = NULL;
      NextSpike  = NULL;
      Periode    = 10000000;
    break;

    case COPY:
      copy = ((struct SpikeTrain_type *)action->data);
      copy->periode = Periode;
      copy->nextspiketime = NextSpikeTime;
      copy->lastspiketime = LastSpikeTime;
      copy->state         = State;
      copy->firstspike    = NULL;
      copy->lastspike     = NULL;
      for(event=FirstSpike; event!= NULL; event=event->next) {
        AddSpikeTime(&(copy->firstspike),&(copy->lastspike),event->time);
        if ( event == NextSpike )
          copy->nextspike = copy->lastspike;
      }
    break;

    case DELETE:
      for(event=FirstSpike; event!= NULL; ) {
        help = event;
        event = event->next;
        free(help);
      }
    break;

    case ADDSPIKES:
      for(i=0; i<action->argc; i++)
        AddSpikeTime(&FirstSpike,&LastSpike,atof(action->argv[i]));
    break;

    case READSPIKES:
      if ( (fi=fopen(action->argv[0],"r"))==NULL) {
	fprintf(stderr,"Can not open file (%s)!\n",action->argv[0]);
	return 0;	  
      } else {
	if ( (action->argc == 2) && ((line=atoi(action->argv[1]))>0) ) {
	  while ((!feof(fi)) && (l<line) )
	    if ( (c=getc(fi)) == '\n' ) l++;
	}
	c=-1;
	while ( (!feof(fi)) && (c!='\n') ) {
	  if ( fscanf(fi,"%g",&st) == 1 ) {
	    AddSpikeTime(&FirstSpike,&LastSpike,st);
	  } else if ( !feof(fi) ) {
	    fprintf(stderr,"Error reading file (%s)!\n",action->argv[0]);
	    fclose(fi);
	    return 0;
	  }
	  while( isspace(c=getc(fi)) && (c!='\n') );
	  if ( c!='\n' ) ungetc(c,fi);
	}
	fclose(fi);
      }
    break;

    case SHOWSPIKES:
      for(event=FirstSpike; event!= NULL; event=event->next) {
        printf("%g ",event->time);
      }
      printf("\n");
    break;

    case DELETESPIKES:
      for(event=FirstSpike; event!= NULL; ) {
        help = event;
        event = event->next;
        free(help);
      }      
      FirstSpike = NULL;
      LastSpike  = NULL;
      NextSpike  = NULL;
      NextSpikeTime = -1;
      LastSpikeTime = -1;
      State         =  0;
    break;

    case PROCESS:
      if ( NextSpike != NULL ) {
        if ( SimulationTime() >= (NextSpikeTime-ClockValue(0)/2.0) ) {
          State = 1;
          LastSpikeTime = NextSpikeTime;
          MSGOUTLOOP(spiketrain, mo) {
            CallEventAction(spiketrain, mo);
          }
          if ( (NextSpike = NextSpike->next) == NULL ) {
            NextSpike = FirstSpike;
            NextSpikeTime += (Periode - LastSpike->time + FirstSpike->time);
          } else {
            NextSpikeTime += (NextSpike->time - NextSpike->prev->time);
          }
        } else {
          State = 0;
        }
      }
    break;

    case RESET:
      NextSpike     = FirstSpike;
      NextSpikeTime = NextSpike == NULL ? -1 : NextSpike->time;
      LastSpikeTime = -1;
      State         =  0;
    break;

    case CHECK:
    break;
  }

  return 1;
}






