#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>

#include <math.h>

#define BEAGLE_REAL

char *RTypeNames[] = { "10K", "50K", "" };


int state=0;
#define STATE_STARTUP 0     /* System not stable yet, wait */
#define STATE_TEMPS_RDY 1   /* We have collected 10 temperature values, so temp should be ok */
#define STATE_TIME_RDY 2    /* Time was set by network, network should be up */


/* Max number of clients */
#define MAX_CONNECTS 20

/* Number of things that can be controlled */
#define MAX_CONTROL 6

/* Number of sensors */
#define MAX_SENSORS 4

int debug=0;

struct pollfd pfds[MAX_CONNECTS];
int numPfds;

#define PGM_0   1       /* Program 0 wants relay on */
#define PGM_1   2       /* Program 1 wants relay on */
#define PGM_2   4       /* Program 2 wants relay on */
#define PGM_3   8       /* Program 3 wants relay on */
#define REQ    16       /* On because of a requires */
#define FRZ    32       /* On because freeze protect */
#define TMO    64       /* Manual time out request  */
#define MAN   128       /* On because of manual request, will stay on forever */

/* Manual on stays on until manual off */
/* Manual off turns all reasons off except FRZ and REQ */

char *controlNames[MAX_CONTROL];
int controlGPIO[MAX_CONTROL];                       /* WHich GPIO pin to toggle to change relay state (0 is none)*/
char *sensorNames[MAX_SENSORS];
double RRefs[MAX_SENSORS];                          /* The bias resistor for each sensor. A 10K is normally 10K and a 50K is 50K */
double R25s[MAX_SENSORS];                           /* Calibration value, normally 10K at 25C for a 10K and 50K for a 50K */
double ROffsets[MAX_SENSORS];                      /* Offset value in degrees, applied after calculation */
unsigned char sensorTypes[MAX_SENSORS];
int sensorVIN[MAX_SENSORS];                         /* Which A/D on beaglebone (0 is none) */
char *YN = "NY";

/* the 10K is a standard Jandy thermistor, encased in plastic so pretty slow response */
#define THERM10K 0
/* The 50K is a Vishay 50K thermistor, much faster response time */
#define THERM50K 1

typedef struct RConstants {
    double a;
    double b;
    double c;
    double d;
} RConstants;

RConstants RConstantsTable[3] = {
    {3.354016e-3, 2.76985e-4, 2.620131e-6, 6.383091e-8 },
    {3.354016e-3, 2.460382e-4, 3.405377e-6, 1.034240e-7 },
    {0, 0, 0, 0}} ;

unsigned char freezeProtect[MAX_CONTROL];           /* If set, then this element has freeze protection */
unsigned char freezeSense[MAX_SENSORS];             /* Which sensors are used for determining freeze. If any are beyond thresh, trigger */
unsigned char controlMain[MAX_CONTROL];             /* Desired Setting, updateRelays will update relay setting if needed */
unsigned char actualMain[MAX_CONTROL];              /* Current settings, set during updateRelay */
int freezeOnTemp = 33;                              /* When temp reaches this turn on freeze protected elements */
int freezeOffTemp = 36;                             /* When temp reaches this and freeze was on, turn off freeze */
unsigned char requires[MAX_CONTROL];                /* If set to anything besides itself, then before this object is energized, energize requires */
int requireTime[MAX_CONTROL];                       /* If we have a requires, how long must we wait in seconds until turning on */
int pgmOnTimes[MAX_CONTROL][4];                     /* Up to 4 programmable times (in minutes) */
int pgmOffTimes[MAX_CONTROL][4];                    /* Up to 4 times (in minutes) */
int pgmOffDayTimes[MAX_CONTROL][4];                 /* B/C a manual off request should turn ff any active timers */
unsigned char pgmDays[MAX_CONTROL][4];              /* which days do we run, typically all */
time_t TMO_offTime[MAX_CONTROL];                    /* If a manual request was made, at what time in the future should we turn off */

time_t startTime[MAX_CONTROL];                      /* If we are on, when did we start, no matter who started us. */

time_t manualStartTime[MAX_CONTROL];                /* For TMO mode, when was the request initiated */

double instTemps[MAX_SENSORS];
double avgTemps[MAX_SENSORS];                       /* Averaged values to reduce noise */

/* 6 max objects, 20 chars max or 120 chars + timers(4) * 6 * 12 ~300 or 420 total chars for  query */
char wbuf[2048];                                    /* Generic buffer for writing */


/*
#define BEAGLE_REAL
*/

/* Parse string of form 12:30 into number of minutes past midnight */
static int parseTime(char *s)
{
int j, k, minutes;

    for(j=0; j<10 && s[j]; j++)
	{
	if( s[j] == '-' )
	    return(2000);   /* Not set */
	if( s[j] == ':' )
	    {
	    minutes = atoi(s) * 60;
	    goto MIN;
	    }
	}
    return(2000);   /* Badly formatted */

MIN:
    j=j+1;
    for(k=j; s[k] && k < 10; k++)
	;
    if( s[k] )
	return(2000);
    return( atoi(&s[j]) + minutes);
}

void makeMinuteString(int t, char *s)
{
int hours, minutes;

    if( t < 0 || t >= 1440 )
	strcpy(s, "-");
    else
	{
	hours = t/60;
	minutes = t%60;
	sprintf(s, "%02d:%02d", hours, minutes);
	}
}

/* COnfig string has object name which could have blanks. Accept either abc or "abc" or "pool pump" */
/* String is # "name" freezeFlag requires */
parseSConfig(char *s, int *i, char *name, char *frz, int *requires)
{
int j, k, l, quoted;

    for(j=0; s[j]; j++)
	if( isdigit(s[j]) )
	    break;
    if( !isdigit(s[j]) )
	return(0);

    *i = atoi(&s[j]);

    for(; s[j]; j++)
	if( isspace(s[j]) )
	    break;

    if( !s[j] )
	return(0);

    for(; s[j]; j++)
	if( !isspace(s[j]) )
	    break;

    if( !s[j] )
	return(0);

    if( s[j] == '"' )
	{   /* Parse to matching quote */
	for(l=0, k=j+1; s[k] ; k++)
	    {
	    if( s[k] == '"' )
		break;
	    else if(l<20)
		name[l++] = s[k];
	    }
	if( s[k] != '"' )
	    return(0);
	}
    else
	{   /* Parse to space */
	for(l=0, k=j; s[k] ; k++)
	    {
	    if( isspace(s[k]) )
		break;
	    else if(l<20)
		name[l++] = s[k];
	    }
	if( !isspace(s[k]) )
	    return(0);
	}
    name[l] = '\0';
    j = k+1;

    for(; s[j]; j++)
	if( !isspace(s[j]) )
	    break;

    for(l=0, k=j; s[k] ; k++)
	{
	if( isspace(s[k]) )
	    break;
	else if(l<1)
	    frz[l++] = s[k];
	}
    frz[l] = '\0';
    if( !isspace(s[k]) )
	return(0);
    *requires = atoi(&s[k]);
    return(1);
}

setupListenSocket(int portNum)
{
int i, listenSocket;
unsigned int x;
unsigned char *flipit, sv;
struct sockaddr_in in;
struct sockaddr_in6 in6;

    listenSocket = socket(PF_INET6, SOCK_STREAM, 0);
    x = INADDR_ANY;
    memcpy((void *) &(in6.sin6_addr), (void *) &in6addr_any, sizeof(in6));
    in6.sin6_family = AF_INET6;
    in6.sin6_port = portNum;
    flipit = (unsigned char *) &in6.sin6_port;
    sv = flipit[0];
    flipit[0] = flipit[1];
    flipit[1] = sv;

    if( bind(listenSocket, (const struct sockaddr*) &in6, sizeof(in6)) != 0 )
	{
	printf("Failed to bind listen socket\n");
	return(0);
	}
    if( listen(listenSocket, 5) != 0 )
	{
	printf("Failed to set up listen on socket\n");
	return(0);
	}
    pfds[0].fd = listenSocket;
    pfds[0].events = POLLIN | POLLOUT;
    numPfds=1;
    return(1);
}

/* Equation appeas accurate to about .2 degrees F at 32 F */
updateTemps(time_t curTime)
{
FILE *fp;
int sensor, type;
char value[100], probeString[128];
double lx;
double T, B, Tf;
double Rt;
double V; /* Range is 12 bits ie 0-4095 */
double cnt;
static int avgCnt=0;

    for(sensor=0; sensor<MAX_SENSORS; sensor++)
	{
	if( !sensorNames[sensor])
	    continue;
	sprintf(probeString, "/sys/bus/iio/devices/iio:device0/in_voltage%d_raw", sensorVIN[sensor]);
	if( (fp = fopen(probeString, "r")) != NULL)
	    {
	    if( fgets(value, 50, fp) != NULL && value[0] != '\0' )
		{
		cnt = atoi(value);
		V = cnt / 4095;
		Rt = V * RRefs[sensor] / ( 1 - V);

		lx = log(Rt/R25s[sensor]);
		type = sensorTypes[sensor];
		T = RConstantsTable[type].a + RConstantsTable[type].b * lx +
		    RConstantsTable[type].c * lx * lx + RConstantsTable[type].d * lx * lx * lx;
		T = (1 / T);
		Tf = (T-273) * (9.0 / 5.0) + 32.0;
		Tf = Tf + ROffsets[sensor];
		instTemps[sensor] = Tf;
		if( debug )
			printf("Rt %f cnt %f T %f\n", Rt, cnt, Tf);
		if( avgCnt == 0 )
		    avgTemps[sensor] = instTemps[sensor];
		else
		    avgTemps[sensor] = (15*avgTemps[sensor] + instTemps[sensor]) / 16;
		if( avgCnt != 10 )
		    {
		    if( avgCnt == 9 )
			{
			if( debug )
			    printf("Temps rdy %s\n", ctime(&curTime));
			state |= STATE_TEMPS_RDY;
			}
		    avgCnt++;
		    }
		}
	    else
		printf("gets failed on %s\n", probeString);
	    fclose(fp);
	    }
	else
		printf("open failed on %s\n", probeString);

	}
}

/* Write the buffer to the remote if it is still active */
sendCmd(char *buf, int ix)
{
int len;

    if( pfds[ix].fd != -1 )
	{
	len = strlen(buf);
	if( write(pfds[ix].fd, buf, len) != len)
	    {
	    close(pfds[ix].fd);
	    pfds[ix].fd = -1;
	    printf("Write failed on ix %d; closing\n", ix);
	    }
	}
}

/* Process command coming from pfds index ix */
processCmd(char *cmd, time_t curTime, int ix)
{
char name[24], flag[24], RType[24];
int i, j, req;
double R25, RRef, ROffset;

    if( strncasecmp(cmd, "SfreezeOn", 9) == 0)
	{
	freezeOnTemp = atoi(&cmd[10]);
	if( freezeOnTemp < 22 )
	    freezeOnTemp = 22;
	if( freezeOnTemp > 40 )
	    freezeOnTemp = 40;
	}
    else if( strncasecmp(cmd, "SfreezeOff", 10) == 0)
	{
	freezeOffTemp = atoi(&cmd[11]);
	if( freezeOffTemp < 34)
	    freezeOffTemp = 34;
	if( freezeOffTemp > 45 )
	    freezeOffTemp = 45;
	}
    else if( strncasecmp(cmd, "Sconfig", 7) == 0)
	{   /* config # name freeze(Y/N) reqNum */
	    /* config 0 filter Y 0 */
	if( parseSConfig(&cmd[8], &i, name, flag, &req) == 0)
	    {
	    printf("Bad command %s\n", cmd);
	    return(0);
	    }
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    return(0);
	    }
	if( req < 0 || req >= MAX_CONTROL )
	    {
	    printf("Bad req index %s\n", cmd);
	    return(0);
	    }
	requires[i] = req;
	if( name[0] != '.' && (!controlNames[i] || strcmp(controlNames[i], name) != 0) )
	    {
	    if( controlNames[i] )
		free(controlNames[i]);
	    controlNames[i] = strdup(name);
	    }
	if( flag[0] == 'Y' )
	    freezeProtect[i] = 1;
	else if(flag[0] == 'N' )
	    freezeProtect[i] = 0;

	}
    else if( strncasecmp(cmd, "Stimers", 7) == 0)
	{   /* config # on off on off on off on off */
	    /* config 0 A 600 660 A 2000 2000 A 2000 2000 A 2000 2000 filter pump, on at 10am off at 11, one pgm only */
	    /* days can be A for all or 0123456 where 0 is sunday, so 02 would be sunday tuesday only */
	int k, on, off;
	unsigned char daysc;
	char days[20], ons[20], offs[20];


	if( sscanf(&cmd[8], "%d %d %19s %19s %19s",
		&i, &j, days, ons, offs) != 5)
	    {
	    printf("Bad command %s\n", cmd);
	    return(0);
	    }
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    return(0);
	    }
	if( j < 0 || j >= 4 )
	    {
	    printf("Bad timer index %s\n", cmd);
	    return(0);
	    }
	if( days[0] == 'A' )
	    daysc = 0xff;
	else
	    {
	    daysc = 0;
	    for(k=0; days[k]; k++)
		daysc |= (1<<(days[k]-'0'));
	    }
	on = parseTime(ons);
	off = parseTime(offs);
	if( on > off )
	    on = off = 2000;
	pgmOnTimes[i][j] = on;
	pgmOffDayTimes[i][j] = pgmOffTimes[i][j] = off;
	pgmDays[i][j] = daysc;
	}
    else if( strncasecmp(cmd, "Ssensor", 7) == 0)
	{   /* sensor # name freezeDetect(Y/N) */
	    /* sensor 0 water N */
	if( sscanf(&cmd[8], "%d %20s %10s %10s %lf %lf %lf", &i, name, flag, RType, &R25, &RRef, &ROffset) != 7)
	    printf("Bad command %s\n", cmd);
	if( i < 0 || i >= MAX_SENSORS )
	    {
	    printf("Bad send index %s\n", cmd);
	    return(0);
	    }
	if( flag[0] == 'Y' )
	    freezeSense[i] = 1;
	else
	    freezeSense[i] = 0;
	if( !sensorNames[i] || strcmp(sensorNames[i], name) != 0 )
	    {
	    if( sensorNames[i] )
		free(sensorNames[i]);
	    sensorNames[i] = strdup(name);
	    }
	for(j=0; RTypeNames[j]; j++)
	    if( strcmp(RType, RTypeNames[j]) == 0 )
		break;
	sensorTypes[i] = j;
	if( !RTypeNames[j] )
	    {
	    printf("Bad Type Name %s\n", RType);
	    freezeSense[0] = 0;
	    free(sensorNames[i]);
	    sensorNames[i][0] = '\0';
	    return(0);
	    }
	R25s[i] = R25;
	RRefs[i] = RRef;
	ROffsets[i] = ROffset;
	}
    /* Used to tweak a sensor */
    else if( strncasecmp(cmd, "SAsensor", 8) == 0)
	{   /* sensor name R25S, TAdjust */
	    /* sensor 0 water N */
	if( sscanf(&cmd[9], "%20s %lf %lf", name, &R25, &ROffset) != 3)
	    printf("Bad command %s\n", cmd);
	for(i=0; i<MAX_SENSORS; i++)
	    if( sensorNames[i] && strcmp(sensorNames[i], name) == 0 )
		break;
	if( i == MAX_SENSORS )
	    {
	    printf("No sensor named %s\n", name);
	    return(0);
	    }
	R25s[i] = R25;
	ROffsets[i] = ROffset;
	}
    else if( strncasecmp(cmd, "STMO", 4) == 0)
	{
	if( sscanf(&cmd[5], "%d %d", &i, &j) != 2 )
	    {   /* Timeout in minutes */
	    printf("Bad command %s\n", cmd);
	    return(0);
	    }
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    return(0);
	    }
	controlMain[i] |= TMO;
	TMO_offTime[i] = curTime + j * 60;
	}
    else if( strncasecmp(cmd, "Son", 3) == 0)
	{
	if( sscanf(&cmd[4], "%d", &i) != 1 )
	    {   /* Timeout in minutes */
	    printf("Bad command %s\n", cmd);
	    return(0);
	    }
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    return(0);
	    }
	controlMain[i] |= MAN;
	}
    else if( strncasecmp(cmd, "Soff", 4) == 0)
	{   /* Off is different from on, on just adds one more thing, off turns off everything */
	if( sscanf(&cmd[5], "%d", &i) != 1 )
	    {   /* Timeout in minutes */
	    printf("Bad command %s\n", cmd);
	    return(0);
	    }
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    return(0);
	    }
	/* If we turn off, any active timers get reset so they do not turn it back on again */
	for(j=0; j<4; j++)
	    if( controlMain[i] & (1<<j) )
		pgmOffDayTimes[i][j] = -1;
	if( controlMain[i] & TMO )
	    TMO_offTime[i] = 0;
	controlMain[i] = controlMain[i] & (FRZ|REQ);
	}
    else if( strncasecmp(cmd, "SSave", 5) == 0)
	{
	if( writeConfig("/root/poolDefaults") )
	    writeConfig("poolDefaults");
	}
    else    /* A status request */
	printf("Bad command %s\n", cmd);

    return(1);
}

/* Called after connection established so therm knows what current state is */
sendAll(int ix, time_t curTime)
{
char msg[128];

/* Send target temps first, so client has them when mode is sent */
	sprintf(msg, "RDY %s", ctime(&curTime));
	sendCmd(msg, ix);
}

processStatus(char *cmd, time_t curTime, int ix)
{
int i, j, k, t, len;

    if( ix <= 0 || ix >= numPfds )
	return(0);
    if( pfds[ix].fd == -1 )
	return(0);
    if( strncasecmp(cmd, "ITEMP", 5) == 0)          /* Temp and on/off should be most requested */
	{   /* Send air temps back */
	strcpy( wbuf, "QTEMP");
	len = 5;
	len += sprintf(&wbuf[len], " {%s", ctime(&curTime));
	wbuf[len-1] = '}';  /* Replace \n with } */
	for(i=0; i<MAX_SENSORS; i++)
	    if( sensorNames[i] )
		len += sprintf(&wbuf[len], " {%d \"%s\" %.1f}", i, sensorNames[i], avgTemps[i]);
	wbuf[len++] = '\n';
	wbuf[len++] = '\0';
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "ISTATUS", 6) == 0 )
	{   /* what index is what object and is it on or off currently */
	strcpy(wbuf, "QSTATUS");
	len = 7;
	for(i=0; i<MAX_CONTROL; i++)
	    if( controlNames[i] )
		len += sprintf(&wbuf[len], " {%d \"%s\" %d}", i, controlNames[i], actualMain[i]);

	wbuf[len++] = '\n';
	wbuf[len++] = '\0';
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "ITIMERS", 6) == 0 )
	{
	char dayStrings[10], onTimes[10], offTimes[10];

	strcpy(wbuf, "QTIMERS");
	len = 7;
	i = atoi(&cmd[7]);
	if( i < 0 || i >= MAX_CONTROL )
	    {
	    printf("Bad control index %s\n", cmd);
	    }
	else
	    {
	    if( controlNames[i] )
		{
		len += sprintf(&wbuf[len], " %d \"%s\"", i, controlNames[i]);
		for(t=0; t<4; t++)
		    {
		    if( pgmDays[i][t] == 0xff )
			strcpy(dayStrings, "A");
		    else
			{
			for(j=k=0; j<7; j++)
			    {
			    if( pgmDays[i][t] & (1<<j) )
				dayStrings[k++] = j+'0';
			    }
			if( k == 0 )
			    dayStrings[k++] = '-';
			dayStrings[k] = '\0';
			}
		    makeMinuteString(pgmOnTimes[i][t], onTimes);
		    makeMinuteString(pgmOffTimes[i][t], offTimes);
		    len += sprintf(&wbuf[len], " {%s %s %s}",
		    dayStrings, onTimes, offTimes);
		    }
		}
	    }
	wbuf[len++] = '\n';
	wbuf[len++] = '\0';
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "IFRZ", 4) == 0 )
	{
	strcpy(wbuf, "QFRZ");
	len = 4;
	sprintf(&wbuf[len], " %d %d\n", freezeOnTemp, freezeOffTemp);
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "ICONFIG", 7) == 0)
	{
	strcpy(wbuf, "QCONFIG");
	len = 7;
	for(i=0; i<MAX_CONTROL; i++)
	    if( controlNames[i] )
		len += sprintf(&wbuf[len], " {%d \"%s\" %c %d}", i, controlNames[i],
			    YN[freezeProtect[i]], requires[i]);
	wbuf[len++] = '\n';
	wbuf[len++] = '\0';
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "ISENSOR", 7) == 0)
	{
	strcpy( wbuf, "QSENSOR");
	len = 7;
	for(i=0; i<MAX_SENSORS; i++)
	    if( sensorNames[i] )
		len += sprintf(&wbuf[len], " {%d \"%s\" %c %s %0.1f %0.1f %0.1f}", i, sensorNames[i], YN[freezeSense[i]],
		    RTypeNames[sensorTypes[i]], R25s[i], RRefs[i], ROffsets[i]);
	wbuf[len++] = '\n';
	wbuf[len++] = '\0';
	sendCmd(wbuf, ix);
	}
    else if( strncasecmp(cmd, "DUMP", 4) == 0 )
	dumpSettings();
}

/* Actually trigger the relays */
updateRelays(time_t curTime)
{
char gpioString[128];
FILE *fp;
int i, j;

    for(i=0; i<MAX_CONTROL; i++)
	{
	if( !controlNames[i] )
	    continue;
	if( actualMain[i] != controlMain[i] )
	    {   /* May need to do something */
	    if( !controlMain[i] )   /* So we want to turn it off */
		{                   /* Does anyone else require us to be on ? */
		for(j=0; j<MAX_CONTROL; j++)
		    {
		    if( j == i )
			continue;
		    if( !controlNames[i] )
			continue;
		    if( requires[j] == i && actualMain[j] )
			{
			if( debug )
			    printf("Cannot turn off %s because %s is still on %s\n", controlNames[i], controlNames[j], ctime(&curTime));
			goto NEXT;
			}
		    }
		/* Really turn it off */
		if( debug )
		    printf("DeActivate %d(%s) status was %o %s", i, controlNames[i], controlMain[i], ctime(&curTime));
/* TEMP
		actualMain[i] = controlMain[i];
		startTime[i] = curTime;
/* ... */
		sprintf(gpioString, "/sys/class/gpio/gpio%d/direction", controlGPIO[i]);
		fp = fopen(gpioString, "w");
		if( fp != NULL )
		    {
		    if( debug )
			printf("Turn off %s\n", controlNames[i]);
		    fprintf(fp, "low\n");
		    actualMain[i] = controlMain[i];
		    fclose(fp);
		    }
		}
	    if( controlMain[i] )    /* Turn on if requires allows */
		{
		if( actualMain[i] ) /* Already on for some reason, keep it on but update */
		    {
		    actualMain[i] = controlMain[i];
		    goto NEXT;
		    }
		if( (j=requires[i]) != i )
		    {
		    if( !actualMain[j] || (curTime-startTime[j]) < requireTime[i] )
			goto NEXT;  /* No can do */
		    }
		if( debug )
		    printf("Activate %d(%s) status was %x %s", i, controlNames[i], controlMain[i], ctime(&curTime));
/* TEMP
		actualMain[i] = controlMain[i];
		startTime[i] = curTime;
/* ... */
		/* Really turn it on */
		sprintf(gpioString, "/sys/class/gpio/gpio%d/direction", controlGPIO[i]);
		fp = fopen(gpioString, "w");
		if( fp != NULL )
		    {
		    if( debug )
			printf("Turn on %s %s", controlNames[i], ctime(&curTime));
		    fprintf(fp, "high\n");
		    actualMain[i] = controlMain[i];
		    startTime[i] = curTime;
		    fclose(fp);
		    }
		}
	    }
NEXT:   i=i;
	}
}

/* Any timer or freeze changes ? */
doPending(time_t curTime, const struct tm *tm)
{
int curMin;
int updated=0, onOffBit, bit, i, j, freezeMode, timer;


    curMin = tm->tm_min + tm->tm_hour * 60;

    if( state & STATE_TIME_RDY )
	{   /* Check programmable timers */
	for(i=0; i<MAX_CONTROL; i++)
	    {
	    if( !controlNames[i] )
		continue;
	    if( controlMain[i] & TMO )
		{       /* A one shot fixed time on but can span midnight */
		if( curTime > TMO_offTime[i] )
		    {
		    if( debug )
			printf("TMO off %s at %s\n", controlNames[i], ctime(&curTime));
		    updated = 1;
		    controlMain[i] = controlMain[i] & ~TMO;
		    }
		}
	    for(timer=0; timer<4; timer++)
		{
		bit = (1<<timer);
		if( !(pgmDays[i][timer] & (1<<tm->tm_wday)) )
		    onOffBit = 0;
		else if( pgmOnTimes[i][timer] > 1440 )       /* Out of bounds, unused */
		    onOffBit = 0;
		else if( pgmOnTimes[i][timer] <= curMin && pgmOffDayTimes[i][timer] >= curMin )
		    onOffBit = bit;
		else
		    onOffBit = 0;
		if( (controlMain[i] & bit) != onOffBit )
		    {
		    if( debug )
			printf("Toggle %s to onOffBit %d b/c of timer %d at %s\n", controlNames[i], onOffBit, timer, ctime(&curTime));
		    updated = 1;
		    controlMain[i] = (controlMain[i] & ~bit) | onOffBit;
		    }
		}
	    }
	}
    /* Any freeze protects ? */
    /* Note either sensor can turn on freeze protection, but both must be above temp to turn it off */
    if( state & STATE_TEMPS_RDY )
	{
	freezeMode = -1; /* Freeze protect is off */
	for(i=0; i<MAX_SENSORS; i++)
	    {
	    if( freezeSense[i] == 0 )
		continue;
	    if( avgTemps[i] <= freezeOffTemp )
		freezeMode = 0;         /* Don't change state, if on by freeze, leave on, if off, leave off */
	    if( avgTemps[i] <= freezeOnTemp )
		{
		freezeMode = 1;         /* Force on if off */
		break;
		}
	    }
	if( freezeMode )
	    {
	    for(i=0; i<MAX_CONTROL; i++)
		{
		if( !controlNames[i] )
		    continue;
		if( freezeProtect[i] == 0 )
		    {
		    if( controlMain[i] & FRZ )  /* Weird, must have reset freeze protect during freeze */
			{
			controlMain[i] = controlMain[i] & ~FRZ;
			updated = 1;
			}
		    continue;
		    }
		if( freezeMode == 1 )
		    {   /* Force on if needed */
		    if( !(controlMain[i] & FRZ) )
			{
			if( debug )
			    printf("Set FRZ %s\n", controlNames[i]);
			updated = 1;
			controlMain[i] |= FRZ;
			}
		    }
		if( freezeMode == -1 )
		    {
		    if( (controlMain[i] & FRZ) )
			{
			if( debug )
			    printf("UNSet FRZ %s\n", controlNames[i]);
			updated = 1;
			controlMain[i] = controlMain[i] & ~FRZ;
			}
		    }
		}
	    }
	}
    /* Finally set REQ if needed */
    for(i=0; i<MAX_CONTROL; i++)
	{
	if( !controlNames[i] )
	    continue;
	j = requires[i];
	if( j != i )
	    {
	    if( controlMain[i] )
		controlMain[j] |= REQ;
	    else if( !controlMain[i] )
		controlMain[j] = controlMain[j] & ~REQ;
	    }
	}

}

/* This is called once at the start of new days and at first initialization */
markNewDay()
{
int i, j;

    if( debug )
	printf("Mark new day\n");
    for(i=0; i<MAX_CONTROL; i++)
	for(j=0; j<4; j++)
	    pgmOffDayTimes[i][j] = pgmOffTimes[i][j];

}

static void usrled(int which, int state)
{
char fName[1024];
FILE *fp;

    sprintf(fName, "/sys/class/leds/beaglebone:green:usr%d/brightness", which+1);
    if( (fp=fopen(fName, "w")) == NULL)
	printf("Can't open %s\n", fName);
    else
	{
	fprintf(fp, "%d\n", state);
	fclose(fp);
	}
}

updateLEDS(int i)
{

    if( !(state & STATE_TIME_RDY) )
	{
	if( i & 0x1 )
	    {
	    usrled(0, 1);
	    usrled(1, 0);
	    }
	else
	    {
	    usrled(0, 0);
	    usrled(1, 1);
	    }
	return;
	}
    if( !(state & STATE_TEMPS_RDY) )
	{
	if( i & 0x1 )
	    {
	    usrled(0, 0);
	    usrled(1, 0);
	    }
	else
	    {
	    usrled(0, 1);
	    usrled(1, 1);
	    }
	return;
	}

    if( actualMain[0] )
	usrled(0,1);
    else
	usrled(0,0);
    if( actualMain[1] )
	usrled(1,1);
    else
	usrled(1,0);

}
looper(void)
{
const struct tm *tm;
struct tm tmx;
struct sockaddr from;
int i, j, rc, rcx, len, updated, newDaySet;
char cmdBuffer[1024];
time_t curTime, initTime, lastTime;
int loopCntr=0;
FILE *fp;


    time(&initTime);
    if( debug )
    	printf("INITTIME %u %s\n", initTime, ctime(&initTime));
    lastTime = initTime;

    newDaySet = 1;
    markNewDay();

again:
    rc = poll(pfds, numPfds, 1000);     /* 1 second between checks */
    time(&curTime);
    tm = localtime(&curTime);

    if( !(state & STATE_TIME_RDY ) )
	{   /* Could end up being up to 2 minutes if time gets inited after exactly 1 minutes */
	if( curTime - lastTime > 60 )
	    {
	    if( debug )
		printf("Probably time adjust; ignored and reset initTime to %s", ctime(&curTime));
	    initTime = curTime;
	    }
	lastTime = curTime;

	if( curTime - initTime > 60 )
	    {
	    if( debug )
		{
		printf("TIME RDY %s\n", ctime(&curTime));
		fflush(stdout);
		}
	    state |= STATE_TIME_RDY;
	    }
	}

    if( state & STATE_TIME_RDY )    /* OK to check for new day */
	{
	if( tm->tm_hour == 0 && tm->tm_min == 0)
	    {
	    if( newDaySet == 0 )
		{
		newDaySet = 1;
		markNewDay();
		}
	    }
	else
	    newDaySet = 0;
	}
    if( loopCntr == 10 )
	loopCntr=0;
    updateLEDS(loopCntr);
    loopCntr++;
    if( rc <= 0 )    /* Nothing polled, tmeout or error */
	{
	updateTemps(curTime);
	doPending(curTime, tm);
	updateRelays(curTime);
	goto again;
	}

    if( pfds[0].revents & POLLIN )  /* Somebody wants to connect */
	{
	len = sizeof(struct sockaddr);
	rcx = accept(pfds[0].fd, &from, &len);
	if( rcx >= 0 )
	    {   /* Add it in to the group we watch */
	    for(i=1; i<numPfds; i++)
		{
		if( pfds[i].fd == -1 )
		    break;
		}
	    if( i < MAX_CONNECTS )
		{
		pfds[i].fd = rcx;
		pfds[i].events = POLLIN | POLLHUP;
		pfds[i].revents =0;
		if( i == numPfds )
		    numPfds++;
		sendAll(i, curTime);
		}
	    else
		close(rcx);     /* At limit, close immediately */
	    }
	goto again;
	}

    /* Run thru all the descriptors to see who wants something */
    for(i=1; i<numPfds; i++)
	{
	if( pfds[i].revents & (POLLHUP|POLLERR|POLLNVAL) )
	    {   /* Connection closed */
	    close(pfds[i].fd);
	    pfds[i].fd = -1;
	    continue;
	    }
	if( pfds[i].revents & POLLIN )
	    {
	    if( debug)
		printf("Data ready on %d\n", i);
	    for(j=0; j<1024; j++)
		{
		if( read(pfds[i].fd, &cmdBuffer[j], 1) != 1 )
		    {
		    if( debug )
			printf("Read error on %d closing\n", i);
		    goto BAD;
		    }
		if( cmdBuffer[j] == '\n')
		    {
		    cmdBuffer[j] = '\0';
		    if( debug)
			printf("CHAN %d CMD %s\n", i, cmdBuffer);
		    if( cmdBuffer[0] == 'S' )
			processCmd(cmdBuffer, curTime, i);
		    else
			processStatus(cmdBuffer, curTime, i);
		    /* Process request */
		    break;
		    }
		}
	    if( j == 1024 )
		{
printf("CMD TOO LONG CHAN %d closing\n", i);
BAD:
		close(pfds[i].fd);
		pfds[i].fd = -1;
		}
	    }
	}
    goto again;
    /* NOTREACHED */
}

readConfig(char *fileName)
{
FILE *fp;
char line[1024];

    if( (fp=fopen(fileName, "r")) == NULL)
	{
	printf("Failed to open defaults file %s; exiting\n");
	return(1);
	}

    while(fgets(line, 1024, fp))
	{
	line[1023] = '\0';
	if( line[0] == '#' || line[0] == '\n' || line[0] == '\0' )
	    continue;
	processCmd(line, 0, -1);
	}
    fclose(fp);
    return(0);
}

writeConfig(char *fileName)
{
FILE *fp;
int i, j, k, l;
char onString[12], offString[12], dayString[12];

    if( (fp=fopen(fileName, "w")) == NULL)
	{
	printf("Failed to open defaults file %s; exiting\n", fileName);
	return(1);
	}

    /* Save the freeze points */
    fprintf(fp, "SfreezeOn %d\nSfreezeOff %d\n", freezeOnTemp, freezeOffTemp);

    /* Save each pump/light element */
    for(i=0; i<MAX_CONTROL; i++)
	{
	if( controlNames[i] == NULL )
	    continue;
	fprintf(fp, "SConfig %d \"%s\" %c %d\n", i, controlNames[i], YN[freezeProtect[i]], requires[i]);
	/* And the timers */
	for(j=0; j<4; j++)
	    {
	    if( pgmOnTimes[i][j] >= 1440 || pgmOffTimes[i][j] >= 1440 || pgmDays[i][j] == 0 )
		continue;
	    makeMinuteString(pgmOnTimes[i][j], onString);
	    makeMinuteString(pgmOffTimes[i][j], offString);
	    if( pgmDays[i][j] == 0xff )
		strcpy(dayString, "A");
	    else
		{
		for(l=k=0; l<7; l++)
		    {
		    if( pgmDays[i][j] & (1<<l) )
			dayString[k++] = l+'0';
		    }
		if( k == 0 )
		    dayString[k++] = '-';
		dayString[k] = '\0';
		}
	    fprintf(fp, "Stimers %d %d %s %s %s\n", i, j, dayString, onString, offString);
	    }
	}
/* Sensor data */
    fprintf(fp, "#                Type R25  RRef Toffset\n");
    for(i=0; i<MAX_SENSORS; i++)
	{
	if( sensorNames[i] == NULL )
	    continue;
	fprintf(fp, "Ssensor %d %s %c %s %0.1f %0.1f %0.1f\n", i, sensorNames[i], YN[freezeSense[i]], RTypeNames[sensorTypes[i]],
					R25s[i], RRefs[i], ROffsets[i]);
	}
    fclose(fp);
    return(0);
}

dumpSettings()
{
int i;

    printf("FREEZE: %d %d\n", freezeOnTemp, freezeOffTemp);
    for(i=0; i<MAX_CONTROL; i++)
	{
	if( controlNames[i] )
	    printf("%d : %s F %d R %d on %d off %d on %d off %d\n",
		i, controlNames[i], freezeProtect[i], requires[i], pgmOnTimes[i][0], pgmOffTimes[i][0], pgmOnTimes[i][1], pgmOffTimes[i][1]);
	}
    for(i=0; i<MAX_SENSORS; i++)
	{
	if( sensorNames[i] )
	    printf("%d : %s F %d RType %s calibR25 %f RRef %f TOffset %f\n", i, sensorNames[i], freezeSense[i], RTypeNames[sensorTypes[i]], R25s[i], RRefs[i], ROffsets[i]);
	}
}

main(int argc, char **argv)
{
int i, j;

    if( argc > 1 && strcmp(argv[1], "-d") == 0 )
	debug = 1;
    for(i=0; i<MAX_SENSORS; i++)
	{
	instTemps[i] = avgTemps[i] = 75;
	sensorVIN[i] = i;
	}

    for(i=0; i<MAX_CONTROL; i++)
	{
	for(j=0; j<4; j++)
	    {
	    pgmOnTimes[i][j] = pgmOffTimes[i][j] = 2000;
	    pgmDays[i][j] = 0xff;
	    }
	}
    if( readConfig("/root/poolDefaults") )
	if( readConfig("poolDefaults") )    /* Fall back to curent dir, needed for testing */
	    exit(44);
    for(i=0; i<MAX_CONTROL; i++)
	{
	requireTime[i] = 10;
	}
    controlGPIO[0] = 60; /* Pin 12 */
    controlGPIO[1] = 50; /* Pin 14 */
    controlGPIO[2] = 48; /* Pin 15 */
    controlGPIO[3] = 51; /* Pin 16 */
    controlGPIO[4] = 49; /* Pin 23 */
    controlGPIO[5] = 115;/* Pin 27 */
    if( debug )
	dumpSettings();
    if( setupListenSocket(7333) == 0 )
	exit(44);

    looper();
}
