/*******************************************************************
	Run news ticker of CNN or BBC headlines across the display
	 - 2002.02.07 - works
 ******************************************************************/

#define MAX_UDP_SOCKET_BUFFERS 2

// Network Settings
#define DHCP_CLASS_ID "Rabbit-TCPIP:Z-World:DHCP-Test:1.0.0"
#define DHCP_NUM_SMTP 1		// Get an SMTP server if possible
#define USE_DHCP			// Main define to cause BOOTP or DHCP to be used.
#define DHCP_MINRETRY 5		// Min DHCP retry interval (secs).  If not defined, defaults to 60.
#define MY_IP_ADDRESS	"10.10.6.100"
#define MY_NETMASK		"255.255.255.0"
#define MY_GATEWAY		"10.10.6.1"
#define HTTP_PORT 80

// Library Setup
#memmap xmem
#use dcrtcp.lib

/*******************************************************************
   MJF Constants
 ******************************************************************/
#define LOOP_DISPLAY	1
#define BRIGHTRES		10
#define B BRIGHTRES			//a useful shortcut
char display[5*BRIGHTRES];	//the LED display

/*******************************************************************
   MJF Characters
 ******************************************************************/
const char digits[10][5][5] = {
//zero
	{
		{0,B,B,B,0},
		{B,0,0,B,B},
		{B,0,B,0,B},
		{B,B,0,0,B},
		{0,B,B,B,0}
	},
//one
	{
		{0,0,B,0,0},
		{0,B,B,0,0},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{B,B,B,B,B}
	},
//two
	{
		{0,B,B,B,0},
		{B,0,0,B,0},
		{0,0,B,0,0},
		{0,B,0,0,0},
		{B,B,B,B,B}
	},
//three
	{
		{0,B,B,B,0},
		{B,0,0,0,B},
		{0,0,B,B,0},
		{B,0,0,0,B},
		{0,B,B,B,0}
	},
//four
	{
		{0,0,0,B,0},
		{0,0,B,B,0},
		{0,B,0,B,0},
		{B,B,B,B,B},
		{0,0,0,B,0}
	},
//five
	{
		{B,B,B,B,B},
		{B,0,0,0,0},
		{B,B,B,B,0},
		{0,0,0,0,B},
		{B,B,B,B,0}
	},
//six
	{
		{0,0,0,B,0},
		{0,0,B,0,0},
		{0,B,B,B,0},
		{B,0,0,0,B},
		{0,B,B,B,0}
	},
//seven
	{
		{B,B,B,B,B},
		{0,0,0,B,0},
		{0,0,B,0,0},
		{0,B,0,0,0},
		{B,0,0,0,0}
	},
//eight
	{
		{0,B,B,B,0},
		{B,0,0,0,B},
		{0,B,B,B,0},
		{B,0,0,0,B},
		{0,B,B,B,0}
	},
//nine
	{
		{0,B,B,B,0},
		{B,0,0,0,B},
		{0,B,B,B,B},
		{0,0,0,B,0},
		{0,B,B,0,0}
	}
};

const char letters[26][5][5] = {
//A
	{
		{0,0,B,0,0},
		{0,B,0,B,0},
		{B,B,B,B,B},
		{B,0,0,0,B},
		{B,0,0,0,B}
	},
//B
	{
		{B,B,B,0,0},
		{B,0,0,B,0},
		{B,B,B,B,0},
		{B,0,0,0,B},
		{B,B,B,B,0}
	},
//C
	{
		{0,B,B,B,B},
		{B,0,0,0,0},
		{B,0,0,0,0},
		{B,0,0,0,0},
		{0,B,B,B,B}
	},
//D
	{
		{B,B,B,B,0},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,B,B,B,0}
	},
//E
	{
		{B,B,B,B,B},
		{B,0,0,0,0},
		{B,B,B,B,0},
		{B,0,0,0,0},
		{B,B,B,B,B}
	},
//F
	{
		{B,B,B,B,B},
		{B,0,0,0,0},
		{B,B,B,B,0},
		{B,0,0,0,0},
		{B,0,0,0,0}
	},
//G
	{
		{0,B,B,B,B},
		{B,0,0,0,0},
		{B,0,0,B,B},
		{B,0,0,0,B},
		{0,B,B,B,0}
	},
//H
	{
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,B,B,B,B},
		{B,0,0,0,B},
		{B,0,0,0,B}
	},
//I
	{
		{B,B,B,B,B},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{B,B,B,B,B}
	},
//J
	{
		{B,B,B,B,B},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{B,0,B,0,0},
		{0,B,0,0,0}
	},
// K
	{
		{B,0,0,B,0},
		{B,0,B,0,0},
		{B,B,B,0,0},
		{B,0,0,B,0},
		{B,0,0,0,B}
	},
//L
	{
		{B,0,0,0,0},
		{B,0,0,0,0},
		{B,0,0,0,0},
		{B,0,0,0,0},
		{B,B,B,B,B}
	},
//M
	{
		{B,0,0,0,B},
		{B,B,0,B,B},
		{B,0,B,0,B},
		{B,0,0,0,B},
		{B,0,0,0,B}
	},
//N
	{
		{B,0,0,0,B},
		{B,B,0,0,B},
		{B,0,B,0,B},
		{B,0,0,B,B},
		{B,0,0,0,B}
	},
//O
	{
		{0,B,B,B,0},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{0,B,B,B,0}
	}, 
//P
	{
		{B,B,B,B,0},
		{B,0,0,0,B},
		{B,B,B,B,0},
		{B,0,0,0,0},
		{B,0,0,0,0}
	},
//Q
	{
		{0,B,B,B,0},
		{B,0,0,0,B},
		{B,0,B,0,B},
		{B,0,0,B,0},
		{0,B,B,0,B}
	},
//R
	{
		{B,B,B,B,0},
		{B,0,0,0,B},
		{B,B,B,B,0},
		{B,0,0,B,0},
		{B,0,0,0,B}
	},
//S
	{
		{0,B,B,B,B},
		{B,0,0,0,0},
		{0,B,B,B,0},
		{0,0,0,0,B},
		{B,B,B,B,0}
	},
//T
	{
		{B,B,B,B,B},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{0,0,B,0,0}
	},
//U
	{
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,0,0,0,B},
		{0,B,B,B,0}
	},
//V
	{
		{B,0,0,0,B},
		{B,0,0,0,B},
		{0,B,0,B,0},
		{0,B,0,B,0},
		{0,0,B,0,0}
	},
//W
	{
		{B,0,0,0,B},
		{B,0,0,0,B},
		{B,0,B,0,B},
		{B,B,0,B,B},
		{B,0,0,0,B}
	},
//X
	{
		{B,0,0,0,B},
		{0,B,0,B,0},
		{0,0,B,0,0},
		{0,B,0,B,0},
		{B,0,0,0,B}
	},
//Y
	{
		{B,0,0,0,B},
		{0,B,0,B,0},
		{0,0,B,0,0},
		{0,0,B,0,0},
		{0,0,B,0,0}
	},
//Z
	{
		{B,B,B,B,B},
		{0,0,0,B,0},
		{0,0,B,0,0},
		{0,B,0,0,0},
		{B,B,B,B,B}
	}
};

/*******************************************************************
   MJF Functions
 ******************************************************************/
//call this first to init the board
void initBoard(void) {
	int f;
	// Convert the I/O ports.  Disable slave port which makes
	// Port A an output, and PORT E not have SCS signal.
	WrPortI(SPCR, & SPCRShadow, 0x84);
	// Read function shadow and set PE0, 1, 4, 5, 7 as normal I/O.
	// LED colomns conencted to PE0,1,4,5,7, make them outputs.
	// Using shadow register preserves function of other Port E bits.
	WrPortI(PEFR,  & PEFRShadow,  ~((1<<7)|(1<<5)|(1<<4)|(1<<1)|(1<<0)) & PEFRShadow);
	WrPortI(PEDDR, & PEDDRShadow, ((1<<7)|(1<<5)|(1<<4)|(1<<1)|(1<<0)) );
	// Set the toggle state to be correct
	//togglestate2 = BitRdPortI(PBDR, 2);
	for (f=0; f< BRIGHTRES*5;f++) {
		display[f] = 0;
	}
}
// call this to set the pixel brightness
void spb(int row, int col, int height) {
	int i;
	if (height > BRIGHTRES) {
		printf("range error");
		height = BRIGHTRES;
	}
	if (height < 0) {
		printf("range error");
		height = 0;
	}

	//printf("row %d\n", row);
	for(i = 0; i < height; i++) {
		display[5*i + row] |= (1 << col);
	}
	for(i=height; i < BRIGHTRES; i++){
		display[5*i + row] &= (0xFF ^ (1 << col));
	}
}
//call this to display a char, valid chars only!
int loadChar(char c, int offset) {
	int x, y;
	int index;	
	if (isspace(c)) {
		for (y = 0; y < 5; y++) {
			for (x = 0; x < 6; x++) {
				if (x+offset >= 0 && x+offset < 8) {
					spb(y, x+offset, 0);	
				}
			}
		}
	} 
	else if (isdigit(c)) {
		index = atoi(&c);
		for (y = 0; y < 5; y++) {
			for (x = -1; x < 6; x++) {
				if (x+offset >= 0 && x+offset < 8) {
					if (x == -1 || x == 5)
						spb(y, x+offset, 0);	
					else
						spb(y, x+offset, digits[index][y][4-x]);
			
				}
			}
		}
	} else if (isalpha(c)) {
		if (islower(c))
			c = toupper(c);
		index = (int)c - 65;
		for (y = 0; y < 5; y++) {
			for (x = -1; x < 6; x++) {	
			if (x+offset >= 0 && x+offset < 8) {
			//	printf("%d\n",index);
				if (x == -1 || x == 5)
					spb(y, x+offset, 0);	
				else
					spb(y, x+offset, letters[index][y][4-x]);
				}
			}
		}
	} else {
		return 0;
	}
	return 1;
}
//scroll the string across the display... blocks until done
void scroll(char * str) {
	int i, len;
	int offset;
	char * ptr;
	ptr = str;
	offset = -3;
	while (ptr) {
		loadChar(*ptr,offset);
		offset++;
		if (offset > 8){
			offset = -3;
			ptr++;
		}
	}
}

/*******************************************************************
   Network Utility Functions
 ******************************************************************/
//print out useful network information
static void print_results(){
	auto long tz;
	printf("Network Parameters:\n");
	printf("  My IP Address = %08lX\n", my_ip_addr);
	printf("  Netmask = %08lX\n", sin_mask);
	if (_dhcphost != ~0UL) {
		if (_dhcpstate == DHCP_ST_PERMANENT) {
			printf("  Permanent lease\n");
		} else {
			printf("  Remaining lease = %ld (sec)\n", _dhcplife - SEC_TIMER);
			printf("  Renew lease in %ld (sec)\n", _dhcpt1 - SEC_TIMER);
		}
		printf("  DHCP server = %08lX\n", _dhcphost);
		printf("  Boot server = %08lX\n", _bootphost);
	}
	if (gethostname(NULL,0))
		printf("  Host name = %s\n", gethostname(NULL,0));
	if (getdomainname(NULL,0))
		printf("  Domain name = %s\n", getdomainname(NULL,0));
	if (dhcp_get_timezone(&tz))
		printf("  Timezone (fallback only) = %ldh\n", tz / 3600);
	else
		printf("  Timezone (DHCP server) = %ldh\n", tz / 3600);
	if (_last_nameserver)
		printf("  First DNS = %08lX\n", def_nameservers[0]);
	if (_smtpsrv)
		printf("  SMTP server = %08lX\n", _smtpsrv);
}
//grab a DCHP address
static int init_DHCP(){
	int status;
	_survivebootp = 1;// Set to 1 (default) or 0 to never use fallbacks
	_bootptimeout = 6;// Short timeout for testing
	printf("Starting network (max wait %d seconds)...\n", _bootptimeout);
	status = sock_init();
	switch (status) {
		case 1:
			printf("! Could not initialize packet driver.\n");
			return 0;
		case 2:
			printf("! Could not configure using DHCP.\n");
			break;	// continue with fallbacks
		case 3:
			printf("! Could not configure using DHCP; fallbacks disallowed.\n");
			return 0;
		default:
			break;
	}
	if (_dhcphost != ~0UL)
		printf("Lease obtained\n");
	else {
		printf("! Lease not obtained.  DHCP server may be down.\n");
		printf("! Using fallback parameters...\n");
		return 0;
	}
	print_results();
	return 1;
}

/*******************************************************************
   Misc Utility Functions
 ******************************************************************/
static void substr(char* dest,char* str,int begin, int end){
	int i;
	for (i=0;i<(end-begin);i++){
		dest[i] = str[i+begin];
	}
	dest[i] = 0;
}

/*******************************************************************
   RSS Fetching...
 ******************************************************************/
#define RSS_JACOBY_SERVER		"csociety.purdue.org"
#define RSS_CNN		"GET /~jacoby/XML/CNN_TOP_STORIES.xml HTTP/1.0 \r\n\r\n"
#define RSS_BBC		"GET /~jacoby/XML/bbc_world_news.rss HTTP/1.0 \r\n\r\n"

static char *get_headlines(char *rss_feed){
	longword rss_server;	//holds resolve(RSS_SERVER
	int bytes_read;			//for sock_fastread
	char buffer[2000];		//for sock_fastread
	tcp_Socket socket;		//for comm
	char rssCode[10000];	//10kb max downloaded file limit
	unsigned int pos;
	char temp[500];
	char* newStr;
	char otherStr[500];
	char headlines[2000];
	//connect to the server
	if (!(rss_server = resolve(RSS_JACOBY_SERVER))) {
        printf("Could not resolve host");
        exit( 3 );
    }
	if( !tcp_open(&socket,0,rss_server,HTTP_PORT,NULL)){
		printf("Unable to connect to remote server");
		exit( 3 );
	}
	//wait for the connection to be established
	while (!sock_established(&socket) && sock_bytesready(&socket)==-1){
       tcp_tick(NULL);
    }
	printf("connected!\n");
	//get the RSS code
	printf("sending request...\n");
	strcpy(rssCode,"");
	sock_puts(&socket,rss_feed);
	do{
		//this should be changes to parse as we receive each set of
		//bytes, so we don't need to waste 10k of memory with the big
		//rssCode array...
		bytes_read=sock_fastread(&socket,buffer,sizeof(buffer)-1);
		if(bytes_read>0){
			buffer[bytes_read]=0;
			strcat(rssCode,buffer);
			printf("got %d (%d), total now %d\n",bytes_read,strlen(buffer),strlen(rssCode));
		}
	} while(tcp_tick(&socket));
	printf("Connection closed...\n");
	printf(rssCode);
	//parse out the headlines
	headlines[0] = 0;
	newStr = strstr(rssCode,"<title>");
	newStr = strstr(newStr,">");
	while(newStr!= NULL){
		pos = strcspn(newStr,"<");
		temp[0] = 0;
		strncat(temp,newStr,pos);
		substr(otherStr,temp,1,strlen(temp));
		printf("\"%s\"\n",otherStr);
		strcat(headlines,otherStr);
		strcat(headlines,"...  ");
		newStr = strstr(newStr,"</title>");
		newStr = strstr(newStr,"<title>");
		newStr = strstr(newStr,">");
	}
	return headlines;
}

/*******************************************************************
   Program Code
 ******************************************************************/
main() {
	int i;			//for loops
	//MJF variables
	int offset;		//for scrolling text
	char * strPtr;	//the string that gets scrolled
	char * scrollingStrPtr;	//used if looping...
	char * last;	//scrolling helper

	// MJF initialization
	initBoard();
	offset = -3;
	spb(0,0, 8);	//show a diagonal line of varying brightness
	spb(1,1,6);
	spb(2,2,4);
	spb(3,3,2);
	spb(4,4,1);
	
	//network initialization
	if(init_DHCP()) strPtr = "connected  ";
	else strPtr = "no network ";

	//grab the headlines
	strPtr = get_headlines(RSS_BBC);
	scrollingStrPtr = strPtr;		//be friendly and leave strPtr untouched

	//control loop has two threads
	while(true){
		// this write procedure dumps bits to 4 leds at a time in order to minimize dimming
		costate {
			for (i = 0; i< 5*BRIGHTRES; i+=5) {		
			WrPortI(PEDR, & PEDRShadow, 0xFE); WrPortI(PADR, & PADRShadow, display[i+0] & 0xF0);yield;
			WrPortI(PEDR, & PEDRShadow, 0xFE); WrPortI(PADR, & PADRShadow, display[i+0] & 0x0F);yield;
			WrPortI(PADR, & PADRShadow, 0);WrPortI(PEDR, & PEDRShadow, 0xFD); WrPortI(PADR, & PADRShadow, display[i+1] & 0xF0);yield;
			WrPortI(PEDR, & PEDRShadow, 0xFD); WrPortI(PADR, & PADRShadow, display[i+1] & 0x0F);yield;
			WrPortI(PADR, & PADRShadow, 0);WrPortI(PEDR, & PEDRShadow, 0xEF); WrPortI(PADR, & PADRShadow, display[i+2] & 0xF0);yield;
			WrPortI(PEDR, & PEDRShadow, 0xEF); WrPortI(PADR, & PADRShadow, display[i+2] & 0x0F);yield;
			WrPortI(PADR, & PADRShadow, 0);WrPortI(PEDR, & PEDRShadow, 0xDF); WrPortI(PADR, & PADRShadow, display[i+3] & 0xF0);yield;
			WrPortI(PEDR, & PEDRShadow, 0xDF); WrPortI(PADR, & PADRShadow, display[i+3] & 0x0F);yield;
			WrPortI(PADR, & PADRShadow, 0);WrPortI(PEDR, & PEDRShadow, 0x7F); WrPortI(PADR, & PADRShadow, display[i+4] & 0xF0);yield;
			WrPortI(PEDR, & PEDRShadow, 0x7F); WrPortI(PADR, & PADRShadow, display[i+4] & 0x0F);yield;
			WrPortI(PADR, & PADRShadow, 0);
			}
		}
		//this cycles through the string displaying the chars and scrolling them across
		costate{
			while (scrollingStrPtr) {
				if (loadChar(*scrollingStrPtr,offset)) {
					if (last)
						loadChar(*last,offset + 6);
					offset++;
					if (offset > 1){
						offset = -4;
						last = scrollingStrPtr;
						scrollingStrPtr++;
					}
					waitfor(DelayMs(75));
				} else
					scrollingStrPtr++;
				if(*scrollingStrPtr==0)	//end of string
					if(LOOP_DISPLAY) scrollingStrPtr = strPtr;
					else scrollingStrPtr = NULL;
			}	
		}
 	}

}