#ifndef __DragDrop__H
#define __DragDrop__H

#include <stdbool.h>
#include "WimpLib:Coords.h"
#include "WimpLib:Event.h"
#include "WimpLib:Sprites.h"

#ifdef __cplusplus
extern "C" {
#endif

/*= Public interface =========================================================*/

/*============================================================================*/
/*= Wimp_DragBox interface ===================================================*/
/*============================================================================*/

#define EDragType_WindowMove     1
#define EDragType_WindowResize   2
#define EDragType_WindowHScroll  3
#define EDragType_WindowVScroll  4
#define EDragType_Box            5
#define EDragType_RubberBox      6
#define EDragType_Point          7
#define EDragType_WindowScrolls 12

typedef struct
{
	HWind w;
	int   type;
	CRect box;
	CRect bounds;
} CDragBox;

void DragBox(CDragBox* pdrag);

/* ------------------------- Drag_DragPoint ------------------------------------
 * Description: This function drags an invisible object around the desktop.
 *
 * Parameters:  FNNullEvent  function called on Null events
 *              pUserHandle  handle/pointer to data passed as parameter to the
 *                           OnDraggingCallbacks to identify the dragged data
 *              pscreenbox   bounding box of dragging
 *              mouse_shape  mouse pointer shape to use while dragging
 *
 * Returns:     The function returns false if the dragging was aborted.
 *
 * Usage:       Dragging of sliders, etc.
 *
 */

typedef void (*DragDrop_FNullEvent)(void* pUserHandle, const Mouse* m);
typedef bool (*DragDrop_FTransfer)(void* handle, file_type type, const void* data, const ScreenPos* pInfo);

bool Drag_DragPoint(void* pUserHandle, DragDrop_FNullEvent FNullEvent, const CRect* pscreenbox, const MouseShape* mouse_shape);

/*============================================================================*/
/*= DragDrop/ActiveDrag interface ============================================*/
/*============================================================================*/

// typedefs for Dragging & DragClaim messages (see Event.h)
#define DragFlags_Selection    0x02
#define DragFlags_Clipboard    0x04
#define DragFlags_DeleteSource 0x08 // Move instead of copy
#define DragFlags_DoNotClaim   0x10 // So that claimant may cleanup (ghost caret, etc)

#define ClaimFlags_PointerChange 0x01 // We, claimant have changed the pointer
#define ClaimFlags_RemoveDragBox 0x02 // Ask the dragging task to hide its dragbox/sprite/object
#define ClaimFlags_DeleteSource  0x08 // Move instead of copy

// Info returned from after dragging is terminated
typedef struct DragDrop_Info
{
	HTask               task;
	unsigned int        msg_ref;
	unsigned int        flags;
	file_type           type;
	DragDrop_FTransfer  FTransfer;
	void*               TransferHandle;
} DragDrop_Info;

/* ------------------------- DragDrag_GetUserHandle ----------------------------
 * Description: This function returns the user handle of the current dragging.
 *
 * Parameters:  None
 *
 * Returns:     The function returns a handle/data pointer for the data being
 *              dragged by our application or NULL for data from other tasks.
 *              Most of the time dragging over your own data means a move
 *              while dragging to another window a copy so you need to be
 *              check that to identify the data dragged around.
 *
 */

void* DragDrop_GetUserHandle(void);

/* ------------------------- DragDrop_DragFile ---------------------------------
 * Description: This function drags a file icon around the desktop asking
 *              applications if they would accept such a file. The sprite
 *              dragged is in form file_xxx where xxx is the first type in the
 *              given list.
 *
 * Parameters:  pUserHandle  handle/pointer to data being dragged to allow
 *                           OnDraggingCallbacks to identify the dragged data
 *              flags        drag an drop flags
 *              types        list of file types in which the data can be saved,
 *                           terminated by -1
 *
 * Returns:     The function returns the following structure:
 *              msg_ref      the claimant's last message id
 *                           or 0 if there is no claimant
 *              task         the task id of the claimant
 *                           or your task id if there is no claimant
 *                           or 0 if the drag was aborted
 *              flags        the claimant flags
 *              type         the first file type supported by
 *                           the claimant and accepted by you
 *                           or the first file type supported by
 *                           you if there is no claimant
 *
 * Other Info:  On return you should first check if the task returned is 0 in
 *              which case dragging is cancelled otherwise you should follow
 *              with a normal Message_DataSave protocol except that if there was
 *              a claimant you should use the claimant's last msg id as
 *              reference and send the message directly to the claimant.
 *              Or you can use DragDrop_Send who does all the dirty job for you.
 *
 */

DragDrop_Info DragDrop_DragFile(void* pUserHandle, unsigned int flags, const file_type* types);

/* ------------------------- ActiveDrag_DragPoint ------------------------------
 * Description: This function drags an invisible object around the desktop.
 *
 * Parameters:  pUserHandle  handle/pointer to data being dragged to allow
 *                           OnDraggingCallbacks to identify the dragged data
 *              pscreenbox   bounding box of dragging
 *              mouse_shape  mouse pointer shape to use while dragging
 *
 * Returns:     The function returns the following structure:
 *              msg_ref      the claimant's last message id
 *                           or 0 if there is no claimant
 *              task         the task id of the claimant
 *                           or your task id if there is no claimant
 *                           or 0 if the drag was aborted
 *              flags        the claimant flags
 *              type         the first file type supported by
 *                           the claimant and accepted by you
 *                           or the first file type supported by
 *                           you if there is no claimant
 *
 */

DragDrop_Info ActiveDrag_DragPoint(void* pUserHandle, const CRect* pscreenbox, const MouseShape* mouse_shape);

/* ------------------------- ActiveDrag_DragSprite -----------------------------
 * Description: This function drags a sprite around the desktop.
 *
 * Parameters:  pUserHandle  handle/pointer to data being dragged to allow
 *                           OnDraggingCallbacks to identify the dragged data
 *              pscreenbox   bounding box of dragging
 *              sprite       sprite to use while dragging
 *
 * Returns:     The function returns the following structure:
 *              msg_ref      the claimant's last message id
 *                           or 0 if there is no claimant
 *              task         the task id of the claimant
 *                           or your task id if there is no claimant
 *                           or 0 if the drag was aborted
 *              flags        the claimant flags
 *              type         the first file type supported by
 *                           the claimant and accepted by you
 *                           or the first file type supported by
 *                           you if there is no claimant
 *
 */

DragDrop_Info ActiveDrag_DragSprite(void* pUserHandle, const CRect* pscreenbox, const char* sprite);

/* ------------------------- DragDrop_GetFileType ------------------------------
 * Description: This function returns the first proposed type supported.
 *
 * Parameters:  proposed     list of file types he would like to receive,
 *                           classified by order of preference,
 *                           terminated by -1
 *              supported    list of file types we support terminated by -1
 *
 * Returns:     The function returns the first proposed type supported.
 *              It returns -1 if no proposed type is supported.
 *
 */

file_type DragDrop_GetFileType(const file_type* proposed, const file_type* supported);

/* ------------------------- DragDrop_SetFileType ------------------------------
 * Description: This function returns the first proposed type supported.
 *
 * Parameters:  proposed     list of file types he would like to receive,
 *                           classified by order of preference,
 *                           terminated by -1
 *              supported    list of file types we support terminated by -1
 *
 * Returns:     The function returns the first proposed type supported.
 *              It returns first supported type if no proposed type is supported.
 *
 */

file_type DragDrop_SetFileType(const file_type* proposed, const file_type* supported);

bool DragDrop_IsValidFileType(file_type proposed, const file_type* supported);

/* ------------------------- DragDrop_ReplyToDragging --------------------------
 * Description: You should use this function to reply to a Dragging message.
 *              It will send a DragClaim message.
 *
 * Parameters:  msg          the Dragging message received
 *              flags        Claim_PointerChange if you changed the pointer
 *                           Claim_RemoveDragBox to ask the caller to hide
 *                           its drag box/sprite/object
 *                           Claim_DeleteSource if source data is to be deleted
 *                           (e.g. you check the Shift key to distinguish
 *                           a move from a copy)
 *              types        list of file types you support terminated by -1
 *              proc         Callback to use send the data from the source
 *                           to the destination when both source and
 *                           destination are internal to the application
 *                           or NULL to use standard RISC OS data exchanges.
 *              handle       Callback transfer handle or NULL.
 *
 */

void DragDrop_ReplyToDragging(const Msg_Dragging* msg
                              , unsigned int flags
                              , const file_type* types
                              , DragDrop_FTransfer proc
                              , void* handle);

/* ------------------------- DragDrop_SetOnDraggingCallback --------------------
 * Description: You should use this function when you receive an Dragging
 *              message. First, you have to setup a callback function of type
 *              OnDraggingCallback that will analyse the message:
 *              - if the drag is supported your callback
 *                + may set a ghost caret
 *                + may change the pointer
 *                + may force windows to scroll
 *                + must call DragDrop_ReplyToDragging
 *                + must return true
 *              - if the drag is not supported or if the message contains
 *                the Drag_DoNotClaim flag your callback must
 *                + remove a possibly set ghost caret
 *                + not reset the pointer (the sender of the Dragging
 *                  message will do it).
 *                + return false.
 *              This function will call your callback and if you accept the
 *              dragging, it will setup a prehandler so that you every
 *              received OnDragging messages will be passed to your callback
 *              until you reject the dragging or a data transfer message
 *              is sent.
 *
 * Parameters:  msg          the Dragging message received
 *              ondragging   the callback that will reply to Dragging messages
 *              pHandle      handle for data required by the callback
 *                           (a pointer to a view of the document, ...)
 *
 * Returns:     The function true if the drag is supported
 */

typedef bool (*OnDraggingCallback)(void* handle, const Msg_Dragging* rcv);

bool DragDrop_SetOnDraggingCallback(const Msg_Dragging* rcv, OnDraggingCallback ondragging, void* pHandle);

/*= Protected interface used by dragging functions ===========================*/

/* ------------------------- DragDrop_DragLoop ---------------------------------
 * Description: This function starts the drag, polls the wimp and returns when
 *              either the user release the drags or the drag is aborted.
 *
 * Parameters:  flags        drag an drop flags
 *              pscreenbox   bounding box of dragging
 *              types        list of file types in which the data can be saved,
 *                           terminated by -1
 *                           or NULL for non DragDrop drags
 *              FNShow       function to show the drag box/sprite/object
 *              FNHide       function to hide the drag box/sprite/object
 *              FNPointer    function to set the dragging pointer
 *              FNNullEvent  function to call on null events
 *                           or NULL to broadcast Dragging messages
 *              FNProcessKey function to call when keys are pressed
 *                           to handle cursor mouse movement
 *              pDragHandle  handle/pointer for data used by the FNShow, FNHide
 *                           and FNPointer functions
 *              pUserHandle  handle/pointer to data being dragged to allow
 *                           OnDraggingCallbacks to identify the dragged data
 *
 * Returns:     The function returns the following structure:
 *              msg_ref      the claimant's last message id
 *                           or 0 if there is no claimant
 *              task         the task id of the claimant
 *                           or your task id if there is no claimant
 *                           or 0 if the drag was aborted
 *              flags        the claimant flags
 *              type         the first file type supported by
 *                           the claimant and accepted by you
 *                           or the first file type supported by
 *                           you if there is no claimant
 *
 */

typedef void (*DragDrop_FShow)(void* pDragHandle);
typedef void (*DragDrop_FHide)(void* pDragHandle);
typedef void (*DragDrop_FPointer)(void* pDragHandle);
typedef bool (*DragDrop_FProcessKey)(void* pDragHandle, const Mouse*, int key);

DragDrop_Info DragDrop_DragLoop(void* pUserHandle
				, unsigned int flags
				, const CRect* box
				, const file_type* types
				, DragDrop_FShow FNShow
				, DragDrop_FHide FNHide
				, DragDrop_FPointer FNPointer
				, DragDrop_FNullEvent FNNullEvent
				, DragDrop_FProcessKey FNProcessKey
				, void* pDragHandle
				);

CSize DragDrop_GetSpriteSize(const CSpriteHdr* pSprite);

/*= Interface used by the task framework =====================================*/

/* ------------------------- DragDrop_CallDraggingCallback ---------------------
 * Description: This function is called by the task framework when it receives
 *              a Message_Dragging to let any registred dragging callback
 *              be the first to process that message, so that for example the
 *              callback unregister itself if the pointer moves outside the
 *              window.
 *
 * Parameters:  msg          the Dragging message received
 *
 * Returns:     The function returns true if the callback handled the message
 *
 */

bool DragDrop_CallDraggingCallback(const Msg_Dragging* rcv);

/* ------------------------- DragDrop_ReplyNoDrop ------------------------------
 * Description: This function is called by the task framework when no handler
 *              responded to a received Message_Dragging. It will claim the
 *              dragging message if the pointer is over one of the task's own
 *              windows and turn the pointer into a 'forbidden' symbol by using
 *              the sprite ptr_nodrop.
 *
 * Parameters:  msg          the Dragging message received
 *
 * Returns:     Nothing
 *
 */

void DragDrop_ReplyNoDrop(const Msg_Dragging* rcv);

/* ------------------------- DragDrop_IsNoDropRef ------------------------------
 * Description: This function is called by the task framework to check if the
 *              reference is the one of the message sent by the last
 *              DragDrop_ReplyNoDrop call so that the task can ignore any
 *              DataSave message that use this reference as we don't want the
 *              data but nothing in the protocol prevents the other task to send
 *              us the message if user drops the data while ptr_nodrop is shown.
 *
 * Parameters:  ref          a message reference
 *
 * Returns:     true if is the reference our last sent Dragging message
 *
 */

bool DragDrop_IsNoDropRef(int ref);

#ifdef __cplusplus
}
#endif

#endif
