/**********************************************************************
   FS_GEN.C
   Z-World, 2001
  
   This is a very minimal use of the DevMate
   filesystem, that uses only ANSI-C keywords.
   (ie: not the #ximport directive)
  
   The page is generated dynamically as raw HTML,
   and is a method of having dynamic content
   without the use of the var subsystem. In
   general, it is better to use the var
   subsystem instead of sending the entire
   page each time, but if the data changes
   very infrequently (hours?) this could be
   a useful technique.
**********************************************************************/

/*
 * Turn on debug messages, if this is uncommented
 */
//#define TC_FS_DEBUG

#define UPDATE_RATE	30		/* Update stored file every this many seconds */

/*
 * Select the filesystem support
 */
#define USE_TC_FS

/*
 * Uncomment this line if this program will be loaded over ethernet. This will
 * force the DevMate communication to use serial port A, re-using the programming
 * link that was used to download the program.
 */
//#define USE_TC_LOADER

#ifdef CC_VER
	/* Dynamic C */
	#memmap xmem
	#use "tc_conf.lib"
#else
	/* Non-Rabbit */
	#include "tc_conf.h"
	#include <stdio.h>	
#endif


/*
 * Global data that will be in the
 * dynamic web page.
 */
int counter1, counter2, counter3, counter4;

/*
 * A flag indicating a page updated is requested. Set this
 * flag to 1 to send an update.
 */
int update_flag;

/*
 * The real-text filename that is being updated
 */
const char file_name[] = "/dynamic.html";

/*
 * The HTML file header/footer...
 */
const char file_header[] = "<html><header><title>Test Dynamic Page!</title></header><body><h2>The current value of the counters:</h2><br><br>";
const char file_footer[] = "</body></html>";

/*
 * The states used in the FSM that uploads the file.
 */
#define UPDATE_SYNC			0
#define UPDATE_SYNCFINISH	1
#define UPDATE_LOOKUP		2
#define UPDATE_LOOKUPWAIT	3
#define UPDATE_CREATE		4
#define UPDATE_SEND			5
#define UPDATE_SENDHEADER	6
#define UPDATE_SENDVARS		7
#define UPDATE_SENDVAR		8
#define UPDATE_SENDVARTEXT	9
#define UPDATE_SENDFOOTER	10
#define UPDATE_CLOSE			11
#define UPDATE_ERROR			12
#define UPDATE_POLL			13
#define UPDATE_REPLACE		14

/*
 * Update the HTML file on the DevMate.
 *
 * This is a state machine that will start an
 * update whenever a specific flag is set.
 */
void update_file(void)
{
	static char not_first_time;	// false if this is the first time the file is sent (a CREATE)
	static DMFile	file;		// the file pointer used for this transaction
	static int state;			// current state...
	static int rv;
#define SENDBUFSIZE	128
	static char sendbuf[SENDBUFSIZE];
	static int varcache[4];
	static int currentvar;
	static int length, offset;
	static FNumber old_id;
	static long timeout;

#ifdef __DC__
#GLOBAL_INIT {
	state = UPDATE_SYNC;		// the initial state...
	update_flag = 0;			// no update requested yet!
	not_first_time = 0;			// do a CREATE!
}
#endif

	switch(state) {
	case UPDATE_SYNC:
		/* try and SYNC with the target */
		rv = devmate_fs_sync(&file,0);
		if(TC_PENDING == rv) {
			timeout = MS_TIMER;
			timeout += 2000;	// wait two seconds...
			
			state = UPDATE_SYNCFINISH;
			// fall through to SYNCFINISH
		} else {
			break;
		}

	case UPDATE_SYNCFINISH:
		/* is the SYNC command finished? */
		rv = devmate_fs_finish(&file);
		if(TC_PENDING == rv) {
			if(timeout < MS_TIMER) {
				// error - timeout! try this again...
				state = UPDATE_SYNC;
			}
			break; // still waiting...
		} else {
			if(TC_ERROR == rv) {
				printf("FS could not SYNC with the DevMate!\n");
				exit(-1);
			}
			// SYNC has finished - the DevMate is ready.
			// fall through to the LOOKUP state
			state = UPDATE_LOOKUP;
		}

	case UPDATE_LOOKUP:
		/* is the file we are trying to create allready there? */
		rv = devmate_fs_idlookup(&file, (char *)file_name, &old_id);
		if(rv != TC_PENDING) {
			/* error! */
			printf("idlookup error\n");
			exit(-1);
		} else {
			// wait for the idlookup to finish
			state = UPDATE_LOOKUPWAIT;
			// fall through to the LOOKUPWAIT state
		}

	case UPDATE_LOOKUPWAIT:
		rv = devmate_fs_finish(&file);
		if(TC_PENDING == rv) {
			// still waiting...
			break;
		} else if(TC_SUCCESS == rv) {
			/* the file allready exists! replace the file */
			state = UPDATE_REPLACE;
			break;
		} else {
			/* the file doesn't exist yet! create a new file... */
			state = UPDATE_CREATE;
			// fall through to the CREATE state!
		}

	case UPDATE_CREATE:
		/* try and create a new file */
		rv = devmate_fs_open(&file, 0, (char *)file_name, TC_FS_FLAGS_NEWID);
		if(TC_PENDING == rv) {
			break; // open isn't ready yet
		} else {
			if(TC_SUCCESS != rv) {
				state = UPDATE_ERROR;
				break;
			}
			// the file is opened - send it!
			state = UPDATE_SEND;
			// fall through to the SEND state
		}

	case UPDATE_SEND:
		/* send the header... */
		length = strlen(file_header);
		offset = 0;
		state = UPDATE_SENDHEADER;
		// fall through to the SENDHEADER state

	case UPDATE_SENDHEADER:
		if(offset < length) {
			// send a piece of the header
			rv = devmate_fs_append(&file, (char *)file_header+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done w/ sending the header!
			state = UPDATE_SENDVARS;
			// fall through to the SENDVARS state
		}

	case UPDATE_SENDVARS:
		/* first grap a cache of the variable, as this could take
			a while, and some variables might change in the meantime */
		varcache[0] = counter1;
		varcache[1] = counter2;
		varcache[2] = counter3;
		varcache[3] = counter4;
		currentvar = 0;	// start with the first variable
		state = UPDATE_SENDVAR;
		// fall through tot he SENDVAR state

	case UPDATE_SENDVAR:
		if(currentvar > 3) {
			state = UPDATE_SENDFOOTER;
			break;
		}
		/* generate the text-line to send for one variable */
		sprintf(sendbuf,"counter%c == 0x%04x<br>", currentvar + '1', varcache[currentvar]);
		offset = 0;
		length = strlen(sendbuf);
		state = UPDATE_SENDVARTEXT;
		// fall through to the SENDVARTEXT state

	case UPDATE_SENDVARTEXT:
		if(offset < length) {
			// send a piece of the text-line
			rv = devmate_fs_append(&file, sendbuf+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done w/ sending that text-line. is there more?
			currentvar += 1;
			state = UPDATE_SENDVAR;
			break;
		}

	case UPDATE_SENDFOOTER:
		if(offset < length) {
			// send a piece of the text-line
			rv = devmate_fs_append(&file, (char *)file_footer+offset, length-offset);
			if(-1 == rv) {
				// error!
				state = UPDATE_ERROR;
			}
			offset += rv;
			break;
		} else {
			// all done with the file! close it!
			state = UPDATE_CLOSE;
			// fall through to the CLOSE state
		}

	case UPDATE_CLOSE:
		/* spin on close to flush the remaining data */
		switch(devmate_fs_close(&file,NULL)) {
		case TC_PENDING:
			// still pending - do nothing
			break;

		case TC_SUCCESS:
			// upload was successfull!
			not_first_time = 1;		// no longer the first time
			update_flag = 0;		// all done w/ this update
			state = UPDATE_POLL;	// poll for the next update request
			break;

		default:
			// error!
			state = UPDATE_ERROR;
			break;
		}		
		break;

	case UPDATE_ERROR:
		/* error in update! try this again... */
		if(!not_first_time) {
			state = UPDATE_CREATE;
		} else {
			state = UPDATE_REPLACE;
		}
		break;

	case UPDATE_POLL:
		/* poll the update flag to see if the file needs to be reset */
		if(update_flag) {
			state = UPDATE_REPLACE;
			// fall through to the REPLACE state
		} else {
			break; // the flag is not set - don't update yet.
		}

	case UPDATE_REPLACE:
		rv = devmate_fs_open(&file, 0, (char *)file_name, TC_FS_FLAGS_REPLACE);
		if(TC_PENDING == rv) {
			// open is still pending
		} else {
			if(TC_SUCCESS != rv) {
				state = UPDATE_ERROR;
				break;
			}
			// open w/ replace was successfull!
			state = UPDATE_SEND;
		}
		break;

	default:
		/* internal error! */
		exit(-1);
	}
}

void worker(int *data)
{
	static foo;
	// Generate some not-very-random data.
	*data = (int)MS_TIMER + foo++;
}

main()
{
	static long timer;
	
	/*
	 * Standard initilization
	 */	
	devmate_init();

	/*
	 * Setup the timeout for when to update
	 * the page.
	 */
	timer = SEC_TIMER + UPDATE_RATE;	// update every this many seconds
	
	/*
	 * Run the state machine and the worker tasks that
	 * update our data.
	 */
	for(;;) {
		worker(&counter1);	// do some work
		worker(&counter2);
		worker(&counter3);
		worker(&counter4);

		update_file();			// drive the updating of pages

		devmate_tick();		// drive the communications with the DevMate

		if(SEC_TIMER > timer) {
			// timeout! request an update and reset the timer
			update_flag = 1;
			timer = SEC_TIMER + UPDATE_RATE;
		}
	}
}