/***************************************************************************
    File                 : macros.h
    Project              : LabPlot
    Description          : Various preprocessor macros for the curve class
    --------------------------------------------------------------------
    Copyright            : (C) 2019 Martin Marmsoler (martin.marmsoler@gmail.com)

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/

#ifndef MACROSXYCURVE_H
#define MACROSXYCURVE_H

#include "backend/lib/commandtemplates.h"
#include "backend/worksheet/plots/cartesian/XYCurve.h"
#include <QObject>
class AbstractColumn;

/*!
  This macro is used to connect the column to the XYCurve slots:
	- <column_prefix>ColumnAboutToBeRemoved
	- <column_prefix>ColumnNameChanged
	- <column_prefix>DataChanged
  This means these slots must be available when using this function.
  \param column pointer to a AbstractColumn
  \param column_prefix columnnames should have always the same style. For example xColumn -> column_prefix = x, xErrorPlusColumn -> column_prefix = xErrorPlus
  */
#define XYCURVE_COLUMN_CONNECT(column_prefix) \
void columnConnect ## column_prefix ## Column(const AbstractColumn* column) { \
	connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &XYCurve::column_prefix ## ColumnAboutToBeRemoved); \
	auto* parent = column->parentAspect(); \
	while (parent) { \
		connect(parent, &AbstractAspect::aspectAboutToBeRemoved, this, &XYCurve::column_prefix ## ColumnAboutToBeRemoved); \
		parent = parent->parentAspect(); \
	} \
	/* When the column is reused with different name, the curve should be informed to disconnect */ \
	connect(column, &AbstractColumn::reset, this, &XYCurve::column_prefix ## ColumnAboutToBeRemoved); \
	connect(column, &AbstractAspect::aspectDescriptionChanged, this, &XYCurve::column_prefix ## ColumnNameChanged); \
	/* after the curve was updated, emit the signal to update the plot ranges */ \
	connect(column, &AbstractColumn::dataChanged, this, &XYCurve::recalcLogicalPoints); /* must be before DataChanged*/ \
	connect(column, &AbstractColumn::dataChanged, this, &XYCurve::column_prefix ## DataChanged);\
}

#define XYCURVE_COLUMN_CONNECT_CALL(curve, column, column_prefix) \
	curve->columnConnect ## column_prefix ## Column(column); \

/*!
 * This macro is used to connect and disconnect the column from the curve
 * The new column is connected to the curve and the old column is diconnected
 * The columnPath is updated
*/
#define XYCURVE_COLUMN_SETTER_CMD_IMPL_F_S(cmd_name, prefix, finalize_method) \
class XYCurve ## Set ## cmd_name ## ColumnCmd: public StandardSetterCmd<XYCurve::Private, const AbstractColumn*> { \
public: \
	XYCurve ## Set ## cmd_name ## ColumnCmd(XYCurve::Private *target, const AbstractColumn* newValue, const KLocalizedString &description) \
		: StandardSetterCmd<XYCurve::Private, const AbstractColumn*>(target, &XYCurve::Private::prefix ## Column, newValue, description), \
		m_private(target), \
		m_column(newValue) \
		  {} \
		virtual void finalize() override { m_target->finalize_method(); emit m_target->q->prefix ## ColumnChanged(m_target->*m_field); } \
		void redo() override { \
			m_columnOld = m_private->prefix ## Column; \
			if (m_columnOld) {\
				/* disconnect only when column valid, because otherwise all
				 * signals are disconnected */ \
				QObject::disconnect(m_columnOld, nullptr, m_private->q, nullptr); \
			}\
			m_private->prefix ## Column = m_column; \
			if (m_column) { \
				m_private->q->set ## cmd_name ## ColumnPath(m_column->path());\
				XYCURVE_COLUMN_CONNECT_CALL(m_private->q, m_column, prefix) \
			} else \
				m_private->q->set ## cmd_name ## ColumnPath("");\
			finalize();\
			emit m_private->q->prefix ## ColumnChanged(m_column);\
			/* emit DataChanged() in order to notify the plot about the changes */ \
			emit m_private->q->prefix ## DataChanged();\
		}\
		void undo() override { \
			if (m_private->prefix ## Column)\
				QObject::disconnect(m_private->prefix ## Column, nullptr, m_private->q, nullptr); \
			m_private->prefix ## Column = m_columnOld;\
			if (m_columnOld) {\
				m_private->q->set ## cmd_name ## ColumnPath(m_columnOld->path());\
				XYCURVE_COLUMN_CONNECT_CALL(m_private->q, m_column, prefix)\
			} else\
				m_private->q->set ## cmd_name ## ColumnPath("");\
			finalize();\
			emit m_private->q->prefix ## ColumnChanged(m_columnOld);\
			/* emit DataChanged() in order to notify the plot about the changes */ \
			emit m_private->q->prefix ## DataChanged();\
		} \
private: \
	XYCurvePrivate* m_private; \
	const AbstractColumn* m_column{nullptr}; \
	const AbstractColumn* m_columnOld{nullptr};	\
};

#endif // MACROSXYCURVE_H
