// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Authors:
 * David Yip <yipdw@rose-hulman.edu>
 *
 * Copyright (c) 2005 Authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef SEEN_COMPOSITE_UNDO_COMMIT_OBSERVER_H
#define SEEN_COMPOSITE_UNDO_COMMIT_OBSERVER_H

#include "undo-stack-observer.h"

#include <vector>

namespace Inkscape {

struct Event;

/**
 * Aggregates UndoStackObservers for management and triggering in an SPDocument's undo/redo
 * system.
 *
 * Heavily inspired by Inkscape::XML::CompositeNodeObserver.
 */
class CompositeUndoStackObserver : public UndoStackObserver
{
public:

	/**
	 * Structure for tracking UndoStackObservers.
	 */
	struct UndoStackObserverRecord {
	public:
		/**
		 * Constructor.
		 *
		 * \param o Reference to the UndoStackObserver that this UndoStackObserverRecord
		 * will track.
		 */
		UndoStackObserverRecord(UndoStackObserver &o) : to_remove(false), _observer(&o) { }
		bool to_remove;

		/**
		 * Overloaded equality test operator to facilitate usage of STL find algorithms.
		 */
		bool operator==(UndoStackObserverRecord const& _x) const
		{
			return this->_observer == _x._observer;
		}

		/**
		 * Issue a redo event to the UndoStackObserver that is associated with this UndoStackObserverRecord.
		 *
		 * \param log The event log generated by the redo event.
		 */
		void issueRedo(Event* log)
		{
			this->_observer->notifyRedoEvent(log);
		}

		/**
		 * Issue an undo event to the UndoStackObserver that is associated with this
		 * UndoStackObserverRecord.
		 *
		 * \param log The event log generated by the undo event.
		 */
		void issueUndo(Event* log)
		{
			this->_observer->notifyUndoEvent(log);
		}

		/**
		 * Issues a committed event to the UndoStackObserver that is associated with this
		 * UndoStackObserverRecord.
		 *
		 * \param log The event log being committed to the undo stack.
		 */
		void issueUndoCommit(Event* log)
		{
			this->_observer->notifyUndoCommitEvent(log);
		}

		/**
		 * Issue a clear undo event to the UndoStackObserver
		 * that is associated with this
		 * UndoStackObserverRecord.
		 */
		void issueClearUndo()
		{
			this->_observer->notifyClearUndoEvent();
		}
	     
		/**
		 * Issue a clear redo event to the UndoStackObserver
		 * that is associated with this
		 * UndoStackObserverRecord.
		 */
		void issueClearRedo()
		{
			this->_observer->notifyClearRedoEvent();
		}

	private:
		UndoStackObserver *_observer;
	};

	/// A list of UndoStackObserverRecords, used to aggregate multiple UndoStackObservers.
	typedef std::vector<UndoStackObserverRecord> UndoObserverRecordList;

	/**
	 * Constructor.
	 */
	CompositeUndoStackObserver();

    ~CompositeUndoStackObserver() override;

	/**
	 * Add an UndoStackObserver.
	 *
	 * \param observer Reference to an UndoStackObserver to add.
	 */
	void add(UndoStackObserver& observer);

	/**
	 * Remove an UndoStackObserver.
	 *
	 * \param observer Reference to an UndoStackObserver to remove.
	 */
	void remove(UndoStackObserver& observer);

	/**
	 * Notify all registered UndoStackObservers of an undo event.
	 *
	 * \param log The event log generated by the undo event.
	 */
	void notifyUndoEvent(Event* log) override;

	/**
	 * Notify all registered UndoStackObservers of a redo event.
	 *
	 * \param log The event log generated by the redo event.
	 */
	void notifyRedoEvent(Event* log) override;

	/**
	 * Notify all registered UndoStackObservers of an event log being committed to the undo stack.
	 *
	 * \param log The event log being committed to the undo stack.
	 */
	void notifyUndoCommitEvent(Event* log) override;

	void notifyClearUndoEvent() override;
	void notifyClearRedoEvent() override;

private:
	// Remove an observer from a given list
	bool _remove_one(UndoObserverRecordList& list, UndoStackObserver& rec);

	// Mark an observer for removal from a given list
	bool _mark_one(UndoObserverRecordList& list, UndoStackObserver& rec);

	// Keep track of whether or not we are notifying observers
	unsigned int _iterating;

	// Observers in the active list
	UndoObserverRecordList _active;

	// Observers to be added
	UndoObserverRecordList _pending;

	// Prevents the observer vector from modifications during 
	// iteration through the vector
	void _lock() { this->_iterating++; }
	void _unlock();
};

}

#endif // SEEN_COMPOSITE_UNDO_COMMIT_OBSERVER_H
