#include "brick_math.h"


bool BrickMathSum::tick_body(double time) {
	double t = 0.;
	for (int i = 0; i < inputs_count; ++i)
		t += inputs[i] * parameters[i].toFloat();
	outputs[0] = t;
	return true;
}


bool BrickMathMultiply::tick_body(double time) {
	double t = 1.;
	for (int i = 0; i < inputs_count; ++i)
		t *= inputs[i];
	outputs[0] = t;
	return true;
}


BrickMathIntegral::BrickMathIntegral(double initial): BrickMathBase(4, 1, 1) {
	type = "Integral";
	setName(type);
	inNames[1] = "Enable";
	inNames[2] = "Reset";
	inNames[3] = "Initial";
	paramNames[0] = "Method";
	note_ = "While \"Reset\" = 1  \"Output\" = \"Initial\", integrate while \"Enable\" = 1.\n";
	note_ += "Methods description you can find in help content.";//PIMathSolver::methods_desc;
	inputs[1] = 1.;
	parameters[0].setValue(-1);
	saveInputsToDefault();
	started();
}


void BrickMathIntegral::started() {
	TF.vector_Bm.resize(1); TF.vector_An.resize(2);
	TF.vector_Bm[0] = 1.;
	TF.vector_An[0] = 0.; TF.vector_An[1] = 1.;
	KF.fromTF(TF);
	KF.X[0] = inputs[Initial];
	KF.setMethod((PIMathSolver::Method)parameters[0].toInt());
}


bool BrickMathIntegral::tick_body(double time) {
	if (inputs[Reset] > 0.)
		outputs[0] = KF.X[0] = inputs[Initial];
	else {
		if (inputs[Enable] > 0.) {
			KF.setTime(time);
			KF.solve(inputs[0], dt);
			outputs[0] = KF.X[0];
		}
	}
	return true;
}


bool BrickMathDerivate::tick_body(double time) {
	outputs[0] = (inputs[0] - v) / dt;
	v = inputs[0];
	return true;
}


bool BrickMathDeadZone::tick_body(double time) {
	if (fabs(inputs[Input]) < inputs[Zone]) {
		outputs[Output] = 0.;
		outputs[InZone] = 1.;
	} else {
		outputs[Output] = (fabs(inputs[Input]) - inputs[Zone]) * sign(inputs[Input]);
		outputs[InZone] = 0.;
	}
	return true;
}


bool BrickMathSaturation::tick_body(double time) {
	if (inputs[Input] > inputs[Max]) {
		outputs[Output] = inputs[Max];
		outputs[InMin] = 0.;
		outputs[InMax] = 1.;
	} else {
		if (inputs[Input] < inputs[Min]) {
			outputs[Output] = inputs[Min];
			outputs[InMin] = 1.;
			outputs[InMax] = 0.;
		} else {
			outputs[Output] = inputs[Input];
			outputs[InMin] = 0.;
			outputs[InMax] = 0.;
		}
	}
	return true;
}


bool BrickMathRelay::tick_body(double time) {
	if (inputs[Input] - v >= 0) {if (inputs[Input] >= inputs[Size]) outputs[0] = inputs[ActiveValue];}
	else {if (inputs[Input] <= -inputs[Size]) outputs[0] = inputs[InactiveValue];}
	v = inputs[Input];
	return true;
}


bool BrickMathDelayTicks::tick_body(double time) {
	if (parameters[0].toUInt() != v.size()) setDelay(parameters[0].toInt());
	v.pop_back();
	v.push_front(inputs[0]);
	outputs[0] = v.back();
	return true;
}


bool BrickMathDelaySeconds::tick_body(double time) {
	t = piRound(parameters[0].toFloat() / dt) + 1;
	if (t < 1) t = 1;
	if (v.size() != t) {
		v.resize(t);
		v.assign(t, 0.);
	}
	v.pop_back();
	v.push_front(inputs[0]);
	outputs[0] = v.back();
	return true;
}


BrickMathFunction::BrickMathFunction(): BrickMathBase(0, 2, 1) {
	type = "Function";
	setName(type);
	paramNames[0] = "Function";
	outNames[0] = "Re out";
	outNames[1] = "Im out";
	parameters[0].setValue("");
	parameterChanged(0);
	interactive = true;
}


void BrickMathFunction::parameterChanged(int index) {
	eval.clearCustomVariables();
	eval.check(parameters[0].toString());
	PIStringList sl = eval.unknownVariables();
	setInputsCount(sl.size());
	for (uint i = 0; i < sl.size(); ++i) {
		eval.setVariable(sl[i]);
		inNames[i] = sl[i];
	}
	eval.check(parameters[0].toString());
	note_ = "Your expression:\n" + eval.error() + "\n" + eval.expression();
}


bool BrickMathFunction::tick_body(double time) {
	for (int i = 0; i < inputs_count; ++i)
		eval.setCustomVariableValue(i, complexd(inputs[i], 0.));
	res = eval.evaluate();
	outputs[0] = res.real();
	outputs[1] = res.imag();
	return true;
}


BrickMathFFT::BrickMathFFT(): BrickMathBase(1, 1, 2) {
	type = "FFT";
	setName(type);
	paramNames[0] = "Buffer size (2^n)";
	paramNames[1] = "Inverse";
	parameters[0].setValue(256);
	parameters[1].setValue(0);
	t = 0;
	buffered = true;
}


bool BrickMathFFT::tick_body(double time) {
	if (v.size() != parameters[0].toUInt()) {
		t = parameters[0].toInt();
		v.resize(t);
		v.fill(complexd(0., 0.));
		buffer.resize(t, 0.);
		t = 0;
	}
	outputs[0] = (t >= 0 && t < o.size_s()) ? o[t].real() : 0.;
	if (t >= v.size()) {
		t = 0;
		if (parameters[1].toInt() > 0.)
			o = *fft.calcFFTinverse(v);
		else
			o = *fft.calcFFT(v);
		for (uint i = 0; i < buffer.size(); ++i)
			buffer[i] = abs(o[i]);
	} else {
		v[t] = inputs[0];
		++t;
	}
	return true;
}


void BrickMathBessel::parameterChanged(int index) {
	k = parameters[0].toInt();
	o = parameters[1].toInt();
	if (k == 0) outNames[0] = "J";
	if (k == 1) outNames[0] = "Y";
	if (k < 0 || k > 1) {
		outNames[0] = "?";
		return;
	}
	outNames[0] += PIString::fromNumber(o);
}


bool BrickMathBessel::tick_body(double time) {
	k = parameters[0].toInt();
	o = parameters[1].toInt();
	if (k == 0) {
		if (o == 0)
			outputs[0] = piJ0(inputs[0]);
		else {
			if (o == 1)
				outputs[0] = piJ1(inputs[0]);
			else
				outputs[0] = piJn(o, inputs[0]);
		}
	}
	if (k == 1) {
		if (o == 0)
			outputs[0] = piY0(inputs[0]);
		else {
			if (o == 1)
				outputs[0] = piY1(inputs[0]);
			else
				outputs[0] = piYn(o, inputs[0]);
		}
	};
	return true;
}
