/*****************************************************************************/
/*  Copyright 2019 WSL Institute for Snow and Avalanche Research  SLF-DAVOS  */
/*****************************************************************************/
/* This file is part of INIshell.
   INIshell 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 3 of the License, or
   (at your option) any later version.

   INIshell 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 INIshell.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "MainPanel.h"
#include "SectionButton.h"
#include "WorkflowPanel.h"
#include "src/main/colors.h"
#include "src/main/constants.h"
#include "src/main/inishell.h"
#include "src/main/os.h"
#include "src/main/settings.h"
#include "src/main/XMLReader.h"
#include "src/gui_elements/gui_elements.h"

#include <QGroupBox>
#include <QHBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QRegularExpression>
#include <QSplitter>
#include <QVBoxLayout>

#ifdef DEBUG
	#include <iostream>
#endif

/**
 * @class MainPanel
 * @brief Constructor for the main panel, i. e. the tab widget with scroll area children.
 * @param[in] parent The parent widget (the main window).
 */
MainPanel::MainPanel(QWidget *parent) : QWidget(parent)
{
	/* widget(s) on the left side and main tab bar */
	workflow_panel_ = new WorkflowPanel;
	workflow_stack_ = new QStackedWidget;
	section_tab_ = new SectionTab;
	workflow_stack_->addWidget(section_tab_);

	/* main layout */
	auto *main_layout( new QHBoxLayout );
	splitter_ = new QSplitter;
	splitter_->addWidget(workflow_panel_); //allow to resize with mouse
	splitter_->addWidget(workflow_stack_);
	setSplitterSizes();
	main_layout->addWidget(splitter_);
	this->setLayout(main_layout);

	displayInfo(); //startup info text
}

/**
 * @brief Display some info and the SLF logo on program start.
 */
void MainPanel::displayInfo()
{
	static const QString message(tr("<br> \
	    &nbsp;&nbsp;Welcome to <b>INIshell</b>, a dynamic graphical user interface builder to manage INI files.<br> \
	    &nbsp;&nbsp;Double-click an application to the left to begin.<br><br> \
	    &nbsp;&nbsp;For help, click \"Help\" in the menu and visit <a href=\"https://models.slf.ch/p/inishell-ng/\">INIshell's project page</a>.<br> \
	    &nbsp;&nbsp;There, you will also find the well-documented <a href=\"https://models.slf.ch/p/inishell-ng/source/tree/master/\">source code</a>.<br> \
	    &nbsp;&nbsp;If you don't know where to begin, press %1. \
	").arg(os::getHelpSequence()));

	auto *info_scroll( new QScrollArea );
	info_scroll->setWidgetResizable(true);

	/* SLF logo */
	QLabel *info_label( new QLabel );
	info_label->setAlignment(Qt::AlignBottom | Qt::AlignRight);
	info_label->setStyleSheet("QLabel {background-color: " + colors::getQColor("app_bg").name() + "}");
	const QPixmap logo(":/icons/slf_desaturated.svg");
	info_label->setPixmap(logo);

	/* info text */
	QLabel *info_text( new QLabel(info_label) );
	info_text->setTextFormat(Qt::RichText);
	info_text->setTextInteractionFlags(Qt::TextBrowserInteraction);
	info_text->setOpenExternalLinks(true);
	info_text->setText(message);
	info_scroll->setWidget(info_label);

	section_tab_->addTab(info_scroll, "Info");
}

/**
 * @brief Query the splitter's current sizes.
 * @return Current sizes of the splitter's panels.
 */
QList<int> MainPanel::getSplitterSizes() const
{
	return splitter_->sizes();
}

/**
 * @brief Set sizes of the panels the splitter manages.
 * @param[in] sizes The sizes the splitter should give to its panels.
 * @param[in] enforce_visible Show the workflow with default size if its size is saved
 * to be zero?
 */
void MainPanel::setSplitterSizes(QList<int> sizes, bool enforce_visible)
{
	splitter_->setStretchFactor(0, Cst::proportion_workflow_horizontal_percent);
	splitter_->setStretchFactor(1, 100 - Cst::proportion_workflow_horizontal_percent);
	if (!sizes.isEmpty()) {
		splitter_->setSizes(sizes);
		return;
	}
	bool workflow_success, main_success;
	const int width_workflow = getSetting("auto::sizes::splitter_workflow", "size").toInt(&workflow_success);
	const int width_mainpanel = getSetting("auto::sizes::splitter_mainpanel", "size").toInt(&main_success);
	if (workflow_success && main_success && workflow_success && !(enforce_visible && width_workflow < 20) )
		splitter_->setSizes(sizes << width_workflow << width_mainpanel);
	else //reset to default
		splitter_->setSizes(QList<int>( {Cst::width_inishell_default * Cst::proportion_workflow_horizontal_percent / 100,
			Cst::width_inishell_default * (100 - Cst::proportion_workflow_horizontal_percent) / 100} )); //has constraints anyway - set something big
}

/**
 * @brief Remove all GUI elements.
 * @details This function clears the main GUI itself, i. e. all panels read from XML, including
 * the workflow panels.
 */
void MainPanel::clearGuiElements()
{
	section_tab_->clear();
	workflow_panel_->clearXmlPanels();
}

/**
 * @brief Reset the whole GUI to default values.
 */
void MainPanel::clearGui(const bool &set_default)
{
	clearDynamicPanels<Replicator>(); //clear special panels (the ones that can produce INI keys)
	clearDynamicPanels<Selector>(); //cf. notes there
	const QList<Atomic *> panel_list( section_tab_->findChildren<Atomic *>() ); //clear all others
	for (auto &panel : panel_list)
		panel->clear(set_default);
}

/**
 * @brief Clear panels which can add/remove children at will.
 * @details This function clears panels that have the ability to create and more importantly delete
 * an arbitrary number of child panels. Those stand for a group of INI keys rather than a single one,
 * and can create child panels for INI keys such as "STATION1, STATION2, ...".
 */
template <class T>
void MainPanel::clearDynamicPanels()
{
	/*
	 * Fetching a list of suitable panels and iterating through them does not work out
	 * because clearing one dynamic panel can delete another one, which means the pointers
	 * we receive could be invalidated.
	 * So, we look for the first dynamic panel with children, clear that, and start the
	 * search again until there are no more dynamic panels with children found.
	 */
	const QList<T *> panel_list( section_tab_->findChildren<T *>() );
	for (auto &pan : panel_list) {
		//the panels themselves are not deleted, so we stop if it's empty:
		if (pan->count() > 0) {
			pan->clear();
			clearDynamicPanels<T>(); //repeat until all dynamic panels are empty
			return;
		}
	}
}
//this is a list of panels that can produce and delete an aribtrary number of child panels:
template void MainPanel::clearDynamicPanels<Selector>();
template void MainPanel::clearDynamicPanels<Replicator>();
