#include "SectionTab.h"
#include "SectionButton.h"
#include "src/main/colors.h"
#include "src/main/inishell.h"
#include "src/main/XMLReader.h"

#include <QTimer>

/**
 * @class ScrollPanel
 * @brief The main scroll panel controlling all dynamically built panels.
 * @param[in] section The section this panel corresponds to. It could be used for styling but is
 * unused otherwise.
 * @param[in] parent The parent widget (the main tab bar).
 */
ScrollPanel::ScrollPanel(const QString &tab_color, QWidget *parent)
	: QWidget(parent)
{
	/* create a scroll area and put a Group in it */
	main_area_ = new QScrollArea;
	main_area_->setWidgetResizable(true); //best to let the manager do its job - troubles ahead if we don't
	main_area_->setStyleSheet("QScrollArea {border: none}"); //we have one from the tabs already
	main_group_ = new Group(QString(), QString(), false, false, false, false, QString(), QString(), tab_color);
	main_area_->setWidget(main_group_);

	/* main layout */
	auto *layout( new QVBoxLayout );
	layout->addWidget(main_area_);
	this->setLayout(layout);
}

/**
 * @brief Retrieve the main grouping element of a scroll panel (there is one per tab).
 * @return The main Group of this scroll panel holding all widgets.
 */
Group * ScrollPanel::getGroup() const
{
	return this->main_group_;
}

SectionTab::SectionTab(QWidget *parent) : QTabWidget(parent)
{
	this->setTabsClosable(true);
}

/**
 * @brief Retrieve the ScrollPanel of a section.
 * @details Sections are grouped in tab bar elements. For this, they are put in that specific
 * tab panel's ScrollPanel which can be retrieved by this function (to build top level panels into).
 * If the section/tab does not exist yet it is created.
 * @param[in] section The INI section for which to get the corresponding ScrollPanel.
 * @param[in] background_color Background color to give to a whole section that is being created.
 * @param[in] color Font color to give a new section.
 * @param[in] no_create Set to true when parsing an INI to "force" INI sections matching the XML.
 * @param[in] dynamic Is this a dynamic section that is being created?
 * @return The found or newly created ScrollPanel.
 */
ScrollPanel * SectionTab::getSectionScrollArea(const QString &section, const QString &background_color,
	const QString &color, const bool&no_create, const int &insert_idx)
{ //find or create section tab

	for (int ii = 0; ii < this->count(); ++ii) {
		//remove system-dependent keyboard shortcuts markers before comparing:
		//HACK this is needed as a workaround for KDE bug https://bugs.kde.org/show_bug.cgi?id=337491
#if defined Q_OS_LINUX || defined Q_OS_FREEBSD //we assume the kde is only used on Linux and FreeBSD
		if (QString::compare(this->tabBar()->tabText(ii).replace("&", ""), section, Qt::CaseInsensitive) == 0)
			return qobject_cast<ScrollPanel *>(this->widget(ii)); //cast to access members
#else
		if (QString::compare(section_tab_->tabBar()->tabText(ii), section, Qt::CaseInsensitive) == 0)
			return qobject_cast<ScrollPanel *>(section_tab_->widget(ii)); //cast to access members
#endif
	}
	if (!no_create) {
		int new_index = insert_idx;
		if (insert_idx == -1)
			new_index = this->count();
		auto *new_scroll( new ScrollPanel(background_color) );
		this->insertTab(new_index, new_scroll, section);
		this->tabBar()->setTabTextColor(new_index, colors::getQColor(color));

		if (section != "Settings") { //HACK: use a flag for this
			if (insert_idx != -1) {
				auto button = new SectionButton(SectionButton::minus, section);
				this->tabBar()->setTabButton(new_index, QTabBar::RightSide, button);
			} else if (dynamic_sections_.indexOf(QRegExp(section, Qt::CaseInsensitive)) != -1) {
				auto button = new SectionButton(SectionButton::plus, section);
				this->tabBar()->setTabButton(new_index, QTabBar::RightSide, button);
			} else {
				//remove tab close buttons:
				this->tabBar()->tabButton(new_index, QTabBar::RightSide)->deleteLater();
				this->tabBar()->setTabButton(new_index, QTabBar::RightSide, nullptr);
			}
		}
		return new_scroll;
	} else { //don't add sections for keys found in ini files
		return nullptr;
	}
}

/**
 * @brief Retrieve a section's ScrollPanel with known section tab index.
 * @param[in] index The section's index in the tab bar.
 * @return ScrollPanel for the section.
 */
ScrollPanel * SectionTab::getSectionScrollArea(const int &index)
{
	return qobject_cast<ScrollPanel *>(this->widget(index));
}


void SectionTab::setSectionDynamic(const QString &section, const QDomNode &node)
{
	dynamic_sections_.push_back(section);
	dynamic_nodes_.push_back(XMLReader::prependParent(node));
	dynamic_running_indices_.push_back(1); //1st of its kind
}

void SectionTab::spawnDynamicSection(const QString &section, const QString &new_tab_name)
{
	int index = getIndex(section, Qt::CaseSensitive);
	const int dyn_idx = dynamic_sections_.indexOf(QRegExp(section, Qt::CaseInsensitive));
	QString new_name( new_tab_name );
	if (new_name.isNull()) { //from a + button click --> pick new name
		new_name = (section + "%1").arg(dynamic_running_indices_.at(dyn_idx));
		bool found = false;
		for (int ii = index + 1; ii < this->count(); ++ii) {
			if (!this->tabBar()->tabText(ii).startsWith(section, Qt::CaseInsensitive)) {
				index = ii - 1; //order after all dynamic sections of this kind
				found = true;
				break;
			}
		}
		if (!found) //parent panel is the last one --> append
			index = this->count() - 1;
	}
	dynamic_running_indices_[dyn_idx] = dynamic_running_indices_.at(dyn_idx) + 1;

	auto dyn_scroll = getSectionScrollArea(new_name, QString(), "normal", false, index + 1); //dynamic
	recursiveBuild(dynamic_nodes_.at(dyn_idx), dyn_scroll->getGroup(), new_name);
}

ScrollPanel * SectionTab::tryDynamicSection(const QString &section)
{ //check if a section could be a dynamic's child; if yes, create it
	for (int ii = 0; ii < dynamic_sections_.length(); ++ii) {
		if (section.startsWith(dynamic_sections_.at(ii), Qt::CaseInsensitive)) {
			const QString postfix( section.mid(dynamic_sections_.at(ii).length()) );
			bool success;
			const int sec_no = postfix.toInt(&success);
			if (success) {
				spawnDynamicSection(dynamic_sections_.at(ii), section);
				if (sec_no > dynamic_running_indices_.at(ii))
					dynamic_running_indices_[ii] = sec_no + 1; //start at highest index on "+" click
				return getSectionScrollArea(section);
			}
		}
	} //endfor ii
	return nullptr;
}

void SectionTab::removeDynamicSection(const QString &section)
{
	for (int ii = 0; ii < this->tabBar()->count(); ++ii) {
		if (this->tabBar()->tabText(ii) == section) {
			this->widget(ii)->deleteLater();
			break;
		}
	}
}

/**
 * @brief Select tab specified by its caption.
 * @param[in] tab_name The tab/section to select.
 * @return True if the tab could be selected.
 */
bool SectionTab::showTab(const QString &tab_name)
{
	const int idx = getIndex(tab_name);
	if (idx == -1) //not found
		return false;
	this->setCurrentIndex(idx);
	return true;
}

bool SectionTab::showFrame(const QString &tab_name, const QString &frame_name)
{

#ifdef DEBUG
	const bool success = showTab(tab_name);
	if (!success)
		qDebug() << "Help section does not exist:" << tab_name;
#else
	(void) control_panel_->getSectionTab()->showTab(tab_name);
#endif //def DEBUG

	bool found = false;
	const auto parent = this->getSectionScrollArea(tab_name);
	const QList<Group *> panel_list( parent->findChildren<Group *>() );
	for (auto &panel : panel_list) { //run through panels in section
		QString section, key;
		(void) panel->getIniValue(section, key);
		if (QString::compare(key, frame_name, Qt::CaseInsensitive) == 0) {
			const QString id( section + Cst::sep + key );
			//this ID must be set in the help XML:
			auto wid = getMainWindow()->findChild<QGroupBox *>("_primary_" + Atomic::getQtKey(id));
			parent->ensureWidgetVisible(wid); //scroll the ScrollBar until visible

			if (wid->property("stylesheet").toString().isEmpty()) //nothing replaced yet --> store original
				wid->setProperty("stylesheet", wid->styleSheet()); //(to reset with timer)

			QString stylesheet_copy( wid->styleSheet() );
			wid->setStyleSheet(stylesheet_copy.replace( //flash frame border color
				colors::getQColor("frameborder").name(QColor::HexRgb).toLower(),
				colors::getQColor("important").name()));
			QTimer::singleShot(Cst::msg_short_length, this,
				[=]{ wid->setStyleSheet(wid->property("stylesheet").toString()); } );
			found = true;
			break;
		}
	} //endfor panel_list
	this->raise();
	return found;
}

int SectionTab::getIndex(const QString &section, const Qt::CaseSensitivity &sensitivity)
{
	for (int ii = 0; ii < this->count(); ++ii) {
//remove system-dependent keyboard shortcuts markers before comparing:
//HACK this is needed as a workaround for KDE bug https://bugs.kde.org/show_bug.cgi?id=337491
#if defined Q_OS_LINUX || defined Q_OS_FREEBSD //we assume the kde is only used on Linux and FreeBSD
		if (QString::compare(this->tabBar()->tabText(ii).replace("&", ""), section, sensitivity) == 0)
			return ii;
#else
		if (QString::compare(this->tabBar()->tabText(ii), section, sensitivity) == 0)
			return ii;
#endif
	}
	return -1; //not found
}

/**
 * @brief Clear all tab pages.
 * @details Unlike the native QTabWidget::clear(), this here also deletes the pages.
 */
void SectionTab::clear()
{
	while (this->count() > 0) {
		this->widget(0)->deleteLater();
		this->removeTab(0);
	}
	dynamic_sections_.clear();
	dynamic_nodes_.clear();
	dynamic_running_indices_.clear();
}
