/* This module, and the entire NoClutter program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation
 *
 * Copyright 1994, Robert Nation. No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact. */

#define TRUE 1
#define FALSE 

#define UPDATE_ONLY 1
#define ALL 2
#define PROP_SIZE 1024

#include <FVWMconfig.h>

#ifdef ISC
#include <sys/bsdtypes.h> /* Saul */
#endif 

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>

#if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_
#include <sys/select.h>
#endif

#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>  

#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>                 

#include "../../fvwm/module.h"

#include "FvwmTalk.h"
#include <fvwm/version.h>

char *MyName;
int fd_width,screen, d_depth;
int fd[2];

int x_fd;
Window Root;
Display *dpy;
char *font_string = "helvetica";
char *ForeColor = "green";
char *BackColor = "black";
char *display_name = NULL;

XFontStruct *font;
Pixel fore_pix,back_pix;
static Atom wm_del_win;
unsigned long valuemask;
XSetWindowAttributes attributes;
XWMHints wmhints;
Window window;
char last_error[256],previous_line[256];
char Text[256];
int pos = 0;

XSizeHints sizehints =
{
  (PMinSize | PResizeInc | PBaseSize | PWinGravity | PMaxSize),
  0, 0, 100, 100,	                /* x, y, width and height */
  1, 1,		                        /* Min width and height */
  0, 0,		                        /* Max width and height */
  1, 1,	                         	/* Width and height increments */
  {0, 0}, {0, 0},                       /* Aspect ratio - not used */
  1, 1,                 		/* base size */
  (NorthWestGravity)                    /* gravity */
};

Pixel GetColor(char *name);
void nocolor(char *a, char *b);
XGCValues gcv;
GC myGC;
int My_XNextEvent(Display *dpy, XEvent *event);
void DrawWindow(int mode);
void paste_primary(int window,int property,int Delete);
void request_selection(int time);

/***********************************************************************
 *
 *  Procedure:
 *	main - start of module
 *
 ***********************************************************************/
void main(int argc, char **argv)
{
  char *temp, *s;
  FILE *file;
  XWMHints        wm_hints;
  XClassHint      class_hints;
  XTextProperty   window_name;
  
  /* Save the program name - its used for error messages and option parsing */
  temp = argv[0];

  s=strrchr(argv[0], '/');
  if (s != NULL)
    temp = s + 1;

  MyName = safemalloc(strlen(temp)+2);
  strcpy(MyName,"*");
  strcat(MyName, temp);

  if((argc != 6)&&(argc != 7))
    {
      fprintf(stderr,"%s Version %s should only be executed by fvwm!\n",MyName,
	      VERSION);
      exit(1);
    }

  /* Save the program name - its used for error messages and option parsing */
  temp = argv[0];

  s=strrchr(argv[0], '/');
  if (s != NULL)
    temp = s + 1;

  MyName = safemalloc(strlen(temp)+2);
  strcpy(MyName,"*");
  strcat(MyName, temp);

  /* Dead pipes mean fvwm died */
  signal (SIGPIPE, DeadPipe);  
  
  fd[0] = atoi(argv[1]);
  fd[1] = atoi(argv[2]);

  /* Initialize X connection */
  if (!(dpy = XOpenDisplay(display_name)))
    {
      fprintf(stderr,"%s: can't open display %s", MyName,
	      XDisplayName(display_name));
      exit (1);
    }
  x_fd = XConnectionNumber(dpy);
  
  screen= DefaultScreen(dpy);
  Root = RootWindow(dpy, screen);
  if(Root == None)
    {
      fprintf(stderr,"%s: Screen %d is not valid ", MyName, (int)screen);
      exit(1);
    }
  d_depth = DefaultDepth(dpy, screen);                   
  
  fd_width = GetFdWidth();

  wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
   
  /* load the font */
  if ((font = XLoadQueryFont(dpy, font_string)) == NULL)
    {
      if ((font = XLoadQueryFont(dpy, "fixed")) == NULL)
	{
	  fprintf(stderr,"%s: No fonts available\n",MyName);
	  exit(1);
	}
    };             
  

  fore_pix = GetColor(ForeColor);
  back_pix = GetColor(BackColor);
   
  valuemask = (CWBackPixel | CWBorderPixel | CWEventMask);
  attributes.background_pixel = back_pix;
  attributes.border_pixel = fore_pix;
  attributes.event_mask = KeyPressMask | ExposureMask | ButtonPressMask;

  sizehints.width = 8*XTextWidth(font,"MMMMMMMMMM",10);  
  sizehints.height = 3*(font->ascent+font->descent+2)+2;
  sizehints.x = 0;
  sizehints.y = 0;
  sizehints.width_inc = 0.1*XTextWidth(font,"MMMMMMMMMM",10);
  sizehints.height_inc = 3*(font->ascent+font->descent+2)+2;
  sizehints.base_width = 1;
  sizehints.base_height = 3*(font->ascent+font->descent+2)+2;
  sizehints.min_width = 1;
  sizehints.min_height = 3*(font->ascent+font->descent+2)+2;
  sizehints.max_height = 3*(font->ascent+font->descent+2)+2;
  sizehints.max_width = 26*XTextWidth(font,"MMMMMMMMMM",10);  

  window = XCreateWindow (dpy, Root, 0, 0, sizehints.width,sizehints.height,
			  (unsigned int) 1,
			  CopyFromParent, InputOutput,
			  (Visual *) CopyFromParent,
			  valuemask, &attributes);                      

  class_hints.res_name = "FvwmTalk";
  class_hints.res_class =  "FvwmTalk";

  wm_hints.flags = InputHint;
  wm_hints.input = True;;
  
  XSetWMProtocols(dpy,window,&wm_del_win,1);
  XSetWMNormalHints(dpy,window,&sizehints);
  XStringListToTextProperty(&(argv[0]), 1, &window_name);
  XSetWMProperties(dpy, window, &window_name, &window_name,
      argv, argc, &sizehints, &wm_hints, &class_hints);

  gcv.foreground = fore_pix;
  gcv.background = back_pix;
  gcv.font = font->fid;
  myGC = XCreateGC(dpy,window,GCForeground|GCBackground|GCFont,&gcv);


  previous_line[0] = 0;
  last_error[0] = 0;
  XMapWindow(dpy,window);
  Loop(fd);
}


/***********************************************************************
 *
 *  Procedure:
 *	Loop - wait for data to process
 *
 ***********************************************************************/
void Loop(int *fd)
{
  KeySym keysym;
  static XComposeStatus compose = {NULL,0};
  XEvent event;
  char kbuf[100];
  int count,w;

  pos = 0;
  Text[0] = 0;

  while(1)
    {
      if(My_XNextEvent(dpy, &event))
	{
	  switch(event.type)
	    {
	    case KeyPress:
	      count = XLookupString(&(event.xkey),kbuf,99,&keysym,&compose);
	      kbuf[count] = (unsigned char)0;
	      if((keysym == XK_BackSpace )||(keysym == XK_Delete))
		{
		  if(pos > 0)
		    Text[--pos] = 0;
		}
	      else if((keysym == XK_Return)||(keysym == XK_KP_Enter))
		{
		  SendText(fd,Text,0);
		  last_error[0] = 0;
		  strncpy(previous_line,Text,255);
		  previous_line[255] = 0;
		  pos = 0;
		  Text[pos] = 0;
		  DrawWindow(ALL);
		}
	      else
		{
		  if(pos + count < 255)
		    {
		      strcat(Text,kbuf);
		      pos += count;
		    }
		  else
		    XBell(dpy,0);
		}
	      DrawWindow(UPDATE_ONLY);
	      break;
	    case ButtonPress:
	      if(event.xbutton.button == 2)
		request_selection(event.xbutton.time);
	      break;
	    case Expose:
	      DrawWindow(ALL);
	      break;
	    case ClientMessage:
	      if ((event.xclient.format==32) &&
		  (event.xclient.data.l[0]==wm_del_win))
		{
		  exit(0);
		}
	      break;
	    case SelectionNotify:
	      paste_primary(event.xselection.requestor,
			    event.xselection.property,True);
	    default:
	      break;
	    }
	}
    }
}


/***********************************************************************
 *
 *  Procedure:
 *	SIGPIPE handler - SIGPIPE means fvwm is dying
 *
 ***********************************************************************/
void DeadPipe(int nonsense)
{
  fprintf(stderr,"FvwmTalk: dead pipe\n");
  exit(0);
}



/****************************************************************************
 * 
 * Loads a single color
 *
 ****************************************************************************/ 
Pixel GetColor(char *name)
{
  XColor color;
  XWindowAttributes attributes;

  XGetWindowAttributes(dpy, Root,&attributes);
  color.pixel = 0;
   if (!XParseColor (dpy, attributes.colormap, name, &color)) 
     {
       nocolor("parse",name);
     }
   else if(!XAllocColor (dpy, attributes.colormap, &color)) 
     {
       nocolor("alloc",name);
     }
  return color.pixel;
}


void nocolor(char *a, char *b)
{
  fprintf(stderr,"%s: can't %s %s\n", MyName, a,b);

}


/***************************************************************************
 *
 * Waits for next X event, or for an auto-raise timeout.
 *
 ****************************************************************************/
int My_XNextEvent(Display *dpy, XEvent *event)
{
  fd_set in_fdset;
  unsigned long header[HEADER_SIZE];
  int body_length;
  int count,count2 = 0;
  static int miss_counter = 0;
  unsigned long *body;
  int total;
  char *cbody;

  if(XPending(dpy))
    {
      XNextEvent(dpy,event);
      return 1;
    }
  
  FD_ZERO(&in_fdset);
  FD_SET(x_fd,&in_fdset);
  FD_SET(fd[1],&in_fdset);
  
#ifdef __hpux
  select(fd_width,(int *)&in_fdset, 0, 0, NULL);
#else
  select(fd_width,&in_fdset, 0, 0, NULL);
#endif  
  
  if(FD_ISSET(x_fd, &in_fdset))
    {
      if(XPending(dpy))
	{
	  XNextEvent(dpy,event);
	  miss_counter = 0;
	  return 1;
	}
      else
	miss_counter++;
      if(miss_counter > 100)
	DeadPipe(0);
    }
  
  if(FD_ISSET(fd[1], &in_fdset))
    {
      if(count = ReadFvwmPacket(fd[1],header,&body) > 0)
	 {
	   if(header[1] == M_ERROR)
	     {
	       strncpy(last_error,(char *)(&body[3]),255);
	       last_error[strlen(last_error)-1] = 0;
	       last_error[255] = 0;
	       XClearArea(dpy,window,0,0,10000,10000,1);
	     }

	   free(body);
	 }
    }
  return 0;
}



void request_selection(int time)
{
  Atom sel_property;
  
  if (XGetSelectionOwner(dpy,XA_PRIMARY) == None) 
    {
      /*  No primary selection so use the cut buffer.
       */
      paste_primary(DefaultRootWindow(dpy),XA_CUT_BUFFER0,False);
      return;
    }
  sel_property = XInternAtom(dpy,"VT_SELECTION",False);
  XConvertSelection(dpy,XA_PRIMARY,XA_STRING,sel_property,window,time);
}

void paste_primary(int window,int property,int Delete)
{
  Atom actual_type;
  int actual_format,i;
  long nitems, bytes_after, nread;
  unsigned char *data, *data2;

  if (property == None)
    return;

  nread = 0;
  do
    {
      if (XGetWindowProperty(dpy,window,property,nread/4,PROP_SIZE,Delete,
                             AnyPropertyType,&actual_type,&actual_format,
                             &nitems,&bytes_after,(unsigned char **)&data)
          != Success)
        return;
      if (actual_type != XA_STRING)
        return;

      data2 = data;
      /* want to make a \n to \r mapping for cut and paste only */
      for(i=0;i<nitems;i++)
        {
          if(*data == '\n')
            *data = '\r';
          data++;
        }

      if(255-pos > 0)
	{
	  strncat(Text,(char *)data2,255-pos-nitems);
	  pos = strlen(Text);
	  DrawWindow(UPDATE_ONLY);
	}
      nread += nitems;
      XFree(data2);
    } while (bytes_after > 0);
}

void DrawWindow(int mode)
{
  int w;

  if(mode == ALL)
    {
      XClearWindow(dpy,window);
      XDrawImageString(dpy,window,myGC,2,(font->ascent+font->descent+2)+
		       font->ascent+2,last_error,strlen(last_error));
      XDrawImageString(dpy,window,myGC,2, font->ascent+2,
		       previous_line,strlen(previous_line));
    }
  else
    {
      XClearArea(dpy,window,0,
		 2*(font->ascent+font->descent+2),10000,10000,0);
    }
  XDrawImageString(dpy,window,myGC,2,2*(font->ascent+font->descent+2)+
		   font->ascent+2,Text,pos);
  w=XTextWidth(font,Text,pos);  
  XDrawLine(dpy,window,myGC,4+w,2*(font->ascent+font->descent+2)+2,
	    4+w,2*(font->ascent+font->descent+2)+
	    font->ascent+font->descent);
}
