/*
	Copyright 2006-2026 The QElectroTech Team
	This file is part of QElectroTech.

	QElectroTech 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.

	QElectroTech 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 QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "autonumberingdockwidget.h"

#include "../../diagram.h"
#include "../../diagramview.h"
#include "../../qetapp.h"
#include "../../shortcutmanager.h"
#include "../../titleblockproperties.h"
#include "../../ui/projectpropertiesdialog.h"
#include "../numerotationcontext.h"
#include "ui_autonumberingdockwidget.h"

#include <QComboBox>
#include <QLineEdit>

/**
	@brief AutoNumberingDockWidget::AutoNumberingDockWidget
	Constructor
	@param parent : parent widget
*/
AutoNumberingDockWidget::AutoNumberingDockWidget(QWidget *parent) :
	QDockWidget(parent),
	ui(new Ui::AutoNumberingDockWidget)
{
	ui->setupUi(this);
	this->setDisabled(true);
}

/**
	@brief AutoNumberingDockWidget::~AutoNumberingDockWidget
	Destructor
*/
AutoNumberingDockWidget::~AutoNumberingDockWidget()
{
	this->disconnect();
	delete ui;
}

/**
	@brief AutoNumberingDockWidget::clear
	Remove all combo box values
*/
void AutoNumberingDockWidget::clear()
{
	ui->m_conductor_cb->clear();
	ui->m_element_cb->clear();
	ui->m_folio_cb->clear();
	ui->m_conductor_value_le->clear();
	ui->m_element_value_le->clear();
	ui->m_folio_value_le->clear();
}

void AutoNumberingDockWidget::projectClosed()
{
	m_project = nullptr;
	m_project_view = nullptr;
	clear();
	this->setDisabled(true);
}

/**
	@brief AutoNumberingDockWidget::setProject
	@param project: project to be setted
	@param projectview: projectview to be setted
	assign Project and ProjectView, connect all signals and setContext
*/
void AutoNumberingDockWidget::setProject(QETProject *project,
					 ProjectView *projectview)
{
		//Disconnect previous project
	if (m_project && m_project_view)
	{
			//Conductor Signals
		disconnect(m_project, SIGNAL(conductorAutoNumChanged()),
			   this,SLOT(conductorAutoNumChanged()));
		disconnect (m_project,SIGNAL(conductorAutoNumRemoved()),
			    this,SLOT(conductorAutoNumChanged()));
		disconnect (m_project,SIGNAL(conductorAutoNumAdded()),
			    this,SLOT(conductorAutoNumChanged()));
		disconnect(m_project_view,SIGNAL(diagramActivated(DiagramView*)),
			   this,SLOT(setConductorActive(DiagramView*)));
	
			//Element Signals
		disconnect (m_project,SIGNAL(elementAutoNumRemoved(QString)),
			    this,SLOT(elementAutoNumChanged()));
		disconnect (m_project,SIGNAL(elementAutoNumAdded(QString)),
			    this,SLOT(elementAutoNumChanged()));
	
			//Folio Signals
		disconnect (m_project,SIGNAL(folioAutoNumRemoved()),
			    this,SLOT(folioAutoNumChanged()));
		disconnect (m_project,SIGNAL(folioAutoNumAdded()),
			    this,SLOT(folioAutoNumChanged()));
		disconnect (this,
			    SIGNAL(folioAutoNumChanged(QString)),
			    &m_project_view->currentDiagram()->diagram()->border_and_titleblock,
			    SLOT (slot_setAutoPageNum(QString)));
		disconnect(m_project, SIGNAL(defaultTitleBlockPropertiesChanged()),
			   this,SLOT(setActive()));
	
			//Conductor, Element and Folio Signals
		disconnect(m_project, &QETProject::autoNumContextUpdated,
			   this, &AutoNumberingDockWidget::refreshValueFields);
		disconnect(m_project, &QETProject::destroyed,
			   this, &AutoNumberingDockWidget::projectClosed);
	}
	
	m_project = project;
	m_project_view = projectview;
	this->setEnabled(true);

		//Conductor Signals
	connect(m_project, SIGNAL(conductorAutoNumChanged()),
		this,SLOT(conductorAutoNumChanged()));
	connect(m_project,SIGNAL(conductorAutoNumRemoved()),
		this,SLOT(conductorAutoNumChanged()));
	connect(m_project,SIGNAL(conductorAutoNumAdded()),
		this,SLOT(conductorAutoNumChanged()));
	connect(m_project_view,SIGNAL(diagramActivated(DiagramView*)),
		this,SLOT(setConductorActive(DiagramView*)));

		//Element Signals
	connect (m_project,SIGNAL(elementAutoNumRemoved(QString)),
		 this,SLOT(elementAutoNumChanged()));
	connect (m_project,SIGNAL(elementAutoNumAdded(QString)),
		 this,SLOT(elementAutoNumChanged()));

		//Folio Signals
	connect (m_project,SIGNAL(folioAutoNumRemoved()),
		 this,SLOT(folioAutoNumChanged()));
	connect (m_project,SIGNAL(folioAutoNumAdded()),
		 this,SLOT(folioAutoNumChanged()));
	connect (this,
		 SIGNAL(folioAutoNumChanged(QString)),
		 &m_project_view->currentDiagram()->diagram()->border_and_titleblock,
		 SLOT (slot_setAutoPageNum(QString)));
	connect(m_project, SIGNAL(defaultTitleBlockPropertiesChanged()),
		this,SLOT(setActive()));

		//Conductor, Element and Folio Signals
	connect(m_project, &QETProject::autoNumContextUpdated,
		this, &AutoNumberingDockWidget::refreshValueFields);
	connect(m_project, &QETProject::destroyed,
		this, &AutoNumberingDockWidget::projectClosed);

		//Set Combobox Context
	setContext();
	
	ShortcutManager::instance().registerAction(ui->m_configure_pb, "autonum.configure",
						    tr("Autonumérotation"), Qt::CTRL | Qt::SHIFT | Qt::Key_P);
}

/**
	@brief AutoNumberingDockWidget::setContext
	Add all itens to comboboxes
*/
void AutoNumberingDockWidget::setContext()
{

	this->clear();

	//Conductor Combobox
	ui->m_conductor_cb->addItem("");
	QList <QString> keys_conductor = m_project->conductorAutoNum().keys();
	if (!keys_conductor.isEmpty()) {
		foreach (QString str, keys_conductor)
		{ ui->m_conductor_cb-> addItem(str); }
	}

	//Element Combobox
	ui->m_element_cb->addItem("");
	QList <QString> keys_element = m_project->elementAutoNum().keys();
	if (!keys_element.isEmpty()) {
		foreach (QString str, keys_element)
		{ui->m_element_cb -> addItem(str);}
	}

	//Folio Combobox
	ui->m_folio_cb->addItem("");
	QList <QString> keys_folio = m_project->folioAutoNum().keys();
	if (!keys_folio.isEmpty()) {
		foreach (QString str, keys_folio)
		{ ui->m_folio_cb -> addItem(str);}
	}

		//The combo boxes have just been repopulated, so the value fields next
		//to them are showing whatever the previous project left there.
	refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
	refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
	refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);

	this->setActive();
}

/**
	@brief AutoNumberingDockWidget::setConductorActive
	@param dv: activated diagramview
*/
void AutoNumberingDockWidget::setConductorActive(DiagramView* dv) {
	if (dv!=nullptr) {
		QString conductor_autonum = dv->diagram()->conductorsAutonumName();
		int conductor_index = ui->m_conductor_cb->findText(conductor_autonum);
		ui->m_conductor_cb->setCurrentIndex(conductor_index);
	}
}

/**
	@brief AutoNumberingDockWidget::setActive
	Set current used autonumberings
*/
void AutoNumberingDockWidget::setActive()
{

	if (m_project_view!=nullptr) {
			//Conductor
		if (m_project_view->currentDiagram()) {
			QString conductor_autonum = m_project_view->currentDiagram()->diagram()->conductorsAutonumName();
			int conductor_index = ui->m_conductor_cb->findText(conductor_autonum);
			ui->m_conductor_cb->setCurrentIndex(conductor_index);
		}

			//Element
		QString element_formula = m_project->elementAutoNumCurrentFormula();
		QString active_element_autonum = m_project->elementCurrentAutoNum();
		int el_index = ui->m_element_cb->findText(active_element_autonum);
		ui->m_element_cb->setCurrentIndex(el_index);

			//Folio
		if (m_project->defaultTitleBlockProperties().folio == "%autonum") {
			QString page_autonum = m_project->defaultTitleBlockProperties().auto_page_num;
			int folio_index = ui->m_folio_cb->findText(page_autonum);
			ui->m_folio_cb->setCurrentIndex(folio_index);
		}
	}
}

/**
	@brief AutoNumberingDockWidget::conductorAutoNumChanged
	Add new or remove conductor auto num from combobox
*/
void AutoNumberingDockWidget::conductorAutoNumChanged()
{
	ui->m_conductor_cb->clear();

	//Conductor Combobox
	ui->m_conductor_cb->addItem("");
	QList <QString> keys_conductor = m_project->conductorAutoNum().keys();
	if (!keys_conductor.isEmpty()) {
		foreach (QString str, keys_conductor)
		{ ui->m_conductor_cb-> addItem(str); }
	}
	setActive();
}

/**
	@brief AutoNumberingDockWidget::on_m_conductor_cb_activated
	Set new conductor AutoNum
*/
void AutoNumberingDockWidget::on_m_conductor_cb_activated(int)
{
	QString current_autonum = ui->m_conductor_cb->currentText();

	m_project->setCurrentConductorAutoNum(current_autonum);
	m_project_view->currentDiagram()->diagram()->setConductorsAutonumName(current_autonum);
	m_project_view->currentDiagram()->diagram()->loadCndFolioSeq();
	refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
}

/**
	@brief AutoNumberingDockWidget::elementAutoNumChanged
	Add new or remove element auto num from combobox
*/
void AutoNumberingDockWidget::elementAutoNumChanged()
{

	ui->m_element_cb->clear();

	//Element Combobox
	ui->m_element_cb->addItem("");
	QList <QString> keys_element = m_project->elementAutoNum().keys();
	if (!keys_element.isEmpty()) {
		foreach (QString str, keys_element) {ui->m_element_cb -> addItem(str);}
	}
	setActive();
}

/**
	@brief AutoNumberingDockWidget::on_m_element_cb_activated
	Set new element AutoNum
*/
void AutoNumberingDockWidget::on_m_element_cb_activated(int)
{
	m_project->setCurrrentElementAutonum(ui->m_element_cb->currentText());
	m_project_view->currentDiagram()->diagram()->loadElmtFolioSeq();
	refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
}

/**
	@brief AutoNumberingDockWidget::folioAutoNumChanged
	Add new or remove folio auto num from combobox
*/
void AutoNumberingDockWidget::folioAutoNumChanged()
{

	ui->m_folio_cb->clear();

	//Folio Combobox
	ui->m_folio_cb->addItem("");
	QList <QString> keys_folio = m_project->folioAutoNum().keys();
	if (!keys_folio.isEmpty()) {
		foreach (QString str, keys_folio) { ui->m_folio_cb -> addItem(str);}
	}
	setActive();
}

/**
	@brief AutoNumberingDockWidget::on_m_folio_cb_activated
	Set new folio AutoNum
*/
void AutoNumberingDockWidget::on_m_folio_cb_activated(int) {
	QString current_autonum = ui->m_folio_cb->currentText();
	TitleBlockProperties ip = m_project -> defaultTitleBlockProperties();
	if (current_autonum != "") {
		ip.setAutoPageNum(current_autonum);
		ip.folio = "%autonum";
		m_project->setDefaultTitleBlockProperties(ip);
	}
	else {
		ip.folio = "%id/%total";
		m_project->setDefaultTitleBlockProperties(ip);
	}
		emit(folioAutoNumChanged(current_autonum));
	refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
}

void AutoNumberingDockWidget::on_m_configure_pb_clicked()
{
	if (m_project)
	{
		ProjectPropertiesDialog ppd (m_project, this);
		ppd.setCurrentPage(ProjectPropertiesDialog::Autonum);
		ppd.exec();
	}
}

void AutoNumberingDockWidget::on_m_conductor_reset_start_pb_clicked()
{
	resetAutoNum(ui->m_conductor_cb, AutoNumCategory::Conductor);
}

void AutoNumberingDockWidget::on_m_element_reset_start_pb_clicked()
{
	resetAutoNum(ui->m_element_cb, AutoNumCategory::Element);
}

void AutoNumberingDockWidget::on_m_folio_reset_start_pb_clicked()
{
	resetAutoNum(ui->m_folio_cb, AutoNumCategory::Folio);
}

void AutoNumberingDockWidget::on_m_conductor_value_le_editingFinished()
{
	applyValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
}

void AutoNumberingDockWidget::on_m_element_value_le_editingFinished()
{
	applyValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
}

void AutoNumberingDockWidget::on_m_folio_value_le_editingFinished()
{
	applyValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
}

/**
	@brief AutoNumberingDockWidget::contextFor
	@return the numerotation context named by combo_box, for category
*/
NumerotationContext AutoNumberingDockWidget::contextFor(QComboBox *combo_box, AutoNumCategory category) const
{
	const QString key = combo_box->currentText();
	switch (category) {
		case AutoNumCategory::Conductor: return m_project->conductorAutoNum(key);
		case AutoNumCategory::Element:   return m_project->elementAutoNum(key);
		case AutoNumCategory::Folio:     return m_project->folioAutoNum(key);
	}
	return NumerotationContext();
}

/**
	@brief AutoNumberingDockWidget::storeContext
	Write context back under the name selected in combo_box and flag the
	project as modified -- without that last step the change is not saved
	and the user is never asked to save it on close.
*/
void AutoNumberingDockWidget::storeContext(QComboBox *combo_box, AutoNumCategory category, const NumerotationContext &context)
{
	const QString key = combo_box->currentText();
	switch (category) {
		case AutoNumCategory::Conductor: m_project->addConductorAutoNum(key, context); break;
		case AutoNumCategory::Element:   m_project->addElementAutoNum(key, context);   break;
		case AutoNumCategory::Folio:     m_project->addFolioAutoNum(key, context);     break;
	}
	m_project->setModified(true);
}

/**
	@brief AutoNumberingDockWidget::counterIndex
	@return the index of the part the value field shows: the last one that
	actually progresses, i.e. the least significant digit of the number.
	-1 when the context has no progressing part at all.
*/
int AutoNumberingDockWidget::counterIndex(const NumerotationContext &context)
{
	for (int i = context.size() - 1 ; i >= 0 ; --i)
	{
		const QString type = context.itemAt(i).at(0);
		if (type == QLatin1String("unit")
				|| type == QLatin1String("ten")
				|| type == QLatin1String("hundred")
				|| type == QLatin1String("unitfolio")
				|| type == QLatin1String("tenfolio")
				|| type == QLatin1String("hundredfolio")
				|| type == QLatin1String("wrap")
				|| type == QLatin1String("alpha"))
			return i;
	}
	return -1;
}

/**
	@brief AutoNumberingDockWidget::refreshValueFields
	Re-read all three value fields from the project. Called whenever a
	numerotation context's values change, which includes every element or
	conductor that consumes the next number -- without this the field only
	caught up when the user re-picked a rule from the combo box, because
	the combo's activated() signal fires on user interaction alone.
*/
void AutoNumberingDockWidget::refreshValueFields()
{
		//Leave alone a field the user is typing in: numbering an element
		//refreshes all three, and overwriting a half-typed value under the
		//cursor is worse than showing it a moment out of date. Only this
		//automatic path skips; an explicit refresh after a reset or an edit
		//still writes, so the field always ends up canonical.
	if (!ui->m_conductor_value_le->hasFocus())
		refreshValueField(ui->m_conductor_cb, ui->m_conductor_value_le, AutoNumCategory::Conductor);
	if (!ui->m_element_value_le->hasFocus())
		refreshValueField(ui->m_element_cb, ui->m_element_value_le, AutoNumCategory::Element);
	if (!ui->m_folio_value_le->hasFocus())
		refreshValueField(ui->m_folio_cb, ui->m_folio_value_le, AutoNumCategory::Folio);
}

/**
	@brief AutoNumberingDockWidget::refreshValueField
	Show the current value of the selected context's counter, so the field
	always reflects where the numbering has actually got to.
*/
void AutoNumberingDockWidget::refreshValueField(QComboBox *combo_box, QLineEdit *line_edit, AutoNumCategory category)
{
	if (!m_project || combo_box->currentText().isEmpty())
	{
		line_edit->clear();
		line_edit->setEnabled(false);
		return;
	}

	const NumerotationContext context = contextFor(combo_box, category);
	const int index = counterIndex(context);
	line_edit->setEnabled(index >= 0);
	line_edit->setText(index >= 0 ? context.itemAt(index).at(1) : QString());
}

/**
	@brief AutoNumberingDockWidget::applyValueField
	Write the value typed in line_edit to the counter it displays. An empty
	field is treated as "no change" rather than as an empty value, so
	clearing the box by accident cannot wipe the counter.
*/
void AutoNumberingDockWidget::applyValueField(QComboBox *combo_box, QLineEdit *line_edit, AutoNumCategory category)
{
	if (!m_project || combo_box->currentText().isEmpty())
		return;

	NumerotationContext context = contextFor(combo_box, category);
	const int index = counterIndex(context);
	if (index < 0)
		return;

	const QString typed = line_edit->text();
	if (typed.isEmpty() || typed == context.itemAt(index).at(1))
	{
		refreshValueField(combo_box, line_edit, category);
		return;
	}

	context.replaceValue(index, typed);
	storeContext(combo_box, category, context);
	refreshValueField(combo_box, line_edit, category);
}

/**
	@brief AutoNumberingDockWidget::resetAutoNum
	Reset the numerotation context currently selected in combo_box back to
	a per-type starting value, then write it back. Does nothing if no
	context is selected.

	Only parts that actually progress are touched: folio-anchored numeric
	types go back to their own stored initialvalue, plain numeric types go
	back to "1", a wrap part goes back to "0" because a modulo counter
	cycles over [0, modulus) -- a PLC card addressed %IX0.0..%IX0.31 starts
	at 0, not 1 -- and alpha goes back to "a". Non-incrementing types
	(string, plant, locmach, idfolio, folio, elementline, elementcolumn,
	elementprefix) are left alone: there is no "start" for them distinct
	from the fixed or contextual value the user configured.
*/
void AutoNumberingDockWidget::resetAutoNum(QComboBox *combo_box, AutoNumCategory category)
{
	if (!m_project || combo_box->currentText().isEmpty())
		return;

	NumerotationContext context = contextFor(combo_box, category);

	for (int i = 0 ; i < context.size() ; ++i)
	{
		const QStringList item = context.itemAt(i);
		const QString &type = item.at(0);
		if (type == QLatin1String("unitfolio")
				|| type == QLatin1String("tenfolio")
				|| type == QLatin1String("hundredfolio"))
			context.replaceValue(i, item.size() > 3 ? item.at(3) : QStringLiteral("1"));
		else if (type == QLatin1String("unit")
				|| type == QLatin1String("ten")
				|| type == QLatin1String("hundred"))
			context.replaceValue(i, QStringLiteral("1"));
		else if (type == QLatin1String("wrap"))
			context.replaceValue(i, QStringLiteral("0"));
		else if (type == QLatin1String("alpha"))
			context.replaceValue(i, QStringLiteral("a"));
	}

	storeContext(combo_box, category, context);

	switch (category) {
		case AutoNumCategory::Conductor: refreshValueField(combo_box, ui->m_conductor_value_le, category); break;
		case AutoNumCategory::Element:   refreshValueField(combo_box, ui->m_element_value_le, category);   break;
		case AutoNumCategory::Folio:     refreshValueField(combo_box, ui->m_folio_value_le, category);     break;
	}
}
