#ifndef CXWINDOWSCLIPBOARD_H
#define CXWINDOWSCLIPBOARD_H

#include "IClipboard.h"
#include "ClipboardTypes.h"
#include "CString.h"
#include "stdmap.h"
#include "stdlist.h"
#include <X11/Xlib.h>

class CXWindowsClipboard : public IClipboard {
public:
	CXWindowsClipboard(Display*, Window, ClipboardID);
	virtual ~CXWindowsClipboard();

	// tell clipboard it lost ownership
	void				lost(Time);

	// add a selection request to the request list.  if the given
	// owner window isn't this clipboard's window then this simply
	// sends a failure event to the requestor.
	void				addRequest(Window owner,
								Window requestor, Atom target,
								::Time time, Atom property);

	// continue processing a selection request.  returns true if the
	// request was handled, false if the request was unknown.
	bool				processRequest(Window requestor,
								::Time time, Atom property);

	// terminate a selection request.  returns true iff the request
	// was known and handled.
	bool				destroyRequest(Window requestor);

	// get the clipboard's window
	Window				getWindow() const;

	// get the clipboard selection atom
	Atom				getSelection() const;

	// IClipboard overrides
	virtual bool		empty();
	virtual void		add(EFormat, const CString& data);
	virtual bool		open(Time) const;
	virtual void		close() const;
	virtual Time		getTime() const;
	virtual bool		has(EFormat) const;
	virtual CString		get(EFormat) const;

private:
	// convert target atom to clipboard format
	EFormat				getFormat(Atom target) const;

	// add a non-MULTIPLE request.  does not verify that the selection
	// was owned at the given time.  returns true if the conversion
	// could be performed, false otherwise.  in either case, the
	// reply is inserted.
	bool				addSimpleRequest(
								Window requestor, Atom target,
								::Time time, Atom property);

	// clear the cache, resetting the cached flag and the added flag for
	// each format.
	void				clearCache() const;
	void				doClearCache();

	// cache all formats of the selection
	void				fillCache() const;
	void				doFillCache();

	// ICCCM interoperability methods
	void				icccmFillCache();
	bool				icccmGetSelection(Atom target,
								Atom* actualTarget,
								CString* data) const;
	Time				icccmGetTime() const;

	// motif interoperability methods
	bool				motifLockClipboard() const;
	void				motifUnlockClipboard() const;
	bool				motifOwnsClipboard() const;
	Time				motifGetTime() const;
	void				motifFillCache();
	// FIXME

	//
	// helper classes
	//

	// read an ICCCM conforming selection
	class CICCCMGetClipboard {
	public:
		CICCCMGetClipboard(Window requestor, Time time, Atom property);
		~CICCCMGetClipboard();

		// convert the given selection to the given type.  returns
		// true iff the conversion was successful or the conversion
		// cannot be performed (in which case *actualTarget == None).
		bool			readClipboard(Display* display,
								Atom selection, Atom target,
								Atom* actualTarget, CString* data);

	private:
		bool			doEventPredicate(Display* display,
								XEvent* event);
		static Bool		eventPredicate(Display* display,
								XEvent* event,
								XPointer arg);
		void			timeout(void*);

	private:
		Window			m_requestor;
		Time			m_time;
		Atom			m_property;
		bool			m_incr;
		bool			m_failed;
		bool			m_done;

		// true iff we've received the selection notify
		bool			m_reading;

		// the converted selection data
		CString*		m_data;

		// the actual type of the data.  if this is None then the
		// selection owner cannot convert to the requested type.
		Atom*			m_actualTarget;

		// property used in event to wake up event loop
		Atom			m_timeout;

	public:
		// true iff the selection owner didn't follow ICCCM conventions
		bool			m_error;
	};

	// Motif structure IDs
	enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader };

	// _MOTIF_CLIP_HEADER structure
	class CMotifClipHeader {
	public:
		SInt32			m_id;			// kMotifClipHeader
		SInt32			m_pad1[3];
		SInt32			m_item;
		SInt32			m_pad2[4];
		SInt32			m_numItems;
		SInt32			m_pad3[3];
		Window			m_selectionOwner;
		SInt32			m_pad4[2];
		SInt32			m_items[1];		// m_numItems items
	};

	// Motif clip item structure
	class CMotifClipItem {
	public:
		SInt32			m_id;			// kMotifClipItem
		SInt32			m_pad1[6];
		SInt32			m_numFormats;
		SInt32			m_pad2[7];
		SInt32			m_formats[1];	// m_numFormats formats
	};

	// Motif clip format structure
	class CMotifClipFormat {
	public:
		SInt32			m_id;			// kMotifClipFormat
		SInt32			m_pad1[6];
		SInt32			m_length;
		SInt32			m_data;
		Atom			m_type;
		SInt32			m_pad2[6];
	};

	// stores data needed to respond to a selection request
	class CReply {
	public:
		CReply(Window, Atom target, ::Time);
		CReply(Window, Atom target, ::Time, Atom property,
								const CString& data, Atom type, int format);

	public:
		// information about the request
		Window			m_requestor;
		Atom			m_target;
		::Time			m_time;
		Atom			m_property;

		// true iff we've sent the notification for this reply
		bool			m_replied;

		// true iff the reply has sent its last message
		bool			m_done;

		// the data to send and its type and format
		CString			m_data;
		Atom			m_type;
		int				m_format;

		// index of next byte in m_data to send
		UInt32			m_ptr;
	};
	typedef std::list<CReply*> CReplyList;
	typedef std::map<Window, CReplyList> CReplyMap;
	typedef std::map<Window, long> CReplyEventMask;

	// reply methods
	bool				insertMultipleReply(Window, ::Time, Atom);
	void				insertReply(CReply*);
	void				pushReplies();
	void				pushReplies(CReplyMap::iterator,
								CReplyList&, CReplyList::iterator);
	bool				sendReply(CReply*);
	void				clearReplies();
	void				clearReplies(CReplyList&);
	void				sendNotify(Window requestor, Atom selection,
								Atom target, Atom property, Time time);
	bool				wasOwnedAtTime(::Time) const;

	// data conversion methods
	Atom				getTargetsData(CString&, int* format) const;
	Atom				getTimestampData(CString&, int* format) const;
	Atom				getStringData(CString&, int* format) const;

private:
	Display*			m_display;
	Window				m_window;
	ClipboardID			m_id;
	Atom				m_selection;
	mutable bool		m_open;
	mutable Time		m_time;
	bool				m_owner;
	mutable Time		m_timeOwned;
	Time				m_timeLost;

	// true iff open and clipboard owned by a motif app
	mutable bool		m_motif;

	// the added/cached clipboard data
	bool				m_cached;
	Time				m_cacheTime;
	bool				m_added[kNumFormats];
	CString				m_data[kNumFormats];

	// conversion request replies
	CReplyMap			m_replies;
	CReplyEventMask		m_eventMasks;

	// atoms we'll need
	Atom				m_atomTargets;
	Atom				m_atomMultiple;
	Atom				m_atomTimestamp;
	Atom				m_atomAtom;
	Atom				m_atomAtomPair;
	Atom				m_atomInteger;
	Atom				m_atomData;
	Atom				m_atomINCR;
	Atom				m_atomString;
	Atom				m_atomText;
	Atom				m_atomCompoundText;
	Atom				m_atomMotifClipLock;
	Atom				m_atomMotifClipHeader;
	Atom				m_atomMotifClipAccess;
	Atom				m_atomGDKSelection;
};

#endif