#include "snd.h"
#include <X11/Xmu/Xmu.h>

#define USING_CLIPBOARD 0
/* Motif clipboard disabled because it is extremely buggy and probably useless anyway in this context */
/* in Linux (Metrolink), the clipboard is apparently limited to 1024 bytes, making it equally useless */

/* Motif 2.0 apparently has a new clipboard mechanism, so it's worth another look, I suppose */


/* -------- DROP -------- */

static Atom TARGETS,SOUND_OBJECT,STRING;

void intern_atoms (snd_state *ss)
{
  TARGETS = XInternAtom(XtDisplay((ss->sgx)->mainshell),"TARGETS",FALSE);
  SOUND_OBJECT = XInternAtom(XtDisplay((ss->sgx)->mainshell),"SOUND_OBJECT",FALSE);
  STRING = XInternAtom(XtDisplay((ss->sgx)->mainshell),"STRING",FALSE);
}

static void massage_selection(Widget w,XtPointer clientData,Atom *selection,Atom *type,XtPointer value,unsigned long *length,int *format)
{
  /* we are receiving data -- we will accept only SOUND_OBJECT or STRING */
  unsigned long i;
  char *str;
  if (*type == SOUND_OBJECT)
    {
      /* must be another snd out there sending us data */
      receive_selection(value,*length);
    }
  else
    {
      if (*type == STRING)
	{
	  str = (char *)calloc(*length,sizeof(char));
	  for (i=0;i<(*length);i++)
	    {
	      if (((char *)value)[i] == ' ')
		{
		  str[i] = '\0';
		  break;
		}
	      else str[i] = ((char *)value)[i];
	    }
	  select_channel(snd_open_file(str,(snd_state *)clientData),0);
	  /* value is the file name if dropped icon from filer */
	}
    }
}

static snd_state *good_grief;

static void HandleDrop(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* we'll only accept SOUND_OBJECT and STRING here */
  /* this is called (see InitializeDrop) when a drop occurs */
  XmDropProcCallbackStruct *cb = (XmDropProcCallbackStruct *)callData;
  Arg args[12];
  int n,i,num_targets,k;
  Atom *targets;
  XmDropTransferEntryRec entries[2];
  XtPointer ss;
  XtVaGetValues(w,XmNuserData,&ss,NULL);
  if ((cb->dropAction != XmDROP) || (cb->operation != XmDROP_COPY))
    {
      cb->dropSiteStatus = XmINVALID_DROP_SITE;
      return;
    }
  k=-1;
  XtVaGetValues(cb->dragContext,XmNexportTargets,&targets,XmNnumExportTargets,&num_targets,NULL);
  for (i=0;i<num_targets;i++) 
    {
      if ((targets[i] == SOUND_OBJECT) || (targets[i] == STRING))
	{
	  k=i; 
	  break;
	}
    }
  if (k == -1)
    {
      cb->dropSiteStatus = XmINVALID_DROP_SITE;
      cb->operation = XmDROP_NOOP;
      n=0;
      XtSetArg(args[n],XmNnumDropTransfers,0); n++;
      XtSetArg(args[n],XmNtransferStatus,XmTRANSFER_FAILURE); n++;
      XmDropTransferStart(cb->dragContext,args,n);
      return;
    }
  entries[0].target = targets[k];
  entries[0].client_data = ss;
  n=0;
  XtSetArg(args[n],XmNdropTransfers,entries); n++;
  XtSetArg(args[n],XmNnumDropTransfers,1); n++;
  XtSetArg(args[n],XmNtransferProc,massage_selection); n++;
  cb->operation = XmDROP_COPY;
  XmDropTransferStart(cb->dragContext,args,n);
}

void InitializeDrop(snd_state *ss)
{
  /* called via startup func */
  int n;
  Atom targets[2];
  Arg args[12];
  targets[0] = SOUND_OBJECT;
  targets[1] = STRING;
  n=0;
  XtSetArg(args[n],XmNdropSiteOperations,XmDROP_COPY); n++;
  XtSetArg(args[n],XmNimportTargets,targets); n++;
  XtSetArg(args[n],XmNnumImportTargets,2); n++;
  XtSetArg(args[n],XmNdropProc,HandleDrop); n++;
  XmDropSiteRegister(get_menubar(),args,n);
  good_grief = ss;
}



/* -------- CLIPBOARD -------- */

#if USING_CLIPBOARD

#define SND_TXT_BUF_SIZE 128
static char snd_txt_buf[SND_TXT_BUF_SIZE];

static Window clip_win = 0;
static Display *clip_dpy = NULL;
static Widget sel_wid = NULL;
static XmString xmstr = NULL;
static int clip_registered = 0;

static void clipboard_callback(Widget w,int *data_id,int *private,int *reason)
{
  if ((*reason) == XmCR_CLIPBOARD_DATA_REQUEST)
    {
      XmClipboardCopyByName(clip_dpy,clip_win,(*data_id),(XtPointer)(send_selection(good_grief)),selection_len()*4,(*private));
    }
}

static char *clipboard_error(int n)
{
  switch (n)
    {
    case 0: return("failed"); break;
    case 2: return("truncated"); break;
    case 4: return("locked"); break;
    case 5: return("bad format"); break;
    case 6: return("no data"); break;
    }
  return(NULL);
}

void CopyToClipboard(Widget w, snd_state *ss)
{
  int result;
  long itemid,dataid;
  unsigned long len;
  finish_keyboard_selection();
  if (selection_len() == 0) return;
  if (!sel_wid) sel_wid = main_SHELL(ss);
  if (!clip_dpy) clip_dpy = XtDisplay(sel_wid);
  if (!clip_win) clip_win = XtWindow(sel_wid);
  if (!clip_registered) {XmClipboardRegisterFormat(clip_dpy,"SOUND_OBJECT",32); clip_registered=1;}
  if (!xmstr) xmstr=XmStringCreateLtoR("Snd",XmFONTLIST_DEFAULT_TAG);
  len = selection_len();
  result = XmClipboardStartCopy(clip_dpy,clip_win,xmstr,XtLastTimestampProcessed(clip_dpy),sel_wid,(XmCutPasteProc)clipboard_callback,&itemid);
  if (result == ClipboardSuccess)
    {
      result = XmClipboardCopy(clip_dpy,clip_win,itemid,"SOUND_OBJECT",NULL,len,0,&dataid);
      if (result == ClipboardSuccess)
	{
	  result=XmClipboardEndCopy(clip_dpy,clip_win,itemid);
	}
    }
  if (result != ClipboardSuccess)
    {
      XmClipboardCancelCopy(clip_dpy,clip_win,itemid);
      sprintf(snd_txt_buf,"clipboard is unhappy: %s ",clipboard_error(result));
      snd_printf(ss,snd_txt_buf);
    }
}

static void CopyFromClipboard(Widget w, snd_state *ss)
{
  int result;
  long id;
  int *data;
  unsigned long numbytes;
  unsigned long len;
  finish_keyboard_selection();
  if (selection_is_ours()) return; /* we already have the data */
  if (!sel_wid) sel_wid = main_SHELL(ss);
  if (!clip_dpy) clip_dpy = XtDisplay(sel_wid);
  if (!clip_win) clip_win = XtWindow(sel_wid);
  if (!clip_registered) {XmClipboardRegisterFormat(clip_dpy,"SOUND_OBJECT",32); clip_registered=1;}
  XmClipboardStartRetrieve(clip_dpy,clip_win,XtLastTimestampProcessed(clip_dpy));
  result = XmClipboardInquireLength(clip_dpy,clip_win,"SOUND_OBJECT",&len);
  if (len != 0)
    {
      data = (int *)calloc(len>>2,sizeof(int));
      result = XmClipboardRetrieve(clip_dpy,clip_win,"SOUND_OBJECT",data,len,&numbytes,&id);
      if (result != ClipboardSuccess)
	{
	  sprintf(snd_txt_buf,"clipboard is unhappy: %d ",result);
	  snd_printf(ss,snd_txt_buf);
	}
      receive_selection(data,numbytes>>2); 
      free(data);
    }
  XmClipboardEndRetrieve(clip_dpy,clip_win);
}

static void lose_selection(Widget w, Atom *selection)
{
  give_up_selection();
}

static Boolean convert_selection(Widget w,Atom *selection,Atom *target,Atom *type,XtPointer *value,unsigned long *length,int *format)
{ /* we are providing data */
  XSelectionRequestEvent* req = XtGetSelectionRequest(w,*selection,(XtRequestId)NULL);
  /* called if someone else wants our selection */
  if (*target == SOUND_OBJECT)
    {
      *type = SOUND_OBJECT;
      *length = selection_len();
      *format = 32;
      *value = send_selection(good_grief);
      return(TRUE);
    }
  if (*target == TARGETS)
    {
      /* they want to know what types we can convert to */
      /* first let XmuConvert... fill in its types */
      Atom* targetP;
      Atom* std_targets;
      unsigned long std_length;
      XmuConvertStandardSelection(w,req->time,selection,target,type,(XPointer*)&std_targets,&std_length,format);
      *value = XtMalloc(sizeof(Atom)*(std_length + 1)); 
      targetP = *(Atom**)value;
      *length = std_length + 1;
      *targetP++ = SOUND_OBJECT;
      bcopy((char*)std_targets,(char*)targetP,sizeof(Atom)*std_length);
      XtFree((char*)std_targets);
      *type = XA_ATOM;
      *format = sizeof(Atom) * 8;
      return(TRUE);     
    } 
  return(XmuConvertStandardSelection(w,CurrentTime,selection,target,type,(XPointer *)value,length,format));
}

void ClaimSelection(Widget w, XButtonEvent *ev, chan_info *cp)
{
  if (!sel_wid) sel_wid = main_SHELL(cp->state);
  if (!XtOwnSelection(sel_wid,XA_PRIMARY,ev->time,convert_selection,lose_selection,NULL))
    fprintf(stderr,"can't become selection owner!");
  CopyToClipboard(sel_wid,cp->state);
  reflect_edit_with_selection_in_menu();
}

void PasteSelection(Widget w,chan_info *cp)
{
  if (!sel_wid) sel_wid = main_SHELL(cp->state);
  CopyFromClipboard(sel_wid,cp->state);
  paste_region(0,cp);
}

void AddSelection(Widget w,chan_info *cp)
{
  if (!sel_wid) sel_wid = main_SHELL(cp->state);
  CopyFromClipboard(sel_wid,cp->state);
  add_region(0,cp);
}

void relinquish_selection_ownership(void)
{
  if (sel_wid) XtDisownSelection(sel_wid,XA_PRIMARY,CurrentTime);
}

#else /* USING_CLIPBOARD */

void CopyToClipboard(Widget w, snd_state *ss) {finish_keyboard_selection();}
void ClaimSelection(Widget w, XButtonEvent *ev, chan_info *cp) {reflect_edit_with_selection_in_menu();}
void PasteSelection(Widget w,chan_info *cp) {paste_region(0,cp);}
void AddSelection(Widget w,chan_info *cp) {add_region(0,cp);}
void relinquish_selection_ownership(void) {give_up_selection();}

#endif


static XtWorkProcId watch_selection_button = 0;
static Boolean WatchSelection(XtPointer cp)
{
  if (watch_selection_button)
    {
      move_selection_2((chan_info *)cp);
      return(FALSE);
    }
  else return(TRUE);
}

void StartSelectionWatch(chan_info *cp)
{
  watch_selection_button = XtAppAddWorkProc(XtWidgetToApplicationContext(main_PANE(cp)),WatchSelection,(XtPointer)cp);
}

void CancelSelectionWatch(void)
{
  XtRemoveWorkProc(watch_selection_button);
  watch_selection_button = 0;
}

