#include "../Logger.hpp"
#include "Normalizer.hpp"

#pragma mark -
#pragma mark memory

Normalizer::Normalizer(unsigned int dimension) :
	Projector(dimension, dimension),
	range_min(0), range_max(0), offset(0),
	factor(1) {
	ready = false;

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

	std::fill_n(range_min, outputDimension, 0.0);
	std::fill_n(range_max, outputDimension, 0.0);
	std::fill_n(offset, outputDimension, 0.0);
}

Normalizer::~Normalizer() {
	deallocate();
}

void Normalizer::allocate() {
	range_min = new double[outputDimension];
	range_max = new double[outputDimension];
	offset = new double[outputDimension];
}

void Normalizer::deallocate() {
	delete[] range_min;
	range_min = 0;
	delete[] range_max;
	range_max = 0;
	delete[] offset;
	offset = 0;
}

#pragma mark -
#pragma mark plot

void Normalizer::project(const double* point) {
	for(unsigned int i = 0; i < inputDimension; ++i) {
		projectedPoint[i] = point[i]*factor + offset[i];
	}

	if(!ready) {
		static unsigned int state = 0;

		switch(state) {
		case 0:
			init_range();
			break;
		case 1000000:
			finish_range();
			ready = true;
			break;
		default:
			update_range();
			break;
		}

		++state;
	}
}

#pragma mark -
#pragma mark setting up

void Normalizer::init_range() {
	for(unsigned int i = 0; i < outputDimension; i++) {
		range_min[i] = range_max[i] = projectedPoint[i];
	}
}

void Normalizer::update_range() {
	for(unsigned int i = 0; i < outputDimension; i++) {
		if(projectedPoint[i] < range_min[i]) {
			range_min[i] = projectedPoint[i];
		} else if(projectedPoint[i] > range_max[i]) {
			range_max[i] = projectedPoint[i];
		}
	}
}

void Normalizer::finish_range() {
	factor = 1.0 / (range_max[0] - range_min[0]);
	for(unsigned int i = 1; i < outputDimension; i++) {
		double dist = range_max[i] - range_min[i];
		if(factor * dist > 1.0) {
			factor = 1.0 / dist;
			//teh_size = canvas->size[i];
			LogDebug("Crap for dimension %d\n", i);
		}
	}

	for(unsigned int i = 0; i < outputDimension; i++) {
		offset[i] = -0.5*factor*(range_min[i] + range_max[i]);
	}
}