/*
 * Copyright 1992 The University of Newcastle upon Tyne
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation for any purpose other than its commercial exploitation
 * is hereby granted without fee, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of The University of Newcastle upon Tyne not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission. The University of
 * Newcastle upon Tyne makes no representations about the suitability of
 * this software for any purpose. It is provided "as is" without express
 * or implied warranty.
 * 
 * THE UNIVERSITY OF NEWCASTLE UPON TYNE DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
 * NEWCASTLE UPON TYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 * 
 * Author:  Jim Wight (j.k.wight@newcastle.ac.uk)
 *          Computing Laboratory, University of Newcastle upon Tyne, UK
 */

#include <X11/Xos.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <stdio.h>
#include <signal.h>
#include "version.h"
#include "patchlevel.h"

extern char *malloc();

/* R4 concession */
#ifndef XPointer
#define XPointer caddr_t
#endif

#define APP_NAME "xcuckoo"
#define APP_CLASS "XCuckoo"

#define DEFAULT_WINDOW "TWM Icon Manager"
#define DEFAULT_UPDATE 60

#define MAXLEN 128

typedef enum {Replace, Prepend, Append} Mode;

Display *dpy;
Window target;
char windowName[MAXLEN];
char *iconName = NULL;
Mode mode = Replace;
int iconToo;
char *separator = " ";

/*
 * This routine pinched from xwininfo
 *
 * Window_With_Name: routine to locate a window with a given name on a display.
 *                   If no window with the given name is found, 0 is returned.
 *                   If more than one window has the given name, the first
 *                   one found will be returned.  Only top and its subwindows
 *                   are looked at.  Normally, top should be the RootWindow.
 */
Window Window_With_Name(dpy, top, name)
     Display *dpy;
     Window top;
     char *name;
{
        Window *children, dummy;
        unsigned int nchildren;
        int i;
        Window w=0;
        char *window_name;

        if (XFetchName(dpy, top, &window_name) && !strcmp(window_name, name))
          return(top);

        if (!XQueryTree(dpy, top, &dummy, &dummy, &children, &nchildren))
          return(0);

        for (i=0; i<nchildren; i++) {
                w = Window_With_Name(dpy, children[i], name);
                if (w)
                  break;
        }
        if (children) XFree ((char *) children);
        return(w);
}

void
SetWMNames(window, winName, icoName)
     Window window;
     char *winName, *icoName;
{
    XTextProperty textProperty;
    char *name = malloc((unsigned) (strlen(windowName) + strlen(winName) +
			(iconToo ? strlen(iconName) + strlen(icoName) : 0)));
    if (!name)
    {
	return;
    }

    *name = '\0';
    if (mode == Append)
    {
	strcat(name, windowName);
	strcat(name, separator);
    }
    strcat(name, winName);
    if (mode == Prepend)
    {
	strcat(name, separator);
	strcat(name, windowName);
    }
    textProperty.value = (unsigned char *) name;
    textProperty.encoding = XA_STRING;
    textProperty.format = 8;
    textProperty.nitems = (unsigned long) strlen(name);
    XSetWMName(dpy, window, &textProperty);

    if (iconToo && iconName)
    {
	*name = '\0';
	if (mode == Append)
	{
	    strcat(name, iconName);
	    strcat(name, separator);
	}
	strcat(name, icoName);
	if (mode == Prepend)
	{
	    strcat(name, separator);
	    strcat(name, iconName);
	}

	textProperty.value = (unsigned char *) name;
	textProperty.nitems = (unsigned long) strlen(name);
	XSetWMIconName(dpy, window, &textProperty);
    }

    XFree(name);
}

int 
xerrorhandler(dpy, event)
     Display *dpy;
     XErrorEvent *event;
{
    /*
     * Parasite dies silently along with its host
     */

    exit(0);
}

#ifdef SIGNALRETURNSINT
int
#else
void
#endif
sigtrap()
{
    /*
     * Restore original window title if process killed
     */

    mode = Replace;
    SetWMNames(target, windowName, iconName);
    XSync(dpy, 0);
    exit(0);
}

static XrmOptionDescRec options[] = {
        { "-display",   ".display",   XrmoptionSepArg, (XPointer) NULL   },
        { "-window",    ".window",    XrmoptionSepArg, (XPointer) NULL   },
        { "-icon",      ".icon",      XrmoptionNoArg,  (XPointer) "True" },
        { "-command",   ".command",   XrmoptionSepArg, (XPointer) NULL   },
        { "-mode",      ".mode",      XrmoptionSepArg, (XPointer) NULL   },
        { "-separator", ".separator", XrmoptionSepArg, (XPointer) NULL   },
        { "-update",    ".update",    XrmoptionSepArg, (XPointer) NULL   },
        { "-wait",      ".wait",      XrmoptionSepArg, (XPointer) NULL   },
        { "-version",   ".version",   XrmoptionNoArg,  (XPointer) "True" },
};

main(argc, argv)
     int argc;
     char **argv;
{
    char *display = NULL;
    char command[MAXLEN];
    unsigned update;
    int wait;
    long t;
    struct tm *timeinfo;
    char timebuf[MAXLEN];
    FILE *f;
    char resource[MAXLEN];
    char Resource[MAXLEN];
    char *type;
    XrmDatabase cdb = NULL, db = NULL;
    XrmValue value;
    XTextProperty property;
                  
    XrmInitialize();

    XrmParseCommand(&cdb, options, sizeof(options)/sizeof(options[0]),
		    APP_NAME, &argc, argv);

    sprintf(resource, "%s.display", APP_NAME);
    sprintf(Resource, "%s.Display", APP_CLASS);
    if (XrmGetResource(cdb, resource, Resource, &type, &value))
    {
	display = (char *) value.addr;
    }
    
    if ( !(dpy = XOpenDisplay(display)) )
    {
	fprintf(stderr, "%s: Couldn't open display %s\n",
		                             APP_NAME, XDisplayName(display));
	exit (1);
    }

    sprintf(resource, "%s.version", APP_NAME);
    sprintf(Resource, "%s.Version", APP_CLASS);
    if (XrmGetResource(cdb, resource, Resource, &type, &value))
    {
	printf("Version %s Patchlevel %d\n", VERSION, PATCHLEVEL);
	exit(0);
    }

    XrmMergeDatabases(XrmGetStringDatabase(XResourceManagerString(dpy)), &db);
    XrmMergeDatabases(cdb, &db);

    sprintf(resource, "%s.window", APP_NAME);
    sprintf(Resource, "%s.Window", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	strcpy(windowName, (char *) value.addr);
    }
    else
    {
	strcpy(windowName, DEFAULT_WINDOW);
    }
    
    sprintf(resource, "%s.icon", APP_NAME);
    sprintf(Resource, "%s.Icon", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	iconToo = (strcmp((char *) value.addr, "true") == 0);
    }

    sprintf(resource, "%s.command", APP_NAME);
    sprintf(Resource, "%s.Command", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	strcpy(command, (char *) value.addr);
    }
    else
    {
	command[0] = '\0';
    }

    sprintf(resource, "%s.mode", APP_NAME);
    sprintf(Resource, "%s.Mode", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	if ( strcmp((char *) value.addr, "prepend") == 0 )
	{
	    mode = Prepend;
	}
	else if ( strcmp((char *) value.addr, "append") == 0 )
	{
	    mode = Append;
	}
	else
	{
	    mode = Replace;
	}
    }

    sprintf(resource, "%s.separator", APP_NAME);
    sprintf(Resource, "%s.Separator", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	separator = (char *) value.addr;
    }

    sprintf(resource, "%s.update", APP_NAME);
    sprintf(Resource, "%s.Update", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	update = atoi((char *) value.addr);
    }
    else
    {
	update = DEFAULT_UPDATE;
    }
    
    sprintf(resource, "%s.wait", APP_NAME);
    sprintf(Resource, "%s.Wait", APP_CLASS);
    if (XrmGetResource(db, resource, Resource, &type, &value))
    {
	wait = atoi((char *) value.addr);
    }
    else
    {
	wait = 0;
    }

    while ( wait >= 0 )
    {
	if ( (target = Window_With_Name(dpy, DefaultRootWindow(dpy), windowName)) )
	{
	    break;
	}
	else
	{
	    wait--;
	    sleep(1);
	}
    }

    if (target)
    {
	if (iconToo && XGetWMIconName(dpy, target, &property) != 0)
	{
	    iconName = (char *) property.value;
	}

	(void) signal(SIGHUP, sigtrap);
	(void) signal(SIGINT, sigtrap);
	(void) signal(SIGQUIT, sigtrap);
	(void) signal(SIGTERM, sigtrap);

	XSetErrorHandler(xerrorhandler);

	for(;;)
	{
	    time(&t);
	    timeinfo = localtime(&t);

	    *timebuf = '\0';
	    if (*command)
	    {
		if ( (f = popen(command, "r")) )
		{
		    (void) fgets(timebuf, MAXLEN, f);
		    pclose(f);
		    if (timebuf[strlen(timebuf) - 1] == '\n')
		    {
			timebuf[strlen(timebuf) - 1] = '\0';
		    }
		}
	    }
	    else
	    {
		sprintf(timebuf, "%02d:%02d",
			                  timeinfo->tm_hour, timeinfo->tm_min);
	    }
	    if (*timebuf)
	    {
		SetWMNames(target, timebuf, timebuf);
		XSync(dpy, 0);
	    }
	    
	    if ( (update % 60) == 0 )
	    {
		sleep(update - timeinfo->tm_sec);
	    }
	    else
	    {
		sleep(update);
	    }
	}
    }
    else
    {
	fprintf(stderr, "%s: No window named \"%s\"\n", APP_NAME, windowName);
    }
}

