/* -*-c-*- */
/* Copyright (C) 2002 the late Joey Shutup.
 *
 * http://www.streetmap.co.uk/streetmap.dll?postcode2map?BS24+9TZ
 *
 * No guarantees or warranties 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.
 */
/*
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include "config.h"

#include <stdio.h>
#include <signal.h>

#include "libs/fvwmlib.h"
#include "libs/Module.h"
#include "libs/fvwmsignal.h"
#include "libs/Parse.h"
#include "libs/Strings.h"

/* Globals */
static ModuleArgs* module;
static int fd[2]; /* communication pipes */

/* forward declarations */
static RETSIGTYPE signal_handler(int signal);
static void set_signals(void);
static void parse_config(void);
static void parse_config_line(char *line);
static void parse_message_line(char *line);
static void main_loop(void) __attribute__((__noreturn__));
static void parse_colorset(char *line);

int main(int argc, char **argv)
{
	module = ParseModuleArgs(argc,argv,0); /* no alias allowed */
	if (module==NULL)
	{
		fprintf(stderr,
			"FvwmTheme "VERSION" should only be executed by fvwm!\n"
			);
		exit(1);
	}

	fprintf(
		stderr,
		"%s is obsolete, see the Colorset section of the fvwm(1) man"
		" page\n", module->name);
	set_signals();

	/* note the communication pipes */
	fd[0] = module->to_fvwm;
	fd[1] = module->from_fvwm;

	/* get the initial configuration options */
	parse_config();

	/* tell fvwm we're running */
	SendFinishedStartupNotification(fd);

	/* sit around waiting for something to do */
	main_loop();
}

static void main_loop(void)
{
	FvwmPacket *packet;
	fd_set_size_t fd_width;
	fd_set in_fdset;

	fd_width = fd[1] + 1;
	FD_ZERO(&in_fdset);

	while (True)
	{
		/* garbage collect */
		alloca(0);

		FD_SET(fd[1], &in_fdset);
		/* wait for an instruction from fvwm or a timeout */
		if (fvwmSelect(fd_width, &in_fdset, NULL, NULL, NULL) < 0)
		{
			fprintf(stderr, "%s: select error!\n", module->name);
			exit(-1);
		}

		packet = ReadFvwmPacket(fd[1]);
		if (!packet)
			exit(0);

		/* handle dynamic config lines and text messages */
		switch (packet->type)
		{
		case M_CONFIG_INFO:
			parse_config_line((char *)&packet->body[3]);
			SendUnlockNotification(fd);
			break;
		case M_STRING:
			parse_message_line((char *)&packet->body[3]);
			SendUnlockNotification(fd);
			break;
		}
	}
}

/* config options, the first NULL is replaced with *FvwmThemeColorset */
/* the second NULL is replaced with *FvwmThemeReadWriteColors */
/* FvwmTheme ignores any "colorset" lines since it causes them */
static char *config_options[] =
{
	"ImagePath",
	"ColorLimit",
	NULL,
	NULL,
	NULL
};

static void parse_config_line(char *line)
{
	char *rest;

	switch(GetTokenIndex(line, config_options, -1, &rest))
	{
	case 0: /* ImagePath */
		break;
	case 1: /* ColorLimit */
		break;
	case 2: /* *FvwmThemColorset */
		parse_colorset(rest);
		break;
	case 3: /* *FvwmThemeReadWriteColors */
		SendText(fd, "ReadWriteColors", 0);
		break;
	}
}

/* translate a colorset spec into a colorset structure */
static void parse_colorset(char *line)
{
	SendText(fd, CatString2("Colorset ", line), 0);
}

/* SendToModule options */
static char *message_options[] = {"Colorset", NULL};

static void parse_message_line(char *line)
{
	char *rest;

	switch(GetTokenIndex(line, message_options, -1, &rest))
	{
	case 0:
		parse_colorset(rest);
		break;
	}
}

static void parse_config(void)
{
	char *line;

	/* prepare the tokenizer array, [0,1] are ImagePath and ColorLimit */
	config_options[2] = safemalloc(module->namelen + 10);
	sprintf(config_options[2], "*%sColorset", module->name);
	config_options[3] = safemalloc(module->namelen + 17);
	sprintf(config_options[3], "*%sReadWriteColors", module->name);

	/* set a filter on the config lines sent */
	line = safemalloc(module->namelen + 2);
	sprintf(line, "*%s", module->name);
	InitGetConfigLine(fd, line);
	free(line);

	/* tell fvwm what we want to receive */
	SetMessageMask(fd, M_CONFIG_INFO | M_END_CONFIG_INFO | M_SENDCONFIG
		       | M_STRING);

	/* loop on config lines, a bit strange looking because GetConfigLine
	 * is a void(), have to test $line */
	while (GetConfigLine(fd, &line), line)
		parse_config_line(line);

	SetSyncMask(fd, M_CONFIG_INFO | M_STRING);
}

static void set_signals(void) {
#ifdef HAVE_SIGACTION
	struct sigaction  sigact;

	sigemptyset(&sigact.sa_mask);
	sigaddset(&sigact.sa_mask, SIGPIPE);
	sigaddset(&sigact.sa_mask, SIGINT);
	sigaddset(&sigact.sa_mask, SIGHUP);
	sigaddset(&sigact.sa_mask, SIGTERM);
# ifdef SA_INTERRUPT
	sigact.sa_flags = SA_INTERRUPT;
# else
	sigact.sa_flags = 0;
# endif
	sigact.sa_handler = signal_handler;
	sigaction(SIGPIPE, &sigact, NULL);
	sigaction(SIGINT, &sigact, NULL);
	sigaction(SIGHUP, &sigact, NULL);
	sigaction(SIGTERM, &sigact, NULL);
#else
	/* We don't have sigaction(), so fall back to less robust methods.  */
#ifdef USE_BSD_SIGNALS
	fvwmSetSignalMask(sigmask(SIGPIPE)
			  | sigmask(SIGINT)
			  | sigmask(SIGHUP)
			  | sigmask(SIGTERM));
#endif
	signal(SIGPIPE, signal_handler);
	signal(SIGINT, signal_handler);
	signal(SIGHUP, signal_handler);
	signal(SIGTERM, signal_handler);
#ifdef HAVE_SIGINTERRUPT
	siginterrupt(SIGPIPE, 1);
	siginterrupt(SIGINT, 1);
	siginterrupt(SIGHUP, 1);
	siginterrupt(SIGTERM, 1);
#endif
#endif
}

static RETSIGTYPE signal_handler(int signal)
{
	fprintf(stderr, "%s quiting on signal %d\n", module->name, signal);
	exit(signal);
	SIGNAL_RETURN;
}
