#ifndef KADU_CONFIGURATION_WINDOW_H
#define KADU_CONFIGURATION_WINDOW_H

#include <qdom.h>
#include <qgroupbox.h>
#include <qtabwidget.h>

#include "color_button.h"
#include "hot_key.h"
#include "path_list_edit.h"
#include "select_font.h"
#include "syntax_editor.h"
#include "userbox.h"

class ConfigGroupBox;
class ConfigLineEdit;
class ConfigTab;
class ConfigurationWindow;
class ConfigWidget;

class QGridLayout;
class QGroupBox;
class QVBox;

class ConfigSection : public QObject
{
	Q_OBJECT

	QString name;
	ConfigurationWindow *configurationWindow;
	QString pixmap;

	QListBoxItem *listBoxItem;
	QMap<QString, ConfigTab *> configTabs;

	QTabWidget *mainWidget;

	ConfigTab *configTab(const QString &name, bool create = true);
	bool activated;

private slots:
	void iconThemeChanged();

public:
	ConfigSection(const QString &name, ConfigurationWindow *configurationWindow, QListBoxItem *listBoxItem, QWidget *parentConfigGroupBoxWidget,
		const QString &pixmap);
	~ConfigSection();

	void activate();

	void show() { mainWidget->show(); }
	void hide() { mainWidget->hide(); }

	ConfigGroupBox * configGroupBox(const QString &tab, const QString &groupBox, bool create = true);

	void removedConfigTab(const QString &configTabName);

};

/**
	@class ConfigGroupBox
	@author Vogel
	@short GroupBox w oknie konfiguracji

	GroupBox w oknie konfiguracji definiowany jest przez następujący tag:
	<code>
		&lt;group-box caption="tytuł" id="id"&gr;
			...
		&lt;/group-box&gt;
	</code>

	GroupBox może znajdować się tylko wewnątrz tagu tab. W jego wnętrzu
	mogą zawierać się dowolne tagi widgetów konfigruacyjnych.

	Dodatkowo, GroupBox'a można stworzyć (lub, jeżeli istnieje, uzyskać)
	wywołując funkcję configGroupBox(section, tab, groupBox) z okna konfiguracyjnego.
	Do tak uzyskanego GroupBox'a można dodawać dowolne widgety (@see addWidget,
	@see addWidgets).
 **/
class ConfigGroupBox
{
	QString name;
	ConfigTab *configTab;

	QGroupBox *groupBox;
	QWidget *container;
	QGridLayout *gridLayout;

public:
	ConfigGroupBox(const QString &name, ConfigTab *configTab, QGroupBox *groupBox);
	~ConfigGroupBox();

	QWidget * widget() { return container; }

	/**
		Dodaje widget do GroupBoxa.
		@param widget dodawany widget (nie może być NULL)
		@param fullSpace kiedy true, dodany widget zajmuje całą szerokość GroupBox'a,
			w przeciwnym wypadku tylko prawą jego część
	 **/
	void addWidget(QWidget *widget, bool fullSpace = false);
	/**
		Dodaje 2 widget do GroupBoxa, jeden po lewej stronie, drugi po prawej.
		@param widget1 widget dodawany z lewej strony (zazwyczaj etykieta)
		@param widget2 widget dodawany z prawej strony
	 **/
	void addWidgets(QWidget *widget1, QWidget *widget2);

	/**
		Zwraca true, gdy GroupBox nie posiada w sobie żadnych widgetów.
		@return true, gdy GroupBox nie posiada w sobie żadnych widgetów
	 **/
	bool empty();

};

/**
	@class ConfigurationWindow
	@author Vogel
	@short Widget okna konfigruacyjnego.

	Okno konfiguracyjne tego typu zawiera widgety konfiuracyjne podzielone wg. 3 stopnioej hierarchii.
	Pierwszym stopniem są sekcje reprezentowane przez ListBox'a z lewej strony okna (zawierającego
	ikony i opis tekstowy). Okno zawierające tylko jedną sekcję nie wyświetla ListBox'a.
	Drugim stopniem są karty reprezentowane przez TabWidget'y, trzecim - grupy opisane przez GroupBox'y.

	Okno konfiguracyjne identyfikuje się przez jego nazwę podaną w konstruktorze
	(dzięki tej nazwie każde okno może osobno zapamiętać swoją pozycję i ostatnio
	otwartej karty).

	Okna mogą teoretycznie zawierać dowolne widgety. Każdy z nich, który dodatkowo
	dziedziczy z klasy ConfigWidget, traktowany jest w specjalny sposób. Jego
	metody loadConfiguration i saveConfiguration są wywoływane automatycznie
	przy otwieraniu okna i przy zapisywaniu konfiguracji, dzięki czemu nie jest
	potrzebne żadne 'ręczne' podpinanie się do tych akcji.

	W momencie zapisania konfiguracji wszystkie obiekty w programie będące instancajmi
	klasy @see ConfigurationAwareObject zostaną o tym poinformowane i będą
	mogły zaktualizować swój stan.

	Widgety w oknie mogą być tworzone na 2 sposoby. Pierwszym z nich jest
	pobranie GroupBoxa za pomocą funkcji @see configGroupBox i dodawanie
	do niego widgetów za pomocą jego funkcji addWidget i addWidgets.
	Drugą jest stworzenie plików XML *.ui, które są wczytywane i usuwane dynamicznie
	z pomocą metod @see appendUiFile i @see removeUiFile.

	W tym drugim przypadku stosuje się pliki *.ui o następującej strukturze:

	&lt;configuration-ui&gt;
		&lt;section caption="tytuł" icon="nazwa_ikony"&gt;
			&lt;tab caption="tytuł"&gt;
				&lt;group-box caption="tytuł" id="id"&gt;
					&lt;widget ... /&gt;
				&lt;/group-box&gt;
			&lt;/tab&gt;
		&lt;/section&gt;
	&lt;/configuration-ui&gt;

	Elementy zawierające atrybut id (nie wymagany) - czyli group-box i dowolny widget
	mogą zostać pobrane przez aplikacje za pomocą metody @see widgetById.
	Widgety z modułów powinny posiadać id w postaci: nazwaModułu/nazwaId.

	Atrybut catpion jest wymagany. Możliwe tagi widget są opisane w dokumentacji
	klas Config* (np.: ConfigComboBox).
 **/
class ConfigurationWindow : public QVBox
{
	Q_OBJECT

	QString Name;

	QVBox *left;
	QMap<QString, ConfigSection *> configSections;
	ConfigSection *currentSection;

	QMap<QString, QWidget *> widgets;

	QListBox *sectionsListBox;
	QWidget *container;

	void loadConfiguration(QObject *object);
	void saveConfiguration(QObject *object);

	ConfigSection *configSection(const QString &name);
	ConfigSection *configSection(const QString &icon, const QString &name, bool create = true);

	QValueList<ConfigWidget *> processUiFile(const QString &fileName, bool append = true);
	QValueList<ConfigWidget *> processUiSectionFromDom(QDomNode sectionNode, bool append = true);
	QValueList<ConfigWidget *> processUiTabFromDom(QDomNode tabNode, const QString &iconName, const QString &sectionName, bool append = true);
	QValueList<ConfigWidget *> processUiGroupBoxFromDom(QDomNode groupBoxNode, const QString &sectionName, const QString &tabName, bool append = true);
	ConfigWidget * appendUiElementFromDom(QDomNode uiElementNode, ConfigGroupBox *configGroupBox);
	void removeUiElementFromDom(QDomNode uiElementNode, ConfigGroupBox *configGroupBox);

private slots:
	void updateAndCloseConfig();
	void updateConfig();

	void changeSection(const QString &newSectionName);

	void refreshIcon(const QPixmap &);

protected:
	virtual void keyPressEvent(QKeyEvent *e);

public:
	/**
		Tworzy okno konfiguracyjne o danej nazwie. Nazwa wykorzystywana
		jest przy zapamiętywaniu pozycji okna oraz jego ostatnio
		otwartej karty.
	 **/
	ConfigurationWindow(const QString &name, const QString &caption);
	virtual ~ConfigurationWindow();

	QString name() { return Name; }

	/**
		Pobiera GroupBox'a zawartego w następującej ścieżce XPath:
		/section[@caption=SECTION]/tab[@caption=TAB]/group-box[@caption=GROUPBOX]

		Jeżeli nie istnieje taki group-box to zostanie on stworzony, gdy create == true.
		Dla crate == false zostanie zwrócony NULL.
	 **/
	ConfigGroupBox * configGroupBox(const QString &section, const QString &tab, const QString &groupBox, bool create = true);

	/**
		Jeżeli okno jest ukryte wczytuje wartości elementów z pliku
		konfiguracyjnego i pokazuje okno.
	 **/
	virtual void show();

	/**
		Dodaje do okna konfiguracyjnego widgety wczytane z pliku fileName.
		Gdy load == true wczytuje ich wartości z pliku konfiguracyjnego. W przeciwnym
		wypadku wczytanie wartości nastąpi po wywołaniu metody @see show.
	 **/
	QValueList<ConfigWidget *> appendUiFile(const QString &fileName, bool load = true);
	/**
		Usuwa kontrolki wczytane z danego pliku *.ui.
	 **/
	void removeUiFile(const QString &fileName);

	/**
		Zwraca element opisane id="id" we wczytanych plikach *.ui. Możliwe
		dowolne widgety konfiguracyjne i elementy group-box.
	 **/
	QWidget *widgetById(const QString &id);

	// TODO: make private or sth
	void removedConfigSection(const QString &sectionName);

signals:
	/**
		Sygnał emitowany po naciśnięciu Ok lub Apply ale przed zapisaniem
		wartości do pliku konfiguracyjnego. Nietypowe widgety konfiguracyjne
		powinny się podpiąć pod ten sygnał i po jego uzyskaniu zapisać
		nowe wartości do pliku.
	 **/
	void configurationWindowApplied();

};

#endif // KADU_CONFIGURATION_WINDOW_H
