/***************************************************************************
 *                           SinBufferedState.cpp                          *
 *                           -------------------                           *
 * copyright            : (C) 2016 by Francisco Naveros                    *
 * email                : fnaveros@ugr.es                                  *
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "../../include/learning_rules/SinBufferedState.h"
#include "../../include/learning_rules/BufferedActivityTimes.h"

#include "../../include/simulation/ExponentialTable.h"
#include "../../include/simulation/TrigonometricTable.h"


#include <string.h>

#include <iostream>
#include <cstdio>
using namespace std;



SinBufferedState::SinBufferedState(unsigned int NumSynapses, unsigned int NewExponent, float NewMaxpos): ConnectionState(NumSynapses, 1), exponent(NewExponent), maxpos(NewMaxpos){

	float tau = this->maxpos/atan((float)exponent);
	float factor = 1./(exp(-atan((float)this->exponent))*pow(sin(atan((float)this->exponent)),(int) this->exponent));

	if (tau==0){
		tau = 1e-6;
	}


	this->maxTimeMeasured=this->maxpos*4;
	this->inv_maxTimeMeasured=1.0f/this->maxTimeMeasured;
	//Precompute the kernel in the look-up table.
	for(int i=0; i<N_elements; i++){
		double time=maxTimeMeasured*i/(N_elements*tau);
		kernelLookupTable[i]=exp(-time)*pow(sin(time),double(exponent))*factor;
	}

	//Inicitialize de buffer of activity
	bufferedActivityTimes = new BufferedActivityTimes(NumSynapses);
}

SinBufferedState::~SinBufferedState() {
	delete bufferedActivityTimes;
}

float SinBufferedState::GetPresynapticActivity(unsigned int index){
	return this->GetStateVariableAt(index, 0);
}

float SinBufferedState::GetPostsynapticActivity(unsigned int index){
	return 0.0f;
}

unsigned int SinBufferedState::GetNumberOfPrintableValues(){
	return ConnectionState::GetNumberOfPrintableValues()+2;
}

double SinBufferedState::GetPrintableValuesAt(unsigned int index, unsigned int position){
	if (position<ConnectionState::GetNumberOfPrintableValues()){
		return ConnectionState::GetStateVariableAt(index,position);
	} else if (position==ConnectionState::GetNumberOfPrintableValues()) {
		return this->exponent;
	} else if (position==ConnectionState::GetNumberOfPrintableValues()+1) {
		return this->maxpos;
	} else return -1;
}


void SinBufferedState::SetNewUpdateTime (unsigned int index, double NewTime, bool pre_post){
	double times[128];
	bufferedActivityTimes->GetTimeElements(index, NewTime - this->maxTimeMeasured,times);

	float value = 0;
	for (int i = 0; i < bufferedActivityTimes->indexData[index * 4 + 1]; i++){
		double ElapsedTime = NewTime - times[i];
		int tableIndex = ElapsedTime*this->N_elements*this->inv_maxTimeMeasured;
		value += this->kernelLookupTable[tableIndex];
	}

	this->SetStateVariableAt(index, 0, value);
	this->SetLastUpdateTime(index, NewTime);
}





void SinBufferedState::ApplyPresynapticSpike(unsigned int index){
	this->bufferedActivityTimes->InsertElement(index, this->GetLastUpdateTime(index), this->GetLastUpdateTime(index)-this->maxTimeMeasured);
}

void SinBufferedState::ApplyPostsynapticSpike(unsigned int index){
	return;
}


