/*
	File                 : SymbolWidget.cc
	Project              : LabPlot
	Description          : label settings widget
	--------------------------------------------------------------------
	SPDX-FileCopyrightText: 2008-2021 Alexander Semke <alexander.semke@web.de>
	SPDX-FileCopyrightText: 2012-2017 Stefan Gerlach <stefan.gerlach@uni-konstanz.de>
	SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "SymbolWidget.h"
#include "backend/worksheet/Worksheet.h"
#include "kdefrontend/GuiTools.h"
#include "kdefrontend/dockwidgets/BaseDock.h"

/*!
	\class SymbolWidget
	\brief Widget for editing the properties of a Symbol object, mostly used in an appropriate dock widget.

	In order the properties of the label to be shown, \c loadConfig() has to be called with the corresponding KConfigGroup
	(settings for a label in *Plot, Axis etc. or for an independent label on the worksheet).

	\ingroup kdefrontend
 */
SymbolWidget::SymbolWidget(QWidget* parent)
	: QWidget(parent) {
	ui.setupUi(this);

	GuiTools::updatePenStyles(ui.cbBorderStyle, Qt::black);
	GuiTools::addSymbolStyles(ui.cbStyle);
	GuiTools::updateBrushStyles(ui.cbFillingStyle, Qt::black);

	connect(ui.cbStyle, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SymbolWidget::styleChanged);
	connect(ui.sbSize, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &SymbolWidget::sizeChanged);
	connect(ui.sbRotation, QOverload<int>::of(&QSpinBox::valueChanged), this, &SymbolWidget::rotationChanged);
	connect(ui.sbOpacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &SymbolWidget::opacityChanged);

	connect(ui.cbFillingStyle, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SymbolWidget::fillingStyleChanged);
	connect(ui.kcbFillingColor, &KColorButton::changed, this, &SymbolWidget::fillingColorChanged);

	connect(ui.cbBorderStyle, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SymbolWidget::borderStyleChanged);
	connect(ui.kcbBorderColor, &KColorButton::changed, this, &SymbolWidget::borderColorChanged);
	connect(ui.sbBorderWidth, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &SymbolWidget::borderWidthChanged);
}

void SymbolWidget::setSymbols(const QList<Symbol*>& symbols) {
	m_symbols = symbols;
	m_symbol = m_symbols.first();

	CONDITIONAL_LOCK_RETURN;
	load();

	// Symbol-Tab
	connect(m_symbol, &Symbol::styleChanged, this, &SymbolWidget::symbolStyleChanged);
	connect(m_symbol, &Symbol::sizeChanged, this, &SymbolWidget::symbolSizeChanged);
	connect(m_symbol, &Symbol::rotationAngleChanged, this, &SymbolWidget::symbolRotationAngleChanged);
	connect(m_symbol, &Symbol::opacityChanged, this, &SymbolWidget::symbolOpacityChanged);
	connect(m_symbol, &Symbol::brushChanged, this, &SymbolWidget::symbolBrushChanged);
	connect(m_symbol, &Symbol::penChanged, this, &SymbolWidget::symbolPenChanged);
}

/*
 * updates the locale in the widgets. called when the application settins are changed.
 */
void SymbolWidget::updateLocale() {
	const auto numberLocale = QLocale();
	ui.sbSize->setLocale(numberLocale);
	ui.sbBorderWidth->setLocale(numberLocale);
}

//*************************************************************
//******** SLOTs for changes triggered in SymbolWidget ********
//*************************************************************
void SymbolWidget::styleChanged(int index) {
	const auto style = static_cast<Symbol::Style>(ui.cbStyle->itemData(index).toInt());

	if (style == Symbol::Style::NoSymbols) {
		ui.sbSize->setEnabled(false);
		ui.sbRotation->setEnabled(false);
		ui.sbOpacity->setEnabled(false);

		ui.kcbFillingColor->setEnabled(false);
		ui.cbFillingStyle->setEnabled(false);

		ui.cbBorderStyle->setEnabled(false);
		ui.kcbBorderColor->setEnabled(false);
		ui.sbBorderWidth->setEnabled(false);
	} else {
		ui.sbSize->setEnabled(true);
		ui.sbRotation->setEnabled(true);
		ui.sbOpacity->setEnabled(true);

		// enable/disable the symbol filling options in the GUI depending on the currently selected symbol.
		bool hasFilling = (style != Symbol::Style::Line && style != Symbol::Style::Cross && style != Symbol::Style::X && style != Symbol::Style::Asterisk
						   && style != Symbol::Style::Tri && style != Symbol::Style::XPlus && style != Symbol::Style::TallPlus
						   && style != Symbol::Style::LatinCross && style != Symbol::Style::DotPlus && style != Symbol::Style::Hash);

		ui.lFilling->setEnabled(hasFilling);
		ui.lFillingStyle->setEnabled(hasFilling);
		ui.cbFillingStyle->setEnabled(hasFilling);
		ui.lFillingColor->setEnabled(hasFilling);
		ui.kcbFillingColor->setEnabled(hasFilling);

		if (hasFilling) {
			ui.lBorder->setText(i18n("Border"));
			bool noBrush = (Qt::BrushStyle(ui.cbFillingStyle->currentIndex()) == Qt::NoBrush);
			ui.kcbFillingColor->setEnabled(!noBrush);
		} else
			ui.lBorder->setText(i18n("Line"));

		ui.cbBorderStyle->setEnabled(true);
		bool noLine = (Qt::PenStyle(ui.cbBorderStyle->currentIndex()) == Qt::NoPen);
		ui.kcbBorderColor->setEnabled(!noLine);
		ui.sbBorderWidth->setEnabled(!noLine);
	}

	CONDITIONAL_LOCK_RETURN;

	for (auto* symbol : m_symbols)
		symbol->setStyle(style);
}

void SymbolWidget::sizeChanged(double value) {
	CONDITIONAL_RETURN_NO_LOCK;

	for (auto* symbol : m_symbols)
		symbol->setSize(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
}

void SymbolWidget::rotationChanged(int value) {
	CONDITIONAL_LOCK_RETURN;

	for (auto* symbol : m_symbols)
		symbol->setRotationAngle(value);
}

void SymbolWidget::opacityChanged(int value) {
	CONDITIONAL_LOCK_RETURN;

	qreal opacity = (float)value / 100.;
	for (auto* symbol : m_symbols)
		symbol->setOpacity(opacity);
}

void SymbolWidget::fillingStyleChanged(int index) {
	if (index == -1)
		return;

	const auto brushStyle = Qt::BrushStyle(index);
	ui.kcbFillingColor->setEnabled(!(brushStyle == Qt::NoBrush));

	CONDITIONAL_LOCK_RETURN;

	QBrush brush;
	for (auto* symbol : m_symbols) {
		brush = symbol->brush();
		brush.setStyle(brushStyle);
		symbol->setBrush(brush);
	}
}

void SymbolWidget::fillingColorChanged(const QColor& color) {
	CONDITIONAL_LOCK_RETURN;

	QBrush brush;
	for (auto* symbol : m_symbols) {
		brush = symbol->brush();
		brush.setColor(color);
		symbol->setBrush(brush);
	}

	GuiTools::updateBrushStyles(ui.cbFillingStyle, color);
}

void SymbolWidget::borderStyleChanged(int index) {
	if (index == -1)
		return;

	const auto penStyle = Qt::PenStyle(index);
	if (penStyle == Qt::NoPen) {
		ui.kcbBorderColor->setEnabled(false);
		ui.sbBorderWidth->setEnabled(false);
	} else {
		ui.kcbBorderColor->setEnabled(true);
		ui.sbBorderWidth->setEnabled(true);
	}

	CONDITIONAL_LOCK_RETURN;

	QPen pen;
	for (auto* symbol : m_symbols) {
		pen = symbol->pen();
		pen.setStyle(penStyle);
		symbol->setPen(pen);
	}
}

void SymbolWidget::borderColorChanged(const QColor& color) {
	CONDITIONAL_LOCK_RETURN;

	QPen pen;
	for (auto* symbol : m_symbols) {
		pen = symbol->pen();
		pen.setColor(color);
		symbol->setPen(pen);
	}
	GuiTools::updatePenStyles(ui.cbBorderStyle, color);
}

void SymbolWidget::borderWidthChanged(double value) {
	CONDITIONAL_RETURN_NO_LOCK;

	QPen pen;
	for (auto* symbol : m_symbols) {
		pen = symbol->pen();
		pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
		symbol->setPen(pen);
	}
}

//*************************************************************
//*********** SLOTs for changes triggered in Symbol ***********
//*************************************************************
void SymbolWidget::symbolStyleChanged(Symbol::Style style) {
	CONDITIONAL_LOCK_RETURN;
	int index = ui.cbStyle->findData((int)style);
	ui.cbStyle->setCurrentIndex(index);
}

void SymbolWidget::symbolSizeChanged(qreal size) {
	CONDITIONAL_LOCK_RETURN;
	ui.sbSize->setValue(Worksheet::convertFromSceneUnits(size, Worksheet::Unit::Point));
}

void SymbolWidget::symbolRotationAngleChanged(qreal angle) {
	CONDITIONAL_LOCK_RETURN;
	ui.sbRotation->setValue(angle);
}

void SymbolWidget::symbolOpacityChanged(qreal opacity) {
	CONDITIONAL_LOCK_RETURN;
	ui.sbOpacity->setValue(round(opacity * 100.0));
}

void SymbolWidget::symbolBrushChanged(const QBrush& brush) {
	CONDITIONAL_LOCK_RETURN;
	ui.cbFillingStyle->setCurrentIndex((int)brush.style());
	ui.kcbFillingColor->setColor(brush.color());
	GuiTools::updateBrushStyles(ui.cbFillingStyle, brush.color());
}

void SymbolWidget::symbolPenChanged(const QPen& pen) {
	CONDITIONAL_LOCK_RETURN;
	ui.cbBorderStyle->setCurrentIndex((int)pen.style());
	ui.kcbBorderColor->setColor(pen.color());
	GuiTools::updatePenStyles(ui.cbBorderStyle, pen.color());
	ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Unit::Point));
}

//**********************************************************
//******************** SETTINGS ****************************
//**********************************************************
void SymbolWidget::load() {
	int index = ui.cbStyle->findData((int)m_symbol->style());
	ui.cbStyle->setCurrentIndex(index);
	ui.sbSize->setValue(Worksheet::convertFromSceneUnits(m_symbol->size(), Worksheet::Unit::Point));
	ui.sbRotation->setValue(m_symbol->rotationAngle());
	ui.sbOpacity->setValue(round(m_symbol->opacity() * 100.0));
	ui.cbFillingStyle->setCurrentIndex((int)m_symbol->brush().style());
	ui.kcbFillingColor->setColor(m_symbol->brush().color());
	ui.cbBorderStyle->setCurrentIndex((int)m_symbol->pen().style());
	ui.kcbBorderColor->setColor(m_symbol->pen().color());
	ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(m_symbol->pen().widthF(), Worksheet::Unit::Point));

	GuiTools::updateBrushStyles(ui.cbFillingStyle, ui.kcbFillingColor->color());
	GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color());
	styleChanged(ui.cbStyle->currentIndex());
}

void SymbolWidget::loadConfig(const KConfigGroup& group) {
	int index = ui.cbStyle->findData((int)m_symbol->style());
	ui.cbStyle->setCurrentIndex(group.readEntry("SymbolStyle", index));
	ui.sbSize->setValue(Worksheet::convertFromSceneUnits(group.readEntry("SymbolSize", m_symbol->size()), Worksheet::Unit::Point));
	ui.sbRotation->setValue(group.readEntry("SymbolRotation", m_symbol->rotationAngle()));
	ui.sbOpacity->setValue(round(group.readEntry("SymbolOpacity", m_symbol->opacity()) * 100.0));
	ui.cbFillingStyle->setCurrentIndex(group.readEntry("SymbolFillingStyle", (int)m_symbol->brush().style()));
	ui.kcbFillingColor->setColor(group.readEntry("SymbolFillingColor", m_symbol->brush().color()));
	ui.cbBorderStyle->setCurrentIndex(group.readEntry("SymbolBorderStyle", (int)m_symbol->pen().style()));
	ui.kcbBorderColor->setColor(group.readEntry("SymbolBorderColor", m_symbol->pen().color()));
	ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(group.readEntry("SymbolBorderWidth", m_symbol->pen().widthF()), Worksheet::Unit::Point));

	GuiTools::updateBrushStyles(ui.cbFillingStyle, ui.kcbFillingColor->color());
	GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color());
	styleChanged(ui.cbStyle->currentIndex());
}

void SymbolWidget::saveConfig(KConfigGroup& group) const {
	group.writeEntry("SymbolStyle", ui.cbStyle->itemData(ui.cbStyle->currentIndex()));
	group.writeEntry("SymbolSize", Worksheet::convertToSceneUnits(ui.sbSize->value(), Worksheet::Unit::Point));
	group.writeEntry("SymbolRotation", ui.sbRotation->value());
	group.writeEntry("SymbolOpacity", ui.sbOpacity->value() / 100.0);
	group.writeEntry("SymbolFillingStyle", ui.cbFillingStyle->currentIndex());
	group.writeEntry("SymbolFillingColor", ui.kcbFillingColor->color());
	group.writeEntry("SymbolBorderStyle", ui.cbBorderStyle->currentIndex());
	group.writeEntry("SymbolBorderColor", ui.kcbBorderColor->color());
	group.writeEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Unit::Point));
}
