/*
	Author: Marco Costalba (C) 2005-2006

	Copyright: See COPYING file that comes with this distribution

*/
#ifndef DOMAIN_H
#define DOMAIN_H

#include <qobject.h>
#include "common.h"

#define UPDATE_DOMAIN(x)    QApplication::postEvent(x, new UpdateDomainEvent(false))
#define UPDATE()            QApplication::postEvent(this, new UpdateDomainEvent(false))
#define UPDATE_DM_MASTER(x) QApplication::postEvent(x, new UpdateDomainEvent(true))

class Domain;
class MainImpl;
class Git;

class UpdateDomainEvent : public QCustomEvent {
public:
	UpdateDomainEvent(bool fromMaster)
	: QCustomEvent(fromMaster ? QGit::UPD_DM_MST_EV : QGit::UPD_DM_EV) {}
};

class StateInfo {
public:
	StateInfo() { clear(); }
	StateInfo& operator=(const StateInfo& newState);
	bool operator==(const StateInfo& newState) const;
	bool operator!=(const StateInfo& newState) const;
	void clear();
	const QString sha(bool n = true) const { return (n ? curS.sha : prevS.sha); };
	const QString fileName(bool n = true) const { return (n ? curS.fn : prevS.fn); };
	const QString diffToSha(bool n = true) const {return(n ? curS.dtSha : prevS.dtSha); };
	bool selectItem(bool n = true) const { return( n ? curS.sel : prevS.sel); };
	bool isMerge(bool n = true) const { return( n ? curS.isM : prevS.isM); };
	bool allMergeFiles(bool n = true) const { return( n ? curS.allM : prevS.allM); };
	void setSha(const QString& s) { if (isLocked) nextS.sha = s; else curS.sha = s; };
	void setFileName(const QString& s) { if (isLocked) nextS.fn = s; else curS.fn = s; };
	void setDiffToSha(const QString& s) { if (isLocked) nextS.dtSha = s; else curS.dtSha = s; };
	void setSelectItem(bool b) { if (isLocked) nextS.sel = b; else curS.sel = b; };
	void setIsMerge(bool b) { if (isLocked) nextS.isM = b; else curS.isM = b; };
	void setAllMergeFiles(bool b) { if (isLocked) nextS.allM = b; else curS.allM = b; };

private:
	friend class Domain;

	bool requestPending() const { return (!nextS.sha.isEmpty() && (nextS != curS)); };
	void setLock(bool b) { isLocked = b; if (b) nextS = curS; };
	void commit() { prevS = curS; };
	void rollBack() {
		if (nextS == curS)
			nextS.clear(); // invalidate to avoid infinite loop
		curS = prevS;
	};
	bool flushQueue() {
		if (requestPending()) {
			curS = nextS;
			return true;
		}
		return false;
	};

	class S {
	public:
		S() { clear(); }
		~S() { clear(); } // FIXME
		void clear();
		S& operator=(const S& newState);
		bool operator==(const S& newState) const;
		bool operator!=(const S& newState) const;

		QString sha;
		QString fn;
		QString dtSha;
		bool sel;
		bool isM;
		bool allM;
	};
	S curS;  // current state, what returns from StateInfo::sha()
	S prevS; // previous good state, used to rollBack in case state update fails
	S nextS; // next queued state, waiting for current update to finish
	bool isLocked;
};

class Domain: public QObject {
Q_OBJECT
public:
	Domain() {}
	Domain(MainImpl* m, Git* git);
	MainImpl* m() const;
	bool isReadyToDrag() const { return readyToDrag; }
	bool setReadyToDrag(bool b);
	bool isDragging() const { return dragging; }
	bool setDragging(bool b);
	bool isDropping() const { return dropping; }
	void setDropping(bool b) { dropping = b; }
	bool isLinked() const { return linked; }
	int tabPos() const { return tabPosition; }
	virtual QWidget* tab() = 0;

	StateInfo st;

signals:
	void updateRequested(StateInfo newSt);

public slots:
	void on_closeAllTabs();

protected slots:
	virtual void on_contextMenu(const QString&, int);
	void on_tabClosed(int);
	void on_updateRequested(StateInfo newSt);

protected:
	virtual void customEvent(QCustomEvent* e);
	virtual bool doUpdate() = 0;
	void linkDomain(Domain* d);
	void unlinkDomain(Domain* d);

	Git* git;
	bool busy;
	int tabPosition;

private:
	void populateState();
	void update(bool fromMaster);
	bool flushQueue();
	void sendPopupEvent();

	bool readyToDrag;
	bool dragging;
	bool dropping;
	bool linked;
	int popupType;
	QString popupData;
	QString statusBarRequest;
};

#endif
