#ifndef ATTRACTORKERNEL_HPP
#define ATTRACTORKERNEL_HPP

#include "Logger.hpp"
#include <string>
#include <algorithm>
#include <cstdlib>

#include "stfu/stf.hpp"

class AttractorKernel {
public:
	virtual ~AttractorKernel() {
		dealloc();
	}

#pragma mark -
#pragma mark parameters
	double& operator[](const unsigned int index) {
		return parameters[index];
	}
	double const& operator[](const unsigned int index) const {
		return parameters[index];
	}
	unsigned int getNumberOfParameters() const {
		return numberOfParameters;
	}

	virtual void generate_random_parameters() {
		for(unsigned int i = 0; i < numberOfParameters; ++i)
			parameters[i] = 6.0 * rand() / double(RAND_MAX) - 3.0;
		for(unsigned int i = 0; i < dimension; ++i)
			vectorNew[i] = vectorOld[i] = 2.0 * rand() / double(RAND_MAX) - 1.0;
	}

#pragma mark -
#pragma mark tests
	bool convergent() {
		double sum = 0.0;
		for(unsigned int i = 0; i < dimension; ++i) {
			sum += (vectorNew[i] - vectorOld[i])*(vectorNew[i] - vectorOld[i]);
		}
		return sum < 1.0e-6;
	}

	bool divergent() {
		double sum = 0.0;
		for(unsigned int i = 0; i < dimension; ++i) {
			sum += (vectorNew[i] - vectorOld[i])*(vectorNew[i] - vectorOld[i]);
		}
		return sum > 1.0e3;
	}

#pragma mark -
#pragma mark main
	// iterate his formula, implemented by subclasses
	virtual void operator()() = 0;

	stfu::node stf_output() {
		stfu::node output;
		output.value("type") = type();
		output.value("dimension") = std::to_string((long long unsigned)dimension);
		stf_other(output);

		stfu::node parameters_node;
		for(unsigned int i = 0; i < numberOfParameters; ++i)
			parameters_node.value(i) = std::to_string((long double)parameters[i]);

		output.addChild("parameters", parameters_node);
		return output;
	}

	virtual std::string type() = 0;

	virtual void stf_other(stfu::node&) {}

#pragma mark -
#pragma mark vector
	double const* vector() const {
		return vectorNew;
	}

	double const* previousVector() const {
		return vectorOld;
	}

	unsigned int getDimension() const {
		return dimension;
	}

#pragma mark -
#pragma mark factory functions
	static AttractorKernel* createAttractorKernel(stfu::node& attractorKernel);
	static AttractorKernel* randomAttractorKernel();

protected:
	double* parameters;
	double* vectorNew;
	double* vectorOld;

	unsigned int numberOfParameters;
	unsigned int dimension;

	AttractorKernel(const unsigned int dimension, const unsigned int numberOfParameters) :
		parameters(0), vectorNew(0), vectorOld(0),
		numberOfParameters(numberOfParameters), dimension(dimension) {

		try {
			allocate();
		} catch(std::exception& e) {
			LogError("Couldn't construct Attractorkernel: %s\n", e.what());
			dealloc();
		}

		std::fill_n(parameters, numberOfParameters, 0.0);
		std::fill_n(vectorNew, dimension, 0.0);
		std::fill_n(vectorOld, dimension, 0.0);
	}

private:
	void allocate() {
		parameters = new double[numberOfParameters];
		vectorNew = new double[dimension];
		vectorOld = new double[dimension];
	}

	void dealloc() {
		delete[] vectorOld;
		vectorOld = NULL;
		delete[] vectorNew;
		vectorNew = NULL;
		delete[] parameters;
		parameters = NULL;
	}
};


#endif // ATTRACTORKERNEL_HPP