#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "x10io.H"
#include "general.H"
#include "cli.H"


void lockUnit(int hc, int uc)
{
	char lockbuf[PATH_MAX];
	FILE *fp;
	long pidin;
	pid_t pid;

	sprintf(lockbuf, "/var/lock/x10/%c%02d", (char) hc, uc);
	if ((fp = fopen(lockbuf, "r")) == NULL) {
		// No other process.  Claim it.
	} else {
		// Other process.  Kill it and claim it.
		if (fscanf(fp, "%ld", &pidin) > 0) {
			pid = pidin;
			kill(pid, SIGTERM);
		}
		fclose(fp);
		sleep(1);

		char otherSocketBuf [PATH_MAX];
		sprintf(otherSocketBuf, "/tmp/xc%ld", pidin);
		unlink(otherSocketBuf);
	}
	unlink(lockbuf);

	if (fp = fopen(lockbuf, "w")) {
		fprintf(fp, "%ld\n", (long) getpid());
		fclose(fp);
	} else {
		perror (lockbuf);
	}
}


void unlockUnit(int hc, int uc)
{
	char buf[PATH_MAX];

	sprintf(buf, "/var/lock/x10/%c%02d", (char) hc, uc);
	unlink(buf);
}



TOKEN_TABLE levels [] = {
	{ 0,	"off" },
	{ 0,	"min" },
	{ 20,	"full" },
	{ 20,	"max" },
	{ 20,	"on" },
	{ 10,	"half" },
	{ -1,	NULL }
};

int fade(int housecode, int unit, char **argv)
{
	struct x10state state;
	int cmd;
	int delay;		// Delay between commands, in deciseconds
	int tlevel;		// Target level
	int intervals = 0;	// Number of steps
	int turnoff = 0;	// Turn off after dim?
	int uponly = 0;		// Only fade up?
	int downonly = 0;	// Only fade down?
	int override = 0;	// Override level data in table
	int slowstart = 0;	// Slow start fade
	int faststart = 0;	// Fast start fade
	int fadeTime;		// Time specified on command line.
	char *error;

	if (!unit)
		return (FAIL_UNITNEEDED);

	if (strcasecmp (*argv, "off") == 0) {
		turnoff = 1;
	}
	tlevel = get_token (*argv, levels);
	if (tlevel < 0) {
		tlevel = strtol (*argv, &error, 10);
		if ((error && *error && strcmp (error, "%") != 0) ||
			 tlevel < 0 || tlevel > 100) {
			fprintf (stderr, "%s: Invalid level '%s'.\n",
				 program_name, *argv);
			return (FAIL_PARSE);
		}
		// levels are 0-20, but we accept a percent -- divide by 5
		// to bring value into our range.
		tlevel /= 5;
	}
	argv++;

	if (!*argv) {
		fprintf (stderr, "%s: Fade time not specified.\n",
			 program_name);
		return (FAIL_PARSE);
	}
		
	fadeTime = strtol (*argv, &error, 10);
	if ((error && *error) || fadeTime < 0) {
		fprintf (stderr, "%s: Invalid fade time '%s'.\n",
			 program_name, *argv);
		return (FAIL_PARSE);
	}
	argv++;

	while (*argv) {
		if (!strcmp(*argv, "override"))
			override = 1;
		else if (!strcmp(*argv, "up"))
			uponly = 1;
		else if (!strcmp(*argv, "down"))
			downonly = 1;
		else if (!strcmp(*argv, "slowstart")) {
			slowstart = 1;
			faststart = 0;
		} else if (!strcmp(*argv, "faststart")) {
			faststart = 1;
			slowstart = 0;
		} else {
			fprintf (stderr, "%s: Unknown option '%s'.\n",
				program_name, *argv);
			return (FAIL_PARSE);
		
		}
		argv++;
	}

	int status = mapX10state(housecode, unit, &state);
	if (status != 0)
		return (status);
	if (!uponly && !downonly) {
		uponly = state.flags & XS_UPONLY;
		if (!uponly) {
			downonly = state.flags & XS_DOWNONLY;
		}
	}
	
	if ((tlevel >= state.level) && (!downonly)) {
		intervals = tlevel - state.level;
		cmd = X10_BRIGHT;
	}
	if ((tlevel < state.level) && (!uponly)) {
		intervals = state.level - tlevel;
		cmd = X10_DIM;
	}

	if (override) {
		intervals = 20;
		if (tlevel == 0) {
			cmd = X10_DIM;
		}
	}

	if (debug_level) {
		fprintf (stderr, "intervals=%d tlevel=%d time=%d turnoff=%d\n",
			 intervals, tlevel, fadeTime, turnoff);
	}

	lockUnit(housecode, unit);

	// If we're turning it off, we don't turn it on first.
	if (!turnoff) {
		status = send_command (housecode, unit, X10_ON, 1);
	} else {
		// If it's already off, there's nothing to do.
		// Fall through and shut it off just in case it's not in
		// the expected state, though.
		if (!(state.flags & XS_ON) & !override)
			intervals = 0;
	}

	if (debug_level) {
		printf("Starting %sfade %s%s, %d interval%s, old flags=%d.\n",
			slowstart ? "slow start " :
				 (faststart ? "fast start " : ""),
			(cmd == X10_BRIGHT) ? "up" : "",
			(cmd == X10_DIM) ? "down" : "",
			intervals, (intervals == 1) ? "" : "s",
			state.flags);
	}
	fflush(stdout);

	for (int i = 0; status == SUCCESS && i < intervals; i++) {
		if (status = send_command (housecode, unit, cmd, 1))
			return (status);

		if (faststart)
				delay = (fadeTime * 600) / (1 << (intervals - i));
		else if (slowstart)
				delay = (fadeTime * 600) / (2 << i);
		else if (intervals)
				delay = (fadeTime * 600) / intervals;
	        else
				delay = 10;

		sleep (MAX (delay / 10, 1));
	}

	if (debug_level)
		printf("Fade complete\n");

	if (tlevel == 0 && status == SUCCESS && turnoff) {
		status = send_command (housecode, unit, X10_OFF, 1);
	}
	unlockUnit (housecode, unit);
	return (status);
}

