#ifndef BRICK_BASE_H
#define BRICK_BASE_H

#include "pievaluator.h"
#include "piconfig.h"
#include "pivariant.h"
#include "picollection.h"
#include "pimathmodule.h"

#define MBRICK(brick) PIOBJECT_SUBCLASS(brick, BrickBase) friend class BrickManager; public: virtual BrickBase * copy() {brick * t = new brick(*this); t->setName(name()); t->type = type; t->copied(); return t;}

inline double residue(double f, double s) {return f - floor(f / s) * s;}
inline double dbool(double d) {return d > 0 ? 1. : 0;}

class BrickComposite;
class BrickManager;

class BrickBase: public PIObject
{
	PIOBJECT(BrickBase)
	friend class BrickManager;
	friend void buildTree(PIVector<BrickBase * > & bricks, PIVector<PIVector<BrickBase * > > & tree);
	friend PIVector<PIVector<BrickBase * > > buildSubtree(PIVector<BrickBase * > & bricks, PIVector<PIVector<BrickBase * > > & tree);
public:
	BrickBase(int inputs_num = 1, int outputs_num = 1, int parameters_num = 0);
	virtual ~BrickBase() {;}
	virtual BrickBase * copy() {return new BrickBase(*this);}
	
	enum IOType {
		Fixed = 0x0,
		VariableInputs = 0x01,
		VariableOutputs = 0x02,
		VariableInOuts = VariableInputs | VariableOutputs
	};
	enum Mode {Synchronous, Asynchronous};
	enum ParameterType {Bool, Integer, Float, String, File};
	
	struct Parameter {
		Parameter() {type = BrickBase::Float;}
		Parameter(const char * v) {setValue(v);}
		Parameter(const bool & v) {setValue(v);}
		Parameter(const int & v) {setValue(v);}
		Parameter(const double & v) {setValue(v);}
		Parameter(const PIString & v) {setValue(v);}
		void setType(const ParameterType & t) {type = t;}
		void setValue(const char * v, bool withType = true) {setValue(PIString(v), withType);}
		void setValue(const bool & v, bool withType = true) {if (withType) type = BrickBase::Bool; value = v;}
		void setValue(const int & v, bool withType = true) {if (withType) type = BrickBase::Integer; value = v;}
		void setValue(const double & v, bool withType = true) {if (withType) type = BrickBase::Float; value = v;}
		void setValue(const PIString & v, bool withType = true) {if (withType) type = BrickBase::String; value = v;}
		void setValue(const PIVariant & v, bool withType = true);
		void setValueOnly(const PIString & v) {value = v;}
		bool toBool() const {return value.toBool();}
		int toInt() const {return value.toInt();}
		uint toUInt() const {return value.toInt();}
		double toFloat() const {return value.toDouble();}
		PIString toString() const {return value.toString();}
		ParameterType type;
		PIVariant value;
	};
	
	virtual void reset_specified() {;}
	virtual void inputsCountChanged(int count) {;}
	virtual void outputsCountChanged(int count) {;}
	virtual void started() {;}
	virtual void finished() {;}
	virtual void parameterChanged(int index) {;}
	virtual void saveInputsToDefault() {defInputs = inputs;}
	virtual void resetOutputs() {outputs.fill(0.);}
	virtual void reset() {finished(); inputs = defInputs; dt = pt = time_ = 0.; reset_specified(); resetOutputs(); started();}
	virtual void proceedConnections(bool compositeToo = false);
	bool tick(double time);
	int frequencyDivider() const {return freqDivider_;}
	void setFrequencyDivider(const int divider) {freqDivider_ = divider;}
	int inputsCount() const {return inputs_count;}
	int outputsCount() const {return outputs_count;}
	int parametersCount() const {return parameters_count;}
	double input(const int index = 0) const {return inputs[index];}
	double output(const int index = 0) const {return outputs[index];}
	const PIVariant & parameter(const int index = 0) const {return parameters[index].value;}
	const BrickBase::ParameterType & parameterType(const int index = 0) const {return parameters[index].type;}
	double * input_ptr(const int index = 0) {return &inputs[index];}
	double * output_ptr(const int index = 0) {return &outputs[index];}
	//double * parameter_ptr(const int index = 0) {return &(parameters[index].value.toDouble());}
	void setInputValue(const int index, double value) {inputs[index] = value;}
	void setOutputValue(const int index, double value) {outputs[index] = value;}
	void setDefaultInputValue(const int index, double value) {defInputs[index] = value;}
	void setParameterValue(const int index, const char * value, bool withType = false) {setParameterValue(index, PIString(value), withType);}
	void setParameterValue(const int index, const bool & value, bool withType = false) {parameters[index].setValue(value, withType); parameterChanged(index);}
	void setParameterValue(const int index, const int & value, bool withType = false) {parameters[index].setValue(value, withType); parameterChanged(index);}
	void setParameterValue(const int index, const double & value, bool withType = false) {parameters[index].setValue(value, withType); parameterChanged(index);}
	void setParameterValue(const int index, const PIString & value, bool withType = false) {parameters[index].setValue(value, withType); parameterChanged(index);}
	void setParameterValue(const int index, const PIVariant & value, bool withType = false) {parameters[index].setValue(value, withType); parameterChanged(index);}
	void setParameterValue(const int index, const BrickBase::Parameter & value) {parameters[index] = value; parameterChanged(index);}
	void setParameterValueOnly(const int index, const PIString & value) {parameters[index].setValueOnly(value); parameterChanged(index);}
	void setInputName(const int index, const PIString & name) {inNames[index] = name;}
	void setOutputName(const int index, const PIString & name) {outNames[index] = name;}
	void setParameterName(const int index, const PIString & name) {paramNames[index] = name;}
	void setInputsCount(const int count) {inputs.resize(count); inputs_count = inputs.size(); inNames.resize(count); inputsCountChanged(count);}
	void setOutputsCount(const int count) {outputs.resize(count); outputs_count = outputs.size(); outNames.resize(count); outputsCountChanged(count);}
	void setParametersCount(const int count, const char * val) {parameters.resize(count, BrickBase::Parameter(PIString(val))); paramNames.resize(count); parameters_count = parameters.size();}
	void setParametersCount(const int count, const bool & val) {parameters.resize(count, BrickBase::Parameter(val)); paramNames.resize(count); parameters_count = parameters.size();}
	void setParametersCount(const int count, const int val) {parameters.resize(count, BrickBase::Parameter(val)); paramNames.resize(count); parameters_count = parameters.size();}
	void setParametersCount(const int count, const double & val = 0.) {parameters.resize(count, BrickBase::Parameter(val)); paramNames.resize(count); parameters_count = parameters.size();}
	void setParametersCount(const int count, const PIString & val) {parameters.resize(count, BrickBase::Parameter(val)); paramNames.resize(count); parameters_count = parameters.size();}
	const PIVector<BrickBase::Parameter> & getParameters() const {return parameters;}
	void setInOutCount(const int count) {setInputsCount(count); setOutputsCount(count);}
	int ioType() const {return io_Type;}
	PIString libName() const {return lib;}
	PIString typeName() const {return type;}
	PIString codeName() const {return className();}
	PIString parametersName() const {return parametersName_;}
	PIString inputName(const int index) const {return inNames[index];}
	PIString outputName(const int index) const {return outNames[index];}
	PIString parameterName(const int index) const {return paramNames[index];}
	PIString note() const {return note_;}
	void setNote(const PIString & str) {note_ = str;}
	PIString tag() const {return tag_;}
	void setTag(const PIString & str) {tag_ = str;}
	BrickManager * manager() const {return manager_;}
	void setManager(BrickManager * m) {manager_ = m;}
	bool isInitial() const {return initial;}
	void setInitial(const bool yes = true) {initial = yes;}
	bool isComposite() const {return composite;}
	void setComposite(const bool yes = true) {composite = yes;}
	bool isBuffered() const {return buffered;}
	void setBuffered(const bool yes = true) {buffered = yes;}
	bool isInteractive() const {return interactive;}
	void setInteractive(const bool yes = true) {interactive = yes;}
	bool isRealTimeOnly() const {return rtOnly;}
	void setRealTimeOnly(const bool yes = true) {rtOnly = yes;}
	BrickComposite * toComposite() {return reinterpret_cast<BrickComposite * >(this);}
	
	struct Connection {
		int out_num;
		BrickBase * brick_to;
		int in_num;
		Connection() {;}
		Connection(int out_n, BrickBase * b_to, int in_n) {brick_to = b_to; out_num = out_n; in_num = in_n;}
	};
	
	void addConnection(Connection conn) {connections.push_back(conn);}
	void clearConnections() {connections.clear();}
	BrickBase::Connection connection(int index) const {return connections[index];}
	int connectionsCount() const {return connections.size();}
	void removeConnection(int index) {connections.remove(index);}
	
	bool lastTick, userBool;
	double time_, userDouble;
	int level_, userInt;
	PIVector<double> buffer;

	static bool connect(BrickBase * b_from, int out_num, BrickBase * b_to, int in_num);
	static bool connect(BrickBase * b_from, int out_num, BrickBase & b_to, int in_num) {return connect(b_from, out_num, &b_to, in_num);}
	static bool connect(BrickBase & b_from, int out_num, BrickBase * b_to, int in_num) {return connect(&b_from, out_num, b_to, in_num);}
	static bool connect(BrickBase & b_from, int out_num, BrickBase & b_to, int in_num) {return connect(&b_from, out_num, &b_to, in_num);}

	static bool disconnect(BrickBase * b_from, int out_num, BrickBase * b_to, int in_num);
	static bool disconnect(BrickBase * b_from, int out_num, BrickBase & b_to, int in_num) {return disconnect(b_from, out_num, &b_to, in_num);}
	static bool disconnect(BrickBase & b_from, int out_num, BrickBase * b_to, int in_num) {return disconnect(&b_from, out_num, b_to, in_num);}
	static bool disconnect(BrickBase & b_from, int out_num, BrickBase & b_to, int in_num) {return disconnect(&b_from, out_num, &b_to, in_num);}
	
protected:
	virtual void copied() {;}
	virtual bool tick_body(double time) {return true;}
	
	PIVector<double> inputs, outputs, defInputs;
	PIVector<Connection> connections;
	PIVector<Parameter> parameters;
	PIVector<PIString> inNames, outNames, paramNames;
	PIString parametersName_, lib, type, note_, tag_;
	int inputs_count, outputs_count, parameters_count, io_Type, tmp_, freqDivider_, freqCurrent_;
	double dt, pt;
	bool initial, ticked, composite, buffered, interactive, rtOnly;
	BrickManager * manager_;
};


#endif // BRICK_BASE_H
