/*
	Copyright 2006-2023 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 "searchandreplacewidget.h"

#include "../../QWidgetAnimation/qwidgetanimation.h"
#include "../../diagram.h"
#include "../../diagramcontent.h"
#include "../../qetapp.h"
#include "../../qetdiagrameditor.h"
#include "../../qetgraphicsitem/conductor.h"
#include "../../qetgraphicsitem/dynamicelementtextitem.h"
#include "../../qetgraphicsitem/element.h"
#include "../../qetgraphicsitem/elementtextitemgroup.h"
#include "../../qetgraphicsitem/independenttextitem.h"
#include "../../qeticons.h"
#include "../../qetinformation.h"
#include "../../qetproject.h"
#include "replaceadvanceddialog.h"
#include "replaceconductordialog.h"
#include "replaceelementdialog.h"
#include "replacefoliowidget.h"
#include "ui_searchandreplacewidget.h"

#include <QSettings>

/**
	@brief SearchAndReplaceWidget::SearchAndReplaceWidget
	Constructor
	@param parent
*/
SearchAndReplaceWidget::SearchAndReplaceWidget(QWidget *parent) :
	QWidget(parent),
	ui(new Ui::SearchAndReplaceWidget)
{
	ui->setupUi(this);

	m_horizontal_animation = new QWidgetAnimation(
				ui->m_advanced_button_widget,
				Qt::Horizontal,
				QWidgetAnimation::minimumSizeHint,
				250);
	m_vertical_animation = new QWidgetAnimation(
				ui->m_advanced_widget,
				Qt::Vertical,
				QWidgetAnimation::availableSpace, 250);
	QVector<QWidget *> v;
	v << ui->m_header_widget;
	m_vertical_animation->widgetToSubtract(v);

	setHideAdvanced(true);
	setUpTreeItems();
	setUpActions();
	setUpConenctions();
}

/**
	@brief SearchAndReplaceWidget::~SearchAndReplaceWidget
	Destructor
*/
SearchAndReplaceWidget::~SearchAndReplaceWidget()
{
	delete ui;
}

/**
	@brief SearchAndReplaceWidget::event
	Reimplemented to clear the the lines edit and hide
	the advanced widgets, when this widget become hidden
	@param event
	@return
*/
bool SearchAndReplaceWidget::event(QEvent *event)
{
	if (event->type() == QEvent::Hide)
	{
		clear();
		if (m_highlighted_element)
		{
			m_highlighted_element.data()->setHighlighted(false);
			m_highlighted_element.clear();
		}
	}
	else if (event->type() == QEvent::Show)
	{
		ui->m_search_le->setFocus();
		fillItemsList();
		m_root_qtwi->setExpanded(true);
	}

	return QWidget::event(event);
}

/**
	@brief SearchAndReplaceWidget::clear
	Clear the content of the search and replace line edit
	Clear all tree items in the tree widget (except the category items).
*/
void SearchAndReplaceWidget::clear()
{
	disconnect(ui->m_tree_widget, &QTreeWidget::itemChanged,
		   this, &SearchAndReplaceWidget::itemChanged);

	qDeleteAll(m_diagram_hash.keys());
	m_diagram_hash.clear();

	qDeleteAll(m_element_hash.keys());
	m_element_hash.clear();

	qDeleteAll(m_text_hash.keys());
	m_text_hash.clear();

	qDeleteAll(m_conductor_hash.keys());
	m_conductor_hash.clear();

	for (QTreeWidgetItem *qtwi : m_category_qtwi)
		qtwi->setHidden(false);

	ui->m_tree_widget->collapseAll();
	ui->m_tree_widget->clearSelection();

	ui->m_search_le->clear();
	ui->m_replace_le->clear();
	updateNextPreviousButtons();
	ui->m_search_le->setPalette(QPalette());
}

/**
	@brief SearchAndReplaceWidget::setEditor
	Set the diagram editor of this widget
	@param editor
*/
void SearchAndReplaceWidget::setEditor(QETDiagramEditor *editor) {
	m_editor = editor;
}

/**
	@brief SearchAndReplaceWidget::setUpTreeItems
	Set up the main tree widget items
*/
void SearchAndReplaceWidget::setUpTreeItems()
{
	m_root_qtwi = new QTreeWidgetItem(ui->m_tree_widget);
	m_root_qtwi->setIcon(0, QET::Icons::ProjectProperties);
	m_root_qtwi->setText(0, tr("Correspondance :"));
	m_root_qtwi->setCheckState(0, Qt::Checked);
	m_category_qtwi.append(m_root_qtwi);

	m_folio_qtwi = new QTreeWidgetItem(m_root_qtwi);
	m_folio_qtwi->setIcon(0, QET::Icons::Diagram);
	m_folio_qtwi->setText(0, tr("Folios"));
	m_folio_qtwi->setCheckState(0, Qt::Checked);
	m_category_qtwi.append(m_folio_qtwi);

	m_indi_text_qtwi = new QTreeWidgetItem(m_root_qtwi);
	m_indi_text_qtwi->setIcon(0, QET::Icons::PartText);
	m_indi_text_qtwi->setText(0, tr("Champs texte"));
	m_indi_text_qtwi->setCheckState(0, Qt::Checked);
	m_category_qtwi.append(m_indi_text_qtwi);

	m_elements_qtwi = new QTreeWidgetItem(m_root_qtwi);
	m_elements_qtwi->setIcon(0, QET::Icons::Element);
	m_elements_qtwi->setText(0, tr("Eléments"));
	m_elements_qtwi->setCheckState(0, Qt::Checked);
	m_category_qtwi.append(m_elements_qtwi);

	m_simple_elmt_qtwi = new QTreeWidgetItem(m_elements_qtwi);
	m_simple_elmt_qtwi->setText(0, tr("Eléments simple"));
	m_simple_elmt_qtwi->setIcon(0, QET::Icons::Element);
	m_simple_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_qtwi_elmts.append(m_simple_elmt_qtwi);

	m_master_elmt_qtwi= new QTreeWidgetItem(m_elements_qtwi);
	m_master_elmt_qtwi->setText(0, tr("Eléments maître"));
	m_master_elmt_qtwi->setIcon(0, QET::Icons::ElementMaster);
	m_master_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_qtwi_elmts.append(m_master_elmt_qtwi);

	m_slave_elmt_qtwi = new QTreeWidgetItem(m_elements_qtwi);
	m_slave_elmt_qtwi->setText(0, tr("Eléments esclave"));
	m_slave_elmt_qtwi->setIcon(0, QET::Icons::ElementSlave);
	m_slave_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_qtwi_elmts.append(m_slave_elmt_qtwi);

	m_report_elmt_qtwi = new QTreeWidgetItem(m_elements_qtwi);
	m_report_elmt_qtwi->setText(0, tr("Eléments report de folio"));
	m_report_elmt_qtwi->setIcon(0, QET::Icons::FolioXrefComing);
	m_report_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_qtwi_elmts.append(m_report_elmt_qtwi);

	m_terminal_elmt_qtwi = new QTreeWidgetItem(m_elements_qtwi);
	m_terminal_elmt_qtwi->setText(0, tr("Eléments bornier"));
	m_terminal_elmt_qtwi->setIcon(0, QET::Icons::ElementTerminal);
	m_terminal_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_qtwi_elmts.append(m_terminal_elmt_qtwi);
	m_category_qtwi.append(m_qtwi_elmts);

	m_conductor_qtwi = new QTreeWidgetItem(m_root_qtwi);
	m_conductor_qtwi->setIcon(0, QET::Icons::Conductor);
	m_conductor_qtwi->setText(0, tr("Conducteurs"));
	m_conductor_qtwi->setCheckState(0, Qt::Checked);
	m_category_qtwi.append(m_conductor_qtwi);

	updateNextPreviousButtons();
}

/**
	@brief SearchAndReplaceWidget::setHideAdvanced
	Hide advanced widgets
	@param hide
*/
void SearchAndReplaceWidget::setHideAdvanced(bool hide)
{
	m_vertical_animation->setHidden(hide);
	m_horizontal_animation->setHidden(hide);
}

/**
	@brief SearchAndReplaceWidget::fillItemsList
	Fill the tree
*/
void SearchAndReplaceWidget::fillItemsList()
{
	disconnect(ui->m_tree_widget, &QTreeWidget::itemChanged,
		   this, &SearchAndReplaceWidget::itemChanged);

	qDeleteAll(m_element_hash.keys());
	m_element_hash.clear();

	QETProject *project_ = m_editor->currentProject();
	if (!project_)
	{
		ui->m_replace_all_pb->setDisabled(true);
		return;
	}
	ui->m_replace_all_pb->setEnabled(true);
	connect(project_, &QETProject::destroyed, this,
		&SearchAndReplaceWidget::on_m_reload_pb_clicked);

	m_root_qtwi         ->setCheckState(0, Qt::Checked);
	m_folio_qtwi        ->setCheckState(0, Qt::Checked);
	m_elements_qtwi     ->setCheckState(0, Qt::Checked);
	m_simple_elmt_qtwi  ->setCheckState(0, Qt::Checked);
	m_report_elmt_qtwi  ->setCheckState(0, Qt::Checked);
	m_master_elmt_qtwi  ->setCheckState(0, Qt::Checked);
	m_slave_elmt_qtwi   ->setCheckState(0, Qt::Checked);
	m_terminal_elmt_qtwi->setCheckState(0, Qt::Checked);
	m_indi_text_qtwi    ->setCheckState(0, Qt::Checked);
	m_conductor_qtwi    ->setCheckState(0, Qt::Checked);

	DiagramContent dc;
	for (Diagram *diagram : project_->diagrams())
	{
		QString str;

		QSettings settings;
		if (settings.value("genericpanel/folio", true).toBool()) {
			str = diagram->border_and_titleblock.finalfolio();
		} else {
			str = QString::number(diagram->folioIndex()+1);
		}

		str.append(" " + diagram->title());

		QTreeWidgetItem *qtwi = new QTreeWidgetItem(m_folio_qtwi);
		qtwi->setText(0, str);
		qtwi->setData(0, Qt::UserRole, searchTerms(diagram));
		qtwi->setCheckState(0, Qt::Checked);
		m_diagram_hash.insert(qtwi, QPointer<Diagram>(diagram));
		dc += DiagramContent(diagram, false);
	}

	for (Element *elmt : dc.m_elements)
		addElement(elmt);

	//Sort child of each "element type" tree item.
	//we hide, "element type" tree item, if they do not have children
	for(QTreeWidgetItem *qtwi : m_qtwi_elmts)
	{
		qtwi->sortChildren(0, Qt::AscendingOrder);
		qtwi->setHidden(qtwi->childCount() ? false : true);
	}

	for (IndependentTextItem *iti : dc.m_text_fields)
	{
		QTreeWidgetItem *qtwi = new QTreeWidgetItem(m_indi_text_qtwi);
		qtwi->setText(0, iti->toPlainText());
		qtwi->setCheckState(0, Qt::Checked);
		qtwi->setData(0, Qt::UserRole, iti->toPlainText());
		m_text_hash.insert(qtwi, QPointer<IndependentTextItem>(iti));
	}

	m_indi_text_qtwi->sortChildren(0, Qt::AscendingOrder);

	for (Conductor *c : dc.m_potential_conductors)
	{
		QTreeWidgetItem *qtwi = new QTreeWidgetItem(m_conductor_qtwi);
		qtwi->setText(0, c->properties().text);
		qtwi->setCheckState(0, Qt::Checked);
		qtwi->setData(0, Qt::UserRole, searchTerms(c));
		m_conductor_hash.insert(qtwi, QPointer<Conductor>(c));
	}
	m_conductor_qtwi->sortChildren(0, Qt::AscendingOrder);

	updateNextPreviousButtons();
	connect(ui->m_tree_widget, &QTreeWidget::itemChanged,
		this, &SearchAndReplaceWidget::itemChanged);
}

/**
	@brief SearchAndReplaceWidget::addElement
	Add a tree widget item for element
	@param element
*/
void SearchAndReplaceWidget::addElement(Element *element)
{
	QTreeWidgetItem *parent = m_elements_qtwi;
	switch (element->linkType()) {
		case Element::Simple:
			parent = m_simple_elmt_qtwi;
			break;
		case Element::NextReport:
			parent = m_report_elmt_qtwi;
			break;
		case Element::PreviousReport:
			parent = m_report_elmt_qtwi;
			break;
		case Element::Master:
			parent = m_master_elmt_qtwi;
			break;
		case Element::Slave:
			parent = m_slave_elmt_qtwi;
			break;
		case Element::Terminale:
			parent = m_terminal_elmt_qtwi;
			break;
		default:
			break;
	}
	QTreeWidgetItem *qtwi = new QTreeWidgetItem(parent);
	m_element_hash.insert(qtwi, QPointer<Element>(element));

	QString str;
	str += element->elementInformations().value("label").toString();
	if(!str.isEmpty())
		str += ("   ");
	str += element->elementInformations().value("comment").toString();
	if (str.isEmpty())
		str = tr("Inconnue");
	qtwi->setText(0, str);
	qtwi->setCheckState(0, Qt::Checked);
	qtwi->setData(0, Qt::UserRole, searchTerms(element));
}

/**
	@brief SearchAndReplaceWidget::search
	Start the search
*/
void SearchAndReplaceWidget::search()
{
	QString str = ui->m_search_le->text();
	if(str.isEmpty())
	{
		for (QTreeWidgetItemIterator it(m_root_qtwi) ; *it ; ++it) {
			(*it)->setHidden(false);
		}

		for (QTreeWidgetItem *item : m_category_qtwi) {
			item->setExpanded(false);
		}
		m_root_qtwi->setExpanded(true);

		ui->m_tree_widget->setCurrentItem(m_root_qtwi);
		ui->m_search_le->setPalette(QPalette());
	}
	else
	{
		for (QTreeWidgetItemIterator it(m_root_qtwi) ; *it ; ++it) {
			(*it)->setHidden(true);
		}

		bool match = false;

			//Extended search,
			//on each string stored in column 0 with role : UserRole
		QList<QTreeWidgetItem *> qtwi_list;
		qtwi_list.append(m_diagram_hash.keys());
		qtwi_list.append(m_element_hash.keys());
		qtwi_list.append(m_text_hash.keys());
		qtwi_list.append(m_conductor_hash.keys());
		for (QTreeWidgetItem *qtwi : qtwi_list)
		{
			QStringList list = qtwi->data(0, Qt::UserRole)
					.toStringList();

			if(ui->m_mode_cb->currentIndex() == 0)
			{
					//Contain string
				list = list.filter(
					str,
					ui->m_case_sensitive_cb->isChecked()
							? Qt::CaseSensitive
							: Qt::CaseInsensitive);
			}
			else
			{
					//entire word
				QRegularExpression rx("\\b" + str + "\\b");
				if (!rx.isValid())
					{
					qWarning() <<QObject::tr("this is an error in the code")
						  << rx.errorString()
						  << rx.patternErrorOffset();
					return;
				}
				if (!ui->m_case_sensitive_cb->isChecked()) {
					rx.setPatternOptions(
					QRegularExpression::CaseInsensitiveOption);
				}
				list = list.filter(rx);
			}
			if (!list.isEmpty())
			{
				match = true;
				qtwi->setHidden(false);
				setVisibleAllParents(qtwi);
			}
		}

		QPalette background = ui->m_search_le->palette();
		background.setColor(QPalette::Base, match
				    ? QColor("#E0FFF0")
				    : QColor("#FFE0EF"));
		ui->m_search_le->setPalette(background);

			//Go to the first occurrence
		ui->m_tree_widget->setCurrentItem(m_root_qtwi);
		on_m_next_pb_clicked();
	}
}

/**
 * @brief SearchAndReplaceWidget::setUpActions
 * Setup some actions used in this widget
 */
void SearchAndReplaceWidget::setUpActions()
{
	m_select_elements   = new QAction(QET::Icons::Element,   tr("Sélectionner les éléments de ce folio"),    ui->m_tree_widget);
	m_select_conductors = new QAction(QET::Icons::Conductor, tr("Sélectionner les conducteurs de ce folio"), ui->m_tree_widget);
	m_select_texts      = new QAction(QET::Icons::PartText,  tr("Sélectionner les textes de ce folio"),      ui->m_tree_widget);
}

/**
 * @brief SearchAndReplaceWidget::setUpConenctions
 * Setup some connection used in this widget
 */
void SearchAndReplaceWidget::setUpConenctions()
{
	connect(ui->m_search_le, &QLineEdit::textEdited,
		this, &SearchAndReplaceWidget::search);

	connect(ui->m_tree_widget, &QTreeWidget::customContextMenuRequested, [this](const QPoint &pos)
	{
		if (m_diagram_hash.keys().contains(ui->m_tree_widget->currentItem()))
		{
			QMenu *menu = new QMenu(ui->m_tree_widget);
			menu->addAction(m_select_elements);
			menu->addAction(m_select_conductors);
			menu->addAction(m_select_texts);
			menu->popup(ui->m_tree_widget->mapToGlobal(pos));
		}
	});

	connect(m_select_elements, &QAction::triggered, [this]()
	{
		DiagramContent dc(m_diagram_hash.value(ui->m_tree_widget->currentItem()), false);
		for (auto elmt : dc.m_elements) {
			if (auto item = m_element_hash.key(elmt)) {
				item->setCheckState(0, Qt::Checked);
			}
		}
	});

	connect(m_select_conductors, &QAction::triggered, [this]()
	{
		DiagramContent dc(m_diagram_hash.value(ui->m_tree_widget->currentItem()), false);
		for (auto cond : dc.conductors()) {
			if (auto item = m_conductor_hash.key(cond)) {
				item->setCheckState(0, Qt::Checked);
			}
		}
	});

	connect(m_select_texts, &QAction::triggered, [this]()
	{
		DiagramContent dc(m_diagram_hash.value(ui->m_tree_widget->currentItem()), false);
		for (auto text : dc.m_text_fields) {
			if (auto item = m_text_hash.key(text)) {
				item->setCheckState(0, Qt::Checked);
			}
		}
	});
}

/**
	@brief SearchAndReplaceWidget::setVisibleAllParents
	Set visible all parents of item until the invisible root item
	@param item
	@param expend_parent
*/
void SearchAndReplaceWidget::setVisibleAllParents(QTreeWidgetItem *item,
						  bool expend_parent)
{
	if (item->parent())
	{
		QTreeWidgetItem *parent = item->parent();
		parent->setHidden(false);
		setVisibleAllParents(parent);
		parent->setExpanded(expend_parent);
	}
}

/**
	@brief SearchAndReplaceWidget::nextItem
	@param item : find the next item from item,
	if item is nullptr, start the search for the root of the tree
	@param flags
	@return the next item according to flag
	or nullptr if there is not a next item
*/
QTreeWidgetItem *SearchAndReplaceWidget::nextItem(
		QTreeWidgetItem *item,
		QTreeWidgetItemIterator::IteratorFlag flags) const
{
	QTreeWidgetItem *qtwi = item;
	if (!item) {
		qtwi = ui->m_tree_widget->currentItem();
	}

	if (!qtwi) {
		qtwi = m_root_qtwi;
	}

	QTreeWidgetItemIterator it(qtwi, flags);

	++it;
	QTreeWidgetItem *next_ = *it;
	if (next_) {
		return next_;
	}
	else {
		return nullptr;
	}
}

/**
	@brief SearchAndReplaceWidget::previousItem
	@param item : find the previous item from item,
	if item is nullptr, start the search for the root of the tree
	@param flags
	@return the previous item according to flag
	or nullptr if there is not a previous item
*/
QTreeWidgetItem *SearchAndReplaceWidget::previousItem(
		QTreeWidgetItem *item,
		QTreeWidgetItemIterator::IteratorFlag flags) const
{
	QTreeWidgetItem *qtwi = item;
	if (!item) {
		qtwi = ui->m_tree_widget->currentItem();
	}

	if (!qtwi) {
		qtwi = m_root_qtwi;
	}

	QTreeWidgetItemIterator it(qtwi, flags);

	--it;
	QTreeWidgetItem *previous_ = *it;
	if (previous_) {
		return previous_;
	}
	else {
		return nullptr;
	}
}

/**
	@brief SearchAndReplaceWidget::updateNextPreviousButtons
	According to the current item, if there is a next or a previous item,
	we enable/disable the buttons next/previous item.
*/
void SearchAndReplaceWidget::updateNextPreviousButtons()
{
	QTreeWidgetItem *item_ = ui->m_tree_widget->currentItem();
	if (!item_)
	{
		ui->m_next_pb->setEnabled(true);
		ui->m_previous_pb->setDisabled(true);
		return;
	}

	QTreeWidgetItem *next_ = item_;
	do
	{
		next_ = nextItem(next_, QTreeWidgetItemIterator::NotHidden);
		if (!next_)
		{
			ui->m_next_pb->setDisabled(true);
			break;
		}
		else if (!m_category_qtwi.contains(next_))
		{
				 ui->m_next_pb->setEnabled(true);
				 break;
		}
	} while (m_category_qtwi.contains(next_));

	QTreeWidgetItem *previous_ = item_;
	do
	{
		previous_ = previousItem(previous_,
					 QTreeWidgetItemIterator::NotHidden);
		if (!previous_)
		{
			ui->m_previous_pb->setDisabled(true);
			break;
		}
		else if (!m_category_qtwi.contains(previous_))
		{
				 ui->m_previous_pb->setEnabled(true);
				 break;
		}
	} while (m_category_qtwi.contains(previous_));
}

/**
	@brief SearchAndReplaceWidget::itemChanged
	Reimplemented from QTreeWidget.
	Use to update the check state of items.
	@param item
	@param column
*/
void SearchAndReplaceWidget::itemChanged(QTreeWidgetItem *item, int column)
{
	Q_UNUSED(column)
	ui->m_tree_widget->blockSignals(true);

	setChildCheckState(item, item->checkState(0));
	updateParentCheckState(item);

	ui->m_tree_widget->blockSignals(false);
}

/**
	@brief SearchAndReplaceWidget::setChildCheckState
	@param item : Parent of the items to be evaluated
	@param check : check state
	@param deep : if true, we evaluate every subchilds.
*/
void SearchAndReplaceWidget::setChildCheckState(QTreeWidgetItem *item,
						Qt::CheckState check,
						bool deep)
{
	for (int i=0 ; i<item->childCount() ; ++i)
	{
		item->child(i)->setCheckState(0, check);
		if (deep && item->child(i)->childCount()) {
			setChildCheckState(item->child(i), check, deep);
		}
	}
}

/**
	@brief SearchAndReplaceWidget::updateParentCheckState
	@param item : a child item of the parent to be evaluated.
	@param all_parents : if true, we evaluate every parents,
	until the root item.
*/
void SearchAndReplaceWidget::updateParentCheckState(QTreeWidgetItem *item,
						    bool all_parents)
{
	QTreeWidgetItem *parent = item->parent();

	if (!parent) {
		parent = item;
	}

	int check=0,
		partially=0;

	for (int i=0 ; i<parent->childCount() ; ++i)
	{
		switch (parent->child(i)->checkState(0)) {
			case Qt::Checked:
				++check;
				break;
			case Qt::PartiallyChecked:
				++partially;
				break;
			default:
				break;
		}
	}

	if (check == parent->childCount()) {
		parent->setCheckState(0, Qt::Checked);
	}
	else if (partially || check) {
		parent->setCheckState(0, Qt::PartiallyChecked);
	}
	else {
		parent->setCheckState(0, Qt::Unchecked);
	}

	if (all_parents && item->parent()) {
		updateParentCheckState(parent, all_parents);
	}
}

/**
	@brief SearchAndReplaceWidget::activateNextChecked
	Activate the next checked (and visible) item
*/
void SearchAndReplaceWidget::activateNextChecked()
{
		//Next button is disabled, so there is not a next item.
	if (!ui->m_next_pb->isEnabled())
		return;

	do {
		on_m_next_pb_clicked();
	} while ((ui->m_tree_widget->currentItem()->checkState(0)
		  != Qt::Checked
		  ) && ui->m_next_pb->isEnabled());
}

/**
	@brief SearchAndReplaceWidget::selectedDiagram
	@return The list of visible and selected diagram in the tree widget
*/
QList<Diagram *> SearchAndReplaceWidget::selectedDiagram() const
{
	QList <Diagram *> diagram_list;

	for (QTreeWidgetItem *qtwi : m_diagram_hash.keys())
	{
		if (!qtwi->isHidden() && qtwi->checkState(0) == Qt::Checked)
		{
			QPointer <Diagram> p = m_diagram_hash.value(qtwi);
			if (p) {
				diagram_list.append(p.data());
			}
		}
	}

	return diagram_list;
}

/**
	@brief SearchAndReplaceWidget::selectedElement
	@return The list of visible and selected element in the tree widget
*/
QList<Element *> SearchAndReplaceWidget::selectedElement() const
{
	QList <Element *> element_list;

	for (QTreeWidgetItem *qtwi : m_element_hash.keys())
	{
		if (!qtwi->isHidden() && qtwi->checkState(0) == Qt::Checked)
		{
			QPointer <Element> p = m_element_hash.value(qtwi);
			if (p) {
				element_list.append(p.data());
			}
		}
	}

	return element_list;
}

/**
	@brief SearchAndReplaceWidget::selectedConductor
	@return The list of visible and selected conductor in the tree widget
*/
QList<Conductor *> SearchAndReplaceWidget::selectedConductor() const
{
	QList <Conductor *> conductor_list;

	for (QTreeWidgetItem *qtwi : m_conductor_hash.keys())
	{
		if (!qtwi->isHidden() && qtwi->checkState(0) == Qt::Checked)
		{
			QPointer <Conductor> c = m_conductor_hash.value(qtwi);
			if (c) {
				conductor_list.append(c.data());
			}
		}
	}

	return conductor_list;
}

/**
	@brief SearchAndReplaceWidget::selectedText
	@return The list of visible and selected independant text
	in the tree widget
*/
QList<IndependentTextItem *> SearchAndReplaceWidget::selectedText() const
{
	QList <IndependentTextItem *> text_list;

	for(QTreeWidgetItem *qtwi : m_text_hash.keys())
	{
		if (!qtwi->isHidden() && qtwi->checkState(0) == Qt::Checked)
		{
			QPointer<IndependentTextItem> t =
					m_text_hash.value(qtwi);
			if (t) {
				text_list.append(t.data());
			}
		}
	}

	return text_list;
}

/**
	@brief SearchAndReplaceWidget::searchTerms
	@param diagram
	@return All QStrings use as terms for search.
*/
QStringList SearchAndReplaceWidget::searchTerms(Diagram *diagram)
{
	QStringList list;
	TitleBlockProperties prop =
			diagram->border_and_titleblock.exportTitleBlock();
	list.append(prop.title);
	list.append(prop.author);
	list.append(prop.filename);
	list.append(prop.plant);
	list.append(prop.locmach);
	list.append(prop.indexrev);
	list.append(prop.folio);
	list.append(prop.date.toString());
	for (QString key : prop.context.keys()) {
		list.append(prop.context.value(key).toString());
	}

	return list;
}

/**
	@brief SearchAndReplaceWidget::searchTerms
	@param element
	@return All QString use as terms for search
*/
QStringList SearchAndReplaceWidget::searchTerms(Element *element)
{
	QStringList list;
	DiagramContext context = element->elementInformations();
	for (QString key : QETInformation::elementInfoKeys())
	{
		QString str = context.value(key).toString();
		if (!str.isEmpty()) {
			list.append(str);
		}
	}

	for (DynamicElementTextItem *deti : element->dynamicTextItems())
	{
		if (deti->textFrom()
				== DynamicElementTextItem::UserText
				|| deti->textFrom()
				== DynamicElementTextItem::CompositeText) {
			list.append(deti->toPlainText());
		}
	}
	for (ElementTextItemGroup *group : element->textGroups())
	{
		list.append(group->name());

		for (DynamicElementTextItem *deti : group->texts()) {
			if (deti->textFrom()
					== DynamicElementTextItem::UserText
					|| deti->textFrom()
					== DynamicElementTextItem::CompositeText) {
				list.append(deti->toPlainText());
			}
		}
	}

	return list;
}

/**
	@brief SearchAndReplaceWidget::searchTerms
	@param conductor
	@return all QString use as terms for search.
*/
QStringList SearchAndReplaceWidget::searchTerms(Conductor *conductor)
{
	QStringList list;
	ConductorProperties properties = conductor->properties();

	list.append(properties.text);
	list.append(properties.m_function);
	list.append(properties.m_tension_protocol);
	list.append(properties.m_wire_color);
	list.append(properties.m_wire_section);

	return list;
}

/**
	@brief SearchAndReplaceWidget::on_m_quit_button_clicked
*/
void SearchAndReplaceWidget::on_m_quit_button_clicked()
{
	if (auto animator =
			this->findChild<QWidgetAnimation *>(
				"search and replace animator")) {
		animator->setHidden(!this->isHidden());
	} else {
		this->setHidden(true);
	}
}

void SearchAndReplaceWidget::on_m_advanced_pb_toggled(bool checked) {
	setHideAdvanced(!checked);
}

void SearchAndReplaceWidget::on_m_tree_widget_itemDoubleClicked(
		QTreeWidgetItem *item,
		int column)
{
	Q_UNUSED(column)

	if (m_diagram_hash.keys().contains(item))
	{
		QPointer<Diagram> diagram = m_diagram_hash.value(item);
		if(diagram) {
			diagram.data()->showMe();
		}
	}
	else if (m_element_hash.keys().contains(item))
	{
		QPointer<Element> elmt = m_element_hash.value(item);
		if (elmt) {
			elmt.data()->diagram()->showMe();
		}
	}
	else if (m_text_hash.keys().contains(item))
	{
		QPointer<IndependentTextItem> text = m_text_hash.value(item);
		if (text) {
			text.data()->diagram()->showMe();
		}
	}
	else if (m_conductor_hash.keys().contains(item))
	{
		QPointer<Conductor> cond = m_conductor_hash.value(item);
		if (cond) {
			cond.data()->diagram()->showMe();
		}
	}
}

void SearchAndReplaceWidget::on_m_reload_pb_clicked()
{
	clear();
	if (m_highlighted_element)
	{
		m_highlighted_element.data()->setHighlighted(false);
		m_highlighted_element.clear();
	}
	if (m_last_selected)
	{
		m_last_selected.data()->setSelected(false);
		m_last_selected.clear();
	}

	ui->m_search_le->setFocus();
	fillItemsList();
	m_root_qtwi->setExpanded(true);
}

void SearchAndReplaceWidget::on_m_tree_widget_currentItemChanged(
		QTreeWidgetItem *current,
		QTreeWidgetItem *previous)
{
	Q_UNUSED(previous)

	if(m_highlighted_element) {
		m_highlighted_element.data()->setHighlighted(false);
	}
	if (m_last_selected) {
		m_last_selected.data()->setSelected(false);
	}

	if (m_element_hash.keys().contains(current))
	{
		QPointer<Element> elmt = m_element_hash.value(current);
		if (elmt)
		{
			m_highlighted_element = elmt;
			elmt.data()->setHighlighted(true);
		}
	}
	else if (m_text_hash.keys().contains(current))
	{
		QPointer<IndependentTextItem> text = m_text_hash.value(current);
		if (text)
		{
			text.data()->setSelected(true);
			m_last_selected = text;
		}
	}
	else if (m_conductor_hash.keys().contains(current))
	{
		QPointer<Conductor> cond = m_conductor_hash.value(current);
		if (cond)
		{
			cond.data()->setSelected(true);
			m_last_selected = cond;
		}
	}

	updateNextPreviousButtons();
	if (!current) {
		return;
	}

	if (current->checkState(0) == Qt::Checked
			&& !m_category_qtwi.contains(current)) {
		ui->m_replace_pb->setEnabled(true);
	} else {
		ui->m_replace_pb->setDisabled(true);
	}
}

void SearchAndReplaceWidget::on_m_next_pb_clicked()
{
	QTreeWidgetItem *item = nullptr;

	do
	{
		item = nextItem(nullptr, QTreeWidgetItemIterator::NotHidden);
		if (item) {
			ui->m_tree_widget->setCurrentItem(item);
		} else {
			return;
		}
	} while (m_category_qtwi.contains(item));

	ui->m_tree_widget->setCurrentItem(item);
	ui->m_tree_widget->scrollToItem(item);
	on_m_tree_widget_itemDoubleClicked(item, 0);
}

void SearchAndReplaceWidget::on_m_previous_pb_clicked()
{
	QTreeWidgetItem *item = nullptr;

	do
	{
		item = previousItem(nullptr,
				    QTreeWidgetItemIterator::NotHidden);
		if (item) {
			ui->m_tree_widget->setCurrentItem(item);
		}
		else
		{
			/* There is not a previous selected item,
			 * so the current item is m_root_qtwi
			 * but we know he must not be selected
			 * we user click on the button 'next item'.
			 * So we select the first selectable item
			 * by calling on_m_next_pb_clicked
			 */
			on_m_next_pb_clicked();
			return;
		}
	} while (m_category_qtwi.contains(item));

	ui->m_tree_widget->setCurrentItem(item);
	ui->m_tree_widget->scrollToItem(item);
	on_m_tree_widget_itemDoubleClicked(item, 0);
}

void SearchAndReplaceWidget::on_m_folio_pb_clicked()
{
	ReplaceFolioDialog *dialog = new ReplaceFolioDialog(this);
	dialog->setTitleBlockProperties(m_worker.m_titleblock_properties);

	int result = dialog->exec();
	if (result == QDialogButtonBox::AcceptRole)
	{
		QString text = ui->m_folio_pb->text();
		if (!text.endsWith(tr(" [édité]"))) {
			text.append(tr(" [édité]"));
		}
		ui->m_folio_pb->setText(text);
		m_worker.m_titleblock_properties =
				dialog->titleBlockProperties();
	}
	else if (result == QDialogButtonBox::ResetRole)
	{
		QString text = ui->m_folio_pb->text();
		if (text.endsWith(tr(" [édité]"))) {
			text.remove(tr(" [édité]"));
		}
		ui->m_folio_pb->setText(text);
		m_worker.m_titleblock_properties = TitleBlockProperties();
	}
}

/**
	@brief SearchAndReplaceWidget::on_m_replace_pb_clicked
	Replace the current selection
*/
void SearchAndReplaceWidget::on_m_replace_pb_clicked()
{
	QTreeWidgetItem *qtwi = ui->m_tree_widget->currentItem();
	if(!qtwi) {
		return;
	}
	if (!m_category_qtwi.contains(qtwi)
			&& qtwi->checkState(0) == Qt::Checked)
	{
		if (ui->m_folio_pb->text().endsWith(tr(" [édité]")) &&
			m_diagram_hash.keys().contains(qtwi))
		{
			QPointer<Diagram> d = m_diagram_hash.value(qtwi);
			if (d) {
				m_worker.replaceDiagram(d.data());
			}
		}
		else if (ui->m_element_pb->text().endsWith(tr(" [édité]")) &&
				 m_element_hash.keys().contains(qtwi))
		{
			QPointer<Element> e = m_element_hash.value(qtwi);
			if (e) {
				m_worker.replaceElement(e.data());
			}
		}
		else if (!ui->m_replace_le->text().isEmpty() &&
				 m_text_hash.keys().contains(qtwi))
		{
			m_worker.m_indi_text = ui->m_replace_le->text();
			QPointer<IndependentTextItem> t =
					m_text_hash.value(qtwi);
			if (t) {
				m_worker.replaceIndiText(t.data());
			}

		}
		else if (ui->m_conductor_pb->text().endsWith(tr(" [édité]")) &&
				 m_conductor_hash.keys().contains(qtwi))
		{
			QPointer<Conductor> c = m_conductor_hash.value(qtwi);
			if (c) {
				m_worker.replaceConductor(c.data());
			}
		}

			//Replace advanced
		if (ui->m_advanced_replace_pb->text().endsWith(tr(" [édité]")))
		{
			QList <Diagram *>dl;
			QList <Element *>el;
			QList <IndependentTextItem *>tl;
			QList <Conductor *>cl;

			if (m_diagram_hash.keys().contains(qtwi))
			{
				QPointer<Diagram> d =
						m_diagram_hash.value(qtwi);
				if (d) {
					dl.append(d.data());
				}
			}
			else if (m_element_hash.keys().contains(qtwi))
			{
				QPointer<Element> e =
						m_element_hash.value(qtwi);
				if (e) {
					el.append(e.data());
				}
			}
			else if (m_text_hash.keys().contains(qtwi))
			{
				QPointer<IndependentTextItem> t =
						m_text_hash.value(qtwi);
				if (t) {
					tl.append(t.data());
				}
			}
			else if (m_conductor_hash.keys().contains(qtwi))
			{
				QPointer<Conductor> c =
						m_conductor_hash.value(qtwi);
				if (c) {
					cl.append(c.data());
				}
			}

			m_worker.replaceAdvanced(dl, el, tl, cl);
		}
	}

	activateNextChecked();
	ui->m_replace_pb->setEnabled(ui->m_next_pb->isEnabled());
}

/**
	@brief SearchAndReplaceWidget::on_m_replace_all_pb_clicked
	Replace all checked item
*/
void SearchAndReplaceWidget::on_m_replace_all_pb_clicked()
{
	if (ui->m_folio_pb->text().endsWith(tr(" [édité]"))) {
		m_worker.replaceDiagram(selectedDiagram());
	}
	if (ui->m_element_pb->text().endsWith(tr(" [édité]"))) {
		m_worker.replaceElement(selectedElement());
	}
	if (!ui->m_replace_le->text().isEmpty()) {
		m_worker.m_indi_text = ui->m_replace_le->text();
		m_worker.replaceIndiText(selectedText() );
	}
	if (ui->m_conductor_pb->text().endsWith(tr(" [édité]"))) {
		m_worker.replaceConductor(selectedConductor());
	}
	if (ui->m_advanced_replace_pb->text().endsWith(tr(" [édité]"))) {

		m_worker.replaceAdvanced(selectedDiagram(),
					 selectedElement(),
					 selectedText(),
					 selectedConductor());
	}

		//Change was made, we reload the panel
		//and search again to keep up to date the tree widget
		//and the match item of search
	QString txt = ui->m_search_le->text();
	on_m_reload_pb_clicked();
	ui->m_search_le->setText(txt);
	search();
}

void SearchAndReplaceWidget::on_m_element_pb_clicked()
{
	ReplaceElementDialog *dialog = new ReplaceElementDialog(
				m_worker.m_element_context,
				this);

	int result = dialog->exec();
	if (result == QDialogButtonBox::AcceptRole)
	{
		QString text = ui->m_element_pb->text();
		if (!text.endsWith(tr(" [édité]"))) {
			text.append(tr(" [édité]"));
		}
		ui->m_element_pb->setText(text);
		m_worker.m_element_context = dialog->context();
	}
	else if (result == QDialogButtonBox::ResetRole)
	{
		QString text = ui->m_element_pb->text();
		if (text.endsWith(tr(" [édité]"))) {
			text.remove(tr(" [édité]"));
		}
		ui->m_element_pb->setText(text);
		m_worker.m_element_context = DiagramContext();
	}
}

/**
	@brief SearchAndReplaceWidget::on_m_mode_cb_currentIndexChanged
	Update the search when user change mode.
	@param index
*/
void SearchAndReplaceWidget::on_m_mode_cb_currentIndexChanged(int index)
{
	Q_UNUSED(index)
	search();
}

/**
	@brief SearchAndReplaceWidget::on_m_case_sensitive_cb_stateChanged
	Update the search when change the case sensitive
	@param arg1
*/
void SearchAndReplaceWidget::on_m_case_sensitive_cb_stateChanged(int arg1)
{
	Q_UNUSED(arg1)
	search();
}

/**
	@brief SearchAndReplaceWidget::on_m_conductor_pb_clicked
	Open a dialog to edit the condutor properties
*/
void SearchAndReplaceWidget::on_m_conductor_pb_clicked()
{
	ReplaceConductorDialog *dialog = new ReplaceConductorDialog(
				m_worker.m_conductor_properties,
				this);
	int result = dialog->exec();

	if (result == QDialogButtonBox::AcceptRole)
	{
		QString text = ui->m_conductor_pb->text();
		if (!text.endsWith(tr(" [édité]"))) {
			text.append(tr(" [édité]"));
		}
		ui->m_conductor_pb->setText(text);
		m_worker.m_conductor_properties = dialog->properties();
	}
	else if (result == QDialogButtonBox::ResetRole)
	{
		QString text = ui->m_conductor_pb->text();
		if (text.endsWith(tr(" [édité]"))) {
			text.remove(tr(" [édité]"));
		}
		ui->m_conductor_pb->setText(text);
		m_worker.m_conductor_properties =
				m_worker.invalidConductorProperties();
	}
}

/**
	@brief SearchAndReplaceWidget::on_m_advanced_replace_pb_clicked
	Open the advanced editor.
*/
void SearchAndReplaceWidget::on_m_advanced_replace_pb_clicked()
{
	replaceAdvancedDialog *dialog = new replaceAdvancedDialog(
				m_worker.m_advanced_struct,
				this);
	int result = dialog->exec();

	if (result == QDialogButtonBox::AcceptRole)
	{
		QString text = ui->m_advanced_replace_pb->text();
		if (!text.endsWith(tr(" [édité]"))) {
			text.append(tr(" [édité]"));
		}
		ui->m_advanced_replace_pb->setText(text);
		m_worker.m_advanced_struct = dialog->advancedStruct();
	}
	else if (result == QDialogButtonBox::ResetRole)
	{
		QString text = ui->m_advanced_replace_pb->text();
		if (text.endsWith(tr(" [édité]"))) {
			text.remove(tr(" [édité]"));
		}
		ui->m_advanced_replace_pb->setText(text);
		m_worker.m_advanced_struct = advancedReplaceStruct();
	}
}
