#ifndef BRICK_MATH_H
#define BRICK_MATH_H

#include "brick_base.h"
#include "brick_manager.h"


class BrickMathBase: public BrickBase {
public: BrickMathBase(int inputs_num = 1, int outputs_num = 1, int parameters_num = 0): BrickBase(inputs_num, outputs_num, parameters_num) {lib = "Mathematic";}
};


class BrickMathAmplifier: public BrickMathBase {
	MBRICK(BrickMathAmplifier)
	BrickMathAmplifier(double gain = 1.): BrickMathBase(2, 1) {setGain(gain); type = "Gain"; setName(type); inNames[1] = "Gain";}
	enum Inputs {Input, Gain};
	void setGain(double gain) {inputs[Gain] = gain;}
	virtual bool tick_body(double time) {outputs[0] = inputs[Gain] * inputs[Input]; return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathAmplifier)


class BrickMathSum: public BrickMathBase {
	MBRICK(BrickMathSum)
	BrickMathSum(int inputs_num = 2): BrickMathBase(inputs_num, 1, inputs_num) {
		parameters.resize(inputs_num);
		parameters.fill(1.);
		type = "Sum";
		setName(type);
		parametersName_ = "Preamps";
		inNames[0] = "";
		io_Type = BrickBase::VariableInputs;
	}
	virtual void parameterChanged(int index) {inNames[index] = parameters[index].toFloat() >= 0. ? "+" : "-";}
	virtual void inputsCountChanged(int count) {setParametersCount(count, 1.);}
	void setPreamp(int input, double preamp) {parameters[input].setValue(preamp);}
	virtual bool tick_body(double time);
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathSum)


class BrickMathSign: public BrickMathBase {
	MBRICK(BrickMathSign)
	BrickMathSign(): BrickMathBase(1, 1) {type = "Sign"; setName(type);}
	enum Inputs {Input};
	virtual bool tick_body(double time) {outputs[0] = sign(inputs[Input]); return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathSign)


class BrickMathAbsolute: public BrickMathBase {
	MBRICK(BrickMathAbsolute)
	BrickMathAbsolute(): BrickMathBase(1, 1) {type = "Abs"; setName(type);}
	enum Inputs {Input};
	virtual bool tick_body(double time) {outputs[0] = fabs(inputs[Input]); return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathAbsolute)


class BrickMathMultiply: public BrickMathBase {
	MBRICK(BrickMathMultiply)
	BrickMathMultiply(int inputs_num = 2): BrickMathBase(inputs_num, 1) {type = "Multiply"; setName(type); inNames[0] = ""; io_Type = BrickBase::VariableInputs;}
	virtual bool tick_body(double time);
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathMultiply)


class BrickMathDivide: public BrickMathBase {
	MBRICK(BrickMathDivide)
	BrickMathDivide(): BrickMathBase(2, 1) {type = "Divide"; setName(type); inNames[0] = "Divident"; inNames[1] = "Divider"; inputs[1] = 2.;}
	enum Inputs {Input, Divider};
	virtual bool tick_body(double time) {if (inputs[Divider] == 0.) return false; outputs[0] = inputs[Input] / inputs[Divider]; return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathDivide)


class BrickMathPower: public BrickMathBase {
	MBRICK(BrickMathPower)
	BrickMathPower(double power = 2.): BrickMathBase(2, 1) {setPower(power); type = "Power"; setName(type); inNames[1] = "Power";}
	enum Inputs {Input, Power};
	void setPower(double power) {inputs[Power] = power;}
	virtual bool tick_body(double time) {outputs[0] = pow(inputs[Input], inputs[Power]); return true;}
private:
	double p;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathPower)


class BrickMathIntegral: public BrickMathBase {
	MBRICK(BrickMathIntegral)
	BrickMathIntegral(double initial = 0.);
	enum Inputs {Input, Enable, Reset, Initial};
	virtual void started();
	virtual void reset_specified() {KF.fromTF(TF); KF.X[0] = inputs[Initial];}
	virtual void parameterChanged(int index) {KF.setMethod((PIMathSolver::Method)parameters[0].toInt());}
	virtual bool tick_body(double time);
protected:
	PIMathSolver KF;
	TransferFunction TF;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathIntegral)


class BrickMathIntegralSaturated: public BrickMathIntegral {
	MBRICK(BrickMathIntegralSaturated)
	BrickMathIntegralSaturated(double initial = 0.): BrickMathIntegral(initial) {type = "IntegralSaturated"; setName(type); setInputsCount(6); inputs[Min] = -1.; inputs[Max] = 1.; inNames[4] = "Min"; inNames[5] = "Max";}
	enum Inputs {Input, Enable, Reset, Initial, Min, Max};
	virtual bool tick_body(double time) {BrickMathIntegral::tick_body(time); if (KF.X[0] < inputs[Min]) {KF.X[0] = inputs[Min]; outputs[0] = KF.X[0];} if (KF.X[0] > inputs[Max]) {KF.X[0] = inputs[Max]; outputs[0] = KF.X[0];} return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathIntegralSaturated)


class BrickMathDerivate: public BrickMathBase {
	MBRICK(BrickMathDerivate)
	BrickMathDerivate(): BrickMathBase(1, 1) {v = 0.; type = "Derivate"; setName(type);}
	virtual bool tick_body(double time);
private:
	double v;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathDerivate)


class BrickMathDeadZone: public BrickMathBase {
	MBRICK(BrickMathDeadZone)
	BrickMathDeadZone(double zone = 1.): BrickMathBase(2, 2) {setZone(zone); type = "DeadZone"; setName(type); inNames[1] = "Zone"; outNames[1] = "In Zone";}
	enum Inputs {Input, Zone};
	enum Outputs {Output, InZone};
	void setZone(double zone) {inputs[Zone] = zone;}
	virtual bool tick_body(double time);
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathDeadZone)


class BrickMathSaturation: public BrickMathBase {
	MBRICK(BrickMathSaturation)
	BrickMathSaturation(double min = -1., double max = 1.): BrickMathBase(3, 3) {setRange(min, max); type = "Saturation"; setName(type); inNames[1] = "Min"; inNames[2] = "Max"; outNames[1] = "< min"; outNames[2] = "> max";}
	enum Inputs {Input, Min, Max};
	enum Outputs {Output, InMin, InMax};
	void setRange(double min, double max) {inputs[Max] = max; inputs[Min] = min;}
	virtual bool tick_body(double time);
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathSaturation)


class BrickMathQuantize: public BrickMathBase {
	MBRICK(BrickMathQuantize)
	BrickMathQuantize(double step = 1.): BrickMathBase(2, 1) {setStep(step); type = "Quantize"; setName(type); inNames[1] = "Step";}
	enum Inputs {Input, Step};
	void setStep(double step) {inputs[Step] = step;}
	virtual bool tick_body(double time) {outputs[0] = piRound(inputs[Input] / inputs[Step]) * inputs[Step]; return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathQuantize)


class BrickMathRelay: public BrickMathBase {
	MBRICK(BrickMathRelay)
	BrickMathRelay(double size = 1., double active = 1., double inactine = 0.): BrickMathBase(4, 1) {setParameters(size, active, inactine); v = 0.; type = "Relay"; setName(type); inNames[1] = "Size"; inNames[2] = "Active Value"; inNames[3] = "Inactive Value";}
	virtual void reset_specified() {v = 0.;}
	enum Inputs {Input, Size, ActiveValue, InactiveValue};
	void setParameters(double size, double active, double inactine) {inputs[Size] = size; inputs[ActiveValue] = active; inputs[InactiveValue] = inactine;}
	virtual bool tick_body(double time);
private:
	double v;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathRelay)


class BrickMathDelayTicks: public BrickMathBase {
	MBRICK(BrickMathDelayTicks)
	BrickMathDelayTicks(uint ticks = 1): BrickMathBase(1, 1, 1) {setDelay(ticks); type = "DelayTicks"; setName(type); paramNames[0] = "Ticks"; parameters[0].setValue(1);}
	virtual void reset_specified() {v.assign(v.size(), 0.);}
	void setDelay(int ticks) {parameters[0].setValue(ticks); v.resize(ticks); v.assign(ticks, 0.);}
	virtual bool tick_body(double time);
private:
	PIDeque<double> v;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathDelayTicks)


class BrickMathDelaySeconds: public BrickMathBase {
	MBRICK(BrickMathDelaySeconds)
	BrickMathDelaySeconds(double secs = 1.): BrickMathBase(1, 1, 1) {setDelay(secs); type = "DelaySeconds"; setName(type); paramNames[0] = "Seconds"; parameters[0].setValue(1.);}
	virtual void reset_specified() {v.assign(v.size(), 0.);}
	void setDelay(double secs) {parameters[0].setValue(secs);}
	virtual bool tick_body(double time);
private:
	PIDeque<double> v;
	uint t;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathDelaySeconds)


class BrickMathThreshold: public BrickMathBase {
	MBRICK(BrickMathThreshold)
	BrickMathThreshold(double value = 0.5): BrickMathBase(2, 1) {setThreshold(value); type = "Threshold"; setName(type); inNames[1] = "Threshold";}
	enum Inputs {Input, Threshold};
	void setThreshold(double value) {inputs[Threshold] = value;}
	virtual bool tick_body(double time) {outputs[0] = (inputs[Input] >= inputs[Threshold] ? 1. : 0.); return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathThreshold)


class BrickMathFunction: public BrickMathBase {
	MBRICK(BrickMathFunction)
	BrickMathFunction();
	virtual void parameterChanged(int index);
	virtual bool tick_body(double time);
private:
	PIEvaluator eval;
	complexd res;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathFunction)


class BrickMathFFT: public BrickMathBase {
	MBRICK(BrickMathFFT)
	BrickMathFFT();
	virtual bool tick_body(double time);
	virtual void started() {t = 0;}
	virtual void reset_specified() {t = 0;}
private:
	PIFFT fft;
	PIVector<complexd> v, o;
	uint t;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathFFT)


class BrickMathCopy: public BrickMathBase {
	MBRICK(BrickMathCopy)
	BrickMathCopy(): BrickMathBase(1, 2) {type = "Copy"; setName(type); outNames[0] = "0"; io_Type = VariableOutputs;}
	virtual bool tick_body(double time) {for (int i = 0; i < outputs_count; ++i) outputs[i] = inputs[0]; return true;}
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathCopy)


class BrickMathBessel: public BrickMathBase {
	MBRICK(BrickMathBessel)
	BrickMathBessel(): BrickMathBase(1, 1, 2) {type = "Bessel"; setName(type); paramNames[0] = "Kind"; paramNames[1] = "Order"; setParameterValue(0, 0); setParameterValue(1, 0);}
	virtual void parameterChanged(int index);
	virtual bool tick_body(double time);
private:
	int o, k;
};
ADD_NEW_TO_COLLECTION(Mathematic, BrickMathBessel)


#endif // BRICK_MATH_H
