/* Interface to the X recording system

Copyright (C) 2015 Ken D. Olum

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Thanks to Noufal Ibrahim.  Some of this code has been taken from
   his record_example.c under GPL */

#include "xdragon.h"
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/extensions/record.h>
/* This file cannot use windows.h, because there are conflicting datatype
   definitions between that file and Xproto.h */

extern Display *inputDisplay;	/* Use input display for our ctrl connection */
extern Display *recordDisplay;	/*  Data display for the record extension */

extern Window vmWindow;
extern KeyCode hotkeycode;

/* Set to request dragon to forget state of spacing, etc. */
extern bool reset_dictation_state;

bool recycle_wait = 0; /* Set if we're waiting to get an event we simulated */
KeyCode recycle_kc; /* KeyCode that we're waiting for */
bool recycle_ud;    /* True if key press, False if release */

extern bool vmFocus;	 /* Does the VM X window have focus? */

void record_callback(XPointer ignore, XRecordInterceptData *intercept)
{ xEvent *event ;
  KeyCode keycode;
  Window kpwindow;

			/* Only consider events from server */
  if (intercept->category == XRecordFromServer) {
    // fprintf(stderr, "X");
    /* Since we only asked to intercept events, this must be an event */
    event = (xEvent *)intercept->data;		  
    keycode = event->u.u.detail; /* If this is a key press or release, this is the code */
    kpwindow = event->u.keyButtonPointer.event; /* Window if button or key event */
    
    // fprintf(stderr,"Type %d\n", event->u.u.type);
    switch (event->u.u.type) {
    case KeyPress:
    case KeyRelease:
      // fprintf(stderr,"Key %d window %lx press %d\n", keycode, kpwindow, (event->u.u.type == KeyPress));
      if (!vmFocus) { /* Do nothing if key going to VM anyway */
	if (recycle_wait && keycode == recycle_kc &&
	    recycle_ud == (event->u.u.type == KeyPress)) {/* got recycled event */
	  // fprintf(stderr," (recycled)");
	  recycle_wait = False; }
	else if (keycode != hotkeycode) /* Hotkey about to be sent to us: ignore */
	  resetDictationState();}
      break;
    case ButtonPress: /* Reset on mouse clicks (but not motion) and focus changes */
    case ButtonRelease:
      if (!vmFocus) {		/* Do nothing if click in VM anyway */
	resetDictationState();}
      break;
    case FocusIn:
      // fprintf(stderr, "FocusIn window %lx, mode %d\n", event->u.focus.window, event->u.focus.mode);
      /* Do not reset on grab and ungrab notifies, because these may be generated by the hotkey */
      if (event->u.focus.mode = NotifyNormal) resetDictationState();
      break;
    }}
  XRecordFreeData(intercept);
}

/* Called when the recordDisplay (data) connection has something to say */
//recordLoop() {
//  XRecordProcessReplies(recordDisplay);
//}

XRecordRange *rrs[2];
XRecordClientSpec rcs = XRecordAllClients;
XRecordContext rc;

setupRecord () {
  int major_version, minor_version;

  debug("Record thread starting\n");

  if (!XRecordQueryVersion(inputDisplay, &major_version, &minor_version)){
     XCloseDisplay(inputDisplay);
     fprintf(stderr, "X server does not support the Record extension\n");
    exit(2);}

  rrs[0] = XRecordAllocRange();
  rrs[1] = XRecordAllocRange();
  if (!rrs[0] || !rrs[1]) {
    fprintf (stderr, "Can't allocate record range objects\n");
    exit(3); }
  
  /* Get notified about key, button, and focus events */
  rrs[0]->delivered_events.first = KeyPress;
  rrs[0]->delivered_events.last =  ButtonRelease;
  /* We need to detect changes of focus, so notified of FocusIn */
  rrs[1]->delivered_events.first = FocusIn;
  rrs[1]->delivered_events.last = FocusIn;

  rc = XRecordCreateContext(inputDisplay, 0, &rcs, 1, rrs, 2);
  if (!rc) {
    fprintf(stderr, "Can't create record context\n");
    exit(4); }
  
  }

/* enable recording and wait for events to arrive */
doRecord () {
  if (!XRecordEnableContext(recordDisplay, rc, record_callback, NULL)) {
    fprintf(stderr, "Can't enable record context\n");
    exit(5);
  }
}

int close_record(Display *display) {
  XRecordDisableContext(inputDisplay, rc);
  XRecordFreeContext(inputDisplay, rc);
  XFree(rrs[1]);XFree(rrs[2]);
  XCloseDisplay (recordDisplay);
}
