Pith - whittle
#pragma once
#include "core/FilterSettings.h"
#include "core/MergeController.h"
#include "core/ScanController.h"
#include "ui/DiffTableModel.h"
#include "ui/FolderTreeModel.h"
#include "ui/PanelTableView.h"
#include <functional>
#include <QMainWindow>
#include <QPointer>
#include <QSet>

class QTreeView;
class QComboBox;   class QAction; class QTimer;
class QPushButton; class QSplitter; class QShowEvent; class QResizeEvent;
class QProgressBar;
class QStyle;

namespace whittle {

	class MainWindow : public QMainWindow {
		Q_OBJECT
	public:
		explicit MainWindow(QWidget* parent = nullptr);

	protected:
		void showEvent(QShowEvent*) override;
		void resizeEvent(QResizeEvent*) override;


		private slots:
		void onScanButton();
		void onScan();
		void onAnalyze();
		void onUsbChanged();
		void onReset();
		void onMerge();
		void onOpenDrive();
		void onEject();
		void onSettings();
		void onSaveScheme();
		void onSchemeActivated(int index);
		void refreshUsbList();
		void pollUsbDrives();
		void onProgress(int folders, int files, qint64 bytes);
		void onResults(whittle::DiffResult result,
			whittle::FileMap sourceMap, whittle::FileMap usbMap);
		void onUsbContextMenu(const QPoint& pos);
		void onMidContextMenu(const QPoint& pos);
		void onCanceled();
		void cancelScan();
		void onMergeProgress(QString rel, qint64 bytesDone, int filesDone);
		void onMergeCopied(QString rel);
		void onMergeRemoved(QString rel, bool ok);
		void onMergeSkipped(QString rel, int reason);
		void onMergeDeviceLost();
		void onMergeDone(bool canceled);
		void cancelMerge();

	private:
		using UdisksHandler = std::function<void(int exitCode, QString output)>;
		QWidget* buildLeftPanel();
		QWidget* buildMidPanel();
		QWidget* buildUsbPanel();
		void refreshSchemeCombo(const QString& select = QString());
		void applyScheme(const QString& name);
		QString schemeLabel(const QString& name) const;
		void updateSchemeLabel();
		void setSchemeDirty(bool dirty);
		void clearLoadedScheme();
		void startScan(const QStringList& roots, bool recursive, const QString& usb);
		void applySplitProportions();
		void runUdisks(const QStringList& args, UdisksHandler done);
		void finishEject(const QString& message, bool unmounted);
		void startMerge(QList<FileRecord> items, QList<FileRecord> deletes);
		QList<FileRecord> selectedUsbRecords() const;
		QList<FileRecord> usbOnlyRecords() const;
		void deleteUsbRecords(const QList<FileRecord>& items);
		void recompareFromCache();
		void updateCounts();
		void recountChecked();
		QString countsText() const;
		void setBarDormant();
		void setBarScanning();
		void retireMergedRow(const QString& rel);
		void expandToPaths(const QStringList& paths);
		void stepExpand();
		bool revealPath(const QString& path);
		void updateActionStates();
		void setScanning(bool on);
		QString currentUsbMount() const;
		void clearReport();

		FolderTreeModel* m_treeModel = nullptr;
		DiffTableModel*  m_midModel  = nullptr;
		DiffTableModel*  m_usbModel  = nullptr;
		QTreeView*  m_tree      = nullptr;
		PanelTableView* m_midView = nullptr;
		PanelTableView* m_usbView = nullptr;
		QComboBox*  m_schemeCombo = nullptr;
		QWidget*    m_schemeHeader  = nullptr;
		QPushButton* m_openBtn     = nullptr;
		QPushButton* m_ejectBtn    = nullptr;
		QPushButton* m_saveSchemeBtn = nullptr;
		QPushButton* m_resetBtn      = nullptr;
		QSplitter*  m_split = nullptr;
		QSplitter*  m_panelSplit = nullptr;
		QWidget*    m_midHeader = nullptr;
		QProgressBar* m_progress = nullptr;
		QPushButton* m_scanBtn  = nullptr;
		QPushButton* m_analyzeBtn = nullptr;
		QPushButton* m_mergeBtn = nullptr;
		QComboBox*  m_usbCombo  = nullptr;
		QWidget*    m_usbHeader   = nullptr;
		QPushButton* m_settingsBtn = nullptr;
		QPushButton* m_quitBtn     = nullptr;
		QStyle*     m_menuStyle   = nullptr;

		QTimer*     m_usbPoll = nullptr;
		QStringList m_usbSignature;
		bool        m_usbFirstBuild = true;
		bool        m_userSplit = false;
		bool        m_ejecting = false;

		QString m_loadedScheme;
		bool    m_schemeDirty     = false;
		bool    m_applyingScheme  = false;

		QStringList m_expandPending;
		QString     m_expandScrollTo;

		QPointer<ScanController> m_controller;
		bool m_scanning = false;

		QPointer<MergeController> m_merger;
		bool m_merging = false;
		bool m_deviceLost = false;
		QSet<QString> m_mergeSkipped;
		qint64 m_mergeTotalBytes = 0;
		int    m_mergeErrors  = 0;
		QString m_mergeCurrent;

		FileMap m_sourceMap;
		FileMap m_usbMap;
		QSet<QString> m_diffPaths;

		QList<FileRecord> m_lastMerge;
		QString m_lastUsbMount;
		QStringList m_lastRoots;
		bool        m_lastRecursive = true;
		QSet<QString> m_lastExcluded;
		QStringList m_pendingRoots;
		bool        m_pendingRecursive = true;
		QSet<QString> m_pendingExcluded;
		QString     m_pendingUsb;

		bool          m_postMergeMode = false;
		QSet<QString> m_postMergeSkipped;

		enum class Report { None, Scan, Merge };
		Report        m_report = Report::None;
		QStringList   m_mergedFiles;
		QStringList   m_deletedFiles;
		QHash<QString, QList<FileRecord>> m_deleteFiles;

		int m_countFolders = 0;
		int m_countFiles   = 0;
		int m_countNew     = 0;
		int m_countDiff    = 0;
		qint64 m_countBytes = 0;
		qint64 m_newBytes   = 0;
		qint64 m_diffBytes  = 0;

		FilterSettings m_settings;
	};

}