/*******************************************************************************************************
DkNoMacs.h
Created on:	26.10.2014
 
nomacs is a fast and small image viewer with the capability of synchronizing multiple instances
 
Copyright (C) 2011-2014 Markus Diem <markus@nomacs.org>
Copyright (C) 2011-2014 Stefan Fiel <stefan@nomacs.org>
Copyright (C) 2011-2014 Florian Kleber <florian@nomacs.org>

This file is part of nomacs.

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

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

*******************************************************************************************************/
#pragma once

#pragma warning(push, 0)	// no warnings from includes - begin
#include <QWidget>
#include <QUrl>
#include <QTextEdit>
#include <QPushButton>
#include <QItemSelection>
#pragma warning(pop)		// no warnings from includes - end

#include "DkImageContainer.h"
#include "DkImageLoader.h"
#include "DkBaseWidgets.h"
#include "DkBatchInfo.h"
#include "DkManipulators.h"

// Qt defines
class QListView;
class QVBoxLayout;
class QLabel;
class QFileInfo;
class QComboBox;
class QLineEdit;
class QSpinBox;
class QDoubleSpinBox;
class QGridLayout;
class QCheckBox;
class QButtonGroup;
class QRadioButton;
class QDialogButtonBox;
class QProgressBar;
class QTabWidget;
class QListWidget;
class QStackedLayout;
class QStandardItem;
class QStandardItemModel;

namespace nmc {

// TODO: these enums are global - they should be put into the respective classes
enum fileNameTypes {
	fileNameTypes_fileName,
	fileNameTypes_Number,
	fileNameTypes_Text,

	fileNameTypes_end
};

enum fileNameWidget {
	fileNameWidget_type,
	fileNameWidget_input1,
	fileNameWidget_input2,
	fileNameWidget_plus,
	fileNameWidget_minus,

	fileNameWidget_end
};

// nomacs defines
class DkPluginBatch;
class DkManipulatorBatch;
class DkBatchProcessing;
class DkBatchTransform;
class DkBatchContent;
class DkButton;
class DkThumbScrollWidget;
class DkImageLoader;
class DkExplorer;
class DkDirectoryEdit;
class DkListWidget;
class DkBatchConfig;
class DkProgressBar;
class DkSettingsWidget;
class DkBatchPluginInterface;
class DkRectWidget;

class DkBatchContent {

public:
	virtual bool hasUserInput() const = 0;
	virtual bool requiresUserInput() const = 0;
	virtual void applyDefault() = 0;
};

class DkBatchTabButton : public QPushButton {
	Q_OBJECT

public:
	DkBatchTabButton(const QString& title, const QString& info = QString(), QWidget* parent = 0);

	QString info() const;

public slots:
	void setInfo(const QString& info);

signals:
	void infoChanged(const QString& msg) const;

protected:
	void paintEvent(QPaintEvent* event) override;

	QString mInfo;
};

class DkBatchContainer : public QObject {
	Q_OBJECT

public:
	DkBatchContainer(const QString& titleString, const QString& headerString, QWidget* parent = 0);
	
	void setContentWidget(QWidget* batchContent);
	QWidget* contentWidget() const;
	DkBatchContent* batchContent() const;
	DkBatchTabButton* headerWidget() const;

public slots:
	void showContent(bool show) const;

signals:
	void showSignal() const;

protected:
	virtual void createLayout();

	DkBatchTabButton* mHeaderButton = 0;
	DkBatchContent* mBatchContent = 0;
};

class DkInputTextEdit : public QTextEdit {
	Q_OBJECT

public:
	DkInputTextEdit(QWidget* parent = 0);

	QStringList getFileList() const;
	void appendDir(const QString& newDir, bool recursive = false);
	void insertFromMimeData(const QMimeData *src);
	void clear();

signals:
	void fileListChangedSignal() const;

public slots:
	void appendFiles(const QStringList& fileList);

protected:
	void dropEvent(QDropEvent *event);
	void dragEnterEvent(QDragEnterEvent *event);
	void dragMoveEvent(QDragMoveEvent *event);
	void appendFromMime(const QMimeData* mimeData, bool recursive = false);

	QList<int> mResultList;
};

class DkBatchInput : public QWidget, public DkBatchContent  {
	Q_OBJECT

public:

	enum {
		tab_thumbs = 0,
		tab_text_input,
		tab_results,

		tab_end
	};

	DkBatchInput(QWidget* parent = 0, Qt::WindowFlags f = 0);

	QString getDir() const;
	QStringList getSelectedFiles() const;
	QStringList getSelectedFilesBatch();
	DkInputTextEdit* getInputEdit() const;

	virtual bool hasUserInput() const {return mHUserInput;};
	virtual bool requiresUserInput() const {return mRUserInput;};
	virtual void applyDefault();

	void changeTab(int tabIdx) const;
	void startProcessing();
	void stopProcessing();
	void setResults(const QStringList& results);

public slots:
	void setDir(const QString& dirPath);
	void browse();
	void updateDir(QVector<QSharedPointer<DkImageContainerT> >);
	void setVisible(bool visible);
	void parameterChanged();
	void selectionChanged();
	void setFileInfo(QFileInfo file);

signals:
	void updateDirSignal(QVector<QSharedPointer<DkImageContainerT> >) const;
	void newHeaderText(const QString&) const;
	void updateInputDir(const QString&) const;
	void changed() const;

protected:
	virtual void createLayout();

	QString mCDirPath;
	QListView* mFileWidget = 0;
	DkThumbScrollWidget* mThumbScrollWidget = 0;
	DkInputTextEdit* mInputTextEdit = 0;
	QTextEdit* mResultTextEdit = 0;
	DkExplorer* mExplorer = 0;
	DkDirectoryEdit* mDirectoryEdit = 0;
	QTabWidget* mInputTabs = 0;
	QSharedPointer<DkImageLoader> mLoader = QSharedPointer<DkImageLoader>(new DkImageLoader());

private:
	bool mHUserInput = false;
	bool mRUserInput = false;

};

class DkFilenameWidget : public QWidget {
	Q_OBJECT

public:	
	DkFilenameWidget(QWidget* parent = 0);
	void enableMinusButton(bool enable);
	void enablePlusButton(bool enable);
	bool hasUserInput() const {return hasChanged;};
	QString getTag() const;
	bool setTag(const QString& tag);

signals:
	void plusPressed(DkFilenameWidget*) const;
	void minusPressed(DkFilenameWidget*) const;
	void changed() const;

private slots:
	void typeCBChanged(int index);
	void pbPlusPressed();
	void pbMinusPressed();
	void checkForUserInput();
	void digitCBChanged(int index);

private:
	void createLayout();
	void showOnlyText();
	void showOnlyNumber();
	void showOnlyFilename();

	QComboBox* mCbType = 0;
		
	QLineEdit* mLeText = 0;
	QComboBox* mCbCase = 0;

	QSpinBox* mSbNumber = 0;
	QComboBox* mCbDigits = 0;
		
	QPushButton* mPbPlus = 0;
	QPushButton* mPbMinus = 0;

	QGridLayout* mLayout = 0;

	bool hasChanged = false;
};

class DkBatchOutput : public QWidget, public DkBatchContent {
Q_OBJECT

public:
	DkBatchOutput(QWidget* parent = 0, Qt::WindowFlags f = 0);

	virtual bool hasUserInput() const;
	virtual bool requiresUserInput() const {return mRUserInput;};
	void applyDefault();
	void loadProperties(const DkBatchConfig& config);

	DkSaveInfo::OverwriteMode overwriteMode() const;
	int getCompression() const;
	bool useInputDir() const;
	bool deleteOriginal() const;
	QString getOutputDirectory();
	QString getFilePattern();
	void loadFilePattern(const QString& pattern);
	void setExampleFilename(const QString& exampleName);

signals:
	void newHeaderText(const QString&) const;
	void changed() const;

public slots:
	void setInputDir(const QString& dirPath);

protected slots:
	void browse();
	void plusPressed(DkFilenameWidget* widget, const QString& tag = QString());
	void minusPressed(DkFilenameWidget* widget);
	void extensionCBChanged(int index);
	void parameterChanged();
	void updateFileLabelPreview();
	void useInputFolderChanged(bool checked);
	void setDir(const QString& dirPath, bool updateLineEdit = true);

protected:
	virtual void createLayout();
	DkFilenameWidget* createFilenameWidget(const QString& tag = QString());
	void addFilenameWidget(const QString& tag = QString());

private:

	bool mHUserInput = false;
	bool mRUserInput = false;
	QString mOutputDirectory;
	QString mInputDirectory;
	QVector<DkFilenameWidget*> mFilenameWidgets;
	DkDirectoryEdit* mOutputlineEdit = 0;
	QVBoxLayout* mFilenameVBLayout = 0;
	QCheckBox* mCbOverwriteExisting = 0;
	QCheckBox* mCbDoNotSave = 0;
	QCheckBox* mCbUseInput = 0;
	QCheckBox* mCbDeleteOriginal = 0;
	QPushButton* mOutputBrowseButton = 0;

	QComboBox* mCbExtension = 0;
	QComboBox* mCbNewExtension = 0;
	QSpinBox* mSbCompression = 0;
	QLabel* mOldFileNameLabel = 0;
	QLabel* mNewFileNameLabel = 0;
	QString mExampleName = 0;

};

class DkProfileSummaryWidget : public DkWidget {
	Q_OBJECT

public:
	DkProfileSummaryWidget(QWidget* parent = 0);

	void setProfile(const QString& name, const DkBatchConfig& config);

public slots:
	void on_deleteButton_clicked();
	void on_updateButton_clicked();
	void on_exportButton_clicked();

signals:
	void deleteCurrentProfile() const;
	void updateCurrentProfile() const;
	void exportCurrentProfile() const;

protected:
	void createLayout();

	QLabel* mTitle = 0;
	QLabel* mNumFiles = 0;
	QLabel* mOutputDir = 0;
	QLabel* mFunctions = 0;
};

class DkProfileWidget : public QWidget, public DkBatchContent {
	Q_OBJECT

public:
	DkProfileWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);

	//void transferProperties(QSharedPointer<DkResizeBatch> batchResize) const;
	bool hasUserInput() const;
	bool requiresUserInput() const;
	void applyDefault();
	void profileSaved(const QString& profileName);

public slots:
	void on_profileList_itemSelectionChanged();
	void on_saveButton_clicked();
	void on_resetButton_clicked();

	void updateCurrentProfile();
	void deleteCurrentProfile();
	void exportCurrentProfile();

signals:
	void newHeaderText(const QString& txt) const;
	void loadProfileSignal(const QString& profilePath) const;
	void saveProfileSignal(const QString& profilePath) const;
	void applyDefaultSignal() const;

protected:
	void createLayout();
	void saveProfile();
	void updateProfileList();
	void changeProfile(const QString& profileName);
	QString currentProfile() const;
	void loadDefaultProfile();

	QListWidget* mProfileList = 0;
	DkProfileSummaryWidget* mSummary = 0;
};


#ifdef WITH_PLUGINS
class DkBatchPluginWidget : public QWidget, public DkBatchContent {
	Q_OBJECT

public:
	DkBatchPluginWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);

	void transferProperties(QSharedPointer<DkPluginBatch> batchPlugin) const;
	bool loadProperties(QSharedPointer<DkPluginBatch> batchPlugin);
	bool hasUserInput() const;
	bool requiresUserInput() const;
	void applyDefault();
	void setSettingsPath(const QString& settingsPath);

public slots:
	void itemChanged(QStandardItem * item);
	void selectionChanged(const QItemSelection &selected);

	void changeSetting(const QString& key, const QVariant& value, const QStringList& parentList) const;
	void removeSetting(const QString& key, const QStringList& parentList) const;

signals:
	void newHeaderText(const QString& txt) const;

public slots:
	void updateHeader() const;

protected:
	void createLayout();
	void addPlugins(QStandardItemModel* model) const;
	void selectPlugin(const QString& pluginName);
	QStringList selectedPlugins(bool selected = true) const;
	QSharedPointer<QSettings> settings() const;

	QStandardItemModel* mModel = 0;
	DkSettingsWidget* mSettingsEditor = 0;
	QLabel* mSettingsTitle = 0;
	QSharedPointer<QSettings> mSettings;
	DkBatchPluginInterface* mCurrentPlugin = 0;
};
#endif

class DkBatchManipulatorWidget : public QWidget, public DkBatchContent {
	Q_OBJECT

public:
	DkBatchManipulatorWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);

	void transferProperties(QSharedPointer<DkManipulatorBatch> batchPlugin) const;
	bool loadProperties(QSharedPointer<DkManipulatorBatch> batchPlugin);
	bool hasUserInput() const;
	bool requiresUserInput() const;
	void applyDefault();
	void setExampleFile(const QString& filePath);

public slots:
	void itemChanged(QStandardItem * item);
	void selectionChanged(const QItemSelection &selected);
	void selectManipulator(QSharedPointer<DkBaseManipulator> mpl);
	void selectManipulator();

signals:
	void newHeaderText(const QString& txt) const;

public slots:
	void updateHeader() const;

protected:
	void createLayout();
	void addSettingsWidgets(DkManipulatorManager& manager);
	void setManager(const DkManipulatorManager& manager);

	QStandardItemModel* mModel = 0;
	DkManipulatorManager mManager;
	QVector<QWidget*> mMplWidgets;
	QVBoxLayout* mSettingsLayout = 0;
	QLabel* mSettingsTitle = 0;
	QLabel* mPreviewLabel = 0;
	QString mPreviewPath;
	QImage mPreview;
	int mMaxPreview = 300;
};

class DkBatchTransformWidget : public QWidget, public DkBatchContent {
	Q_OBJECT

public:
	DkBatchTransformWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);

	void transferProperties(QSharedPointer<DkBatchTransform> batchTransform) const;
	bool loadProperties(QSharedPointer<DkBatchTransform> batchTransform);
	bool hasUserInput() const;
	bool requiresUserInput() const;
	void applyDefault();

public slots:
	void updateHeader() const;
	void modeChanged();

signals:
	void newHeaderText(const QString& txt) const;

protected:
	void createLayout();
	int getAngle() const;
	QRect cropRect() const;

	QButtonGroup* mRotateGroup = 0;
	QRadioButton* mRbRotate0 = 0;
	QRadioButton* mRbRotateLeft = 0;
	QRadioButton* mRbRotateRight = 0;
	QRadioButton* mRbRotate180 = 0;

	QCheckBox* mCbCropMetadata = 0;

	QCheckBox* mCbCropRectangle = 0;
	DkRectWidget* mCropRectWidget = 0;

	QComboBox* mResizeComboMode;
	QComboBox* mResizeComboProperties;
	QSpinBox* mResizeSbPx;
	QDoubleSpinBox* mResizeSbPercent;

};

class DkBatchButtonsWidget : public DkWidget {
	Q_OBJECT

public:
	DkBatchButtonsWidget(QWidget* parent = 0);

	void setPaused(bool paused = true);
	QPushButton* logButton();
	QPushButton* playButton();

signals:
	void playSignal(bool play = true) const;
	void showLogSignal() const;

protected:
	void createLayout();

	QPushButton* mPlayButton = 0;
	QPushButton* mLogButton = 0;
};

class DkBatchInfoWidget : public DkWidget {
	Q_OBJECT

public:
	DkBatchInfoWidget(QWidget* parent);

	enum InfoMode {
		info_message,
		info_warning,
		info_critical,

		info_end
	};

public slots:
	void setInfo(const QString& message, const DkBatchInfoWidget::InfoMode& mode = DkBatchInfoWidget::InfoMode::info_message);

protected:
	void createLayout();

	QLabel* mInfo = 0;
	QLabel* mIcon = 0;
};

class DkBatchWidget : public DkWidget {
	Q_OBJECT

public:
	DkBatchWidget(const QString& currentDirectory = QString(), QWidget* parent = 0);
	~DkBatchWidget();

	enum batchWidgets {
		batch_input,
		batch_manipulator,
		batch_transform,
		batch_plugin,
		batch_output,
		batch_profile,

		batchWidgets_end
	};

	bool cancel();

public slots:
	void toggleBatch(bool start);
	void widgetChanged();
	void showLog();
	void processingFinished();
	void updateProgress(int progress);
	void updateLog();
	void setSelectedFiles(const QStringList& selFiles);
	void changeWidget(DkBatchContainer* widget = 0);
	void nextTab();
	void previousTab();
	void saveProfile(const QString& profilePath) const;
	void loadProfile(const QString& profilePath);
	void applyDefault();

signals:
	void infoSignal(const QString& message, const DkBatchInfoWidget::InfoMode& mode = DkBatchInfoWidget::InfoMode::info_message) const;

protected:
	void createLayout();
	DkBatchConfig createBatchConfig(bool strict = true) const;
	void startProcessing();
	void stopProcessing();
	void startBatch();
	DkBatchInput* inputWidget() const;
	DkBatchOutput* outputWidget() const;
	DkBatchManipulatorWidget* manipulatorWidget() const;
	DkProfileWidget* profileWidget() const;
	DkBatchTransformWidget* transformWidget() const;

#ifdef WITH_PLUGINS
	DkBatchPluginWidget* pluginWidget() const;
#endif

	QVector<DkBatchContainer*> mWidgets;
		
	QStackedLayout* mCentralLayout = 0;
	int mCurrentIndex = 0;

	QString mCurrentDirectory;

	DkBatchProcessing* mBatchProcessing = 0;

	// info/controls
	DkBatchInfoWidget* mInfoWidget = 0;
	DkProgressBar* mProgressBar = 0;
	DkBatchButtonsWidget* mButtonWidget = 0;
	
	QTimer mLogUpdateTimer;
	bool mLogNeedsUpdate = false;

	// title
	QLabel* mContentTitle = 0;
	QLabel* mContentInfo = 0;
};

}
