#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <ftdi.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BAUDRATE	4800
#define MINTEMP		-55
#define MAXTEMP		150
#define SCREEN_W	640
#define SCREEN_H	480
#define GRAPH_W		400
#define GRAPH_H		MAXTEMP+(-MINTEMP)		/* -55ºC to 150ºC */
#define TITLE_Y		10
#define GRAPH_Y		100
#define TEMP_Y		GRAPH_Y + GRAPH_H + 30
#define TITLE		"MSP430 + DS1624 + SDL Termometer"
#define CREDITS		"David Pello, FabLAB Asturias, FabAcademy 2012"
#define CREDITS_Y	20
#define DELAY		200000000;

/* SDL Variables */
SDL_Surface* screen = NULL;
SDL_Event event;
TTF_Font* font;

SDL_Color black={0,0,0};
SDL_Color white={255,255,255};
SDL_Color red={255,0,0};

/* FTDI variables */
struct ftdi_context ftdic;
int portopen = 0;

/* global variables */
float temperature = 0.0;		/* for reading from MSP430 */
float temp = 0.0;			/* debug */
int temperror;
int graph_xpos = 1;			/* Graph positions */
int graph_ypos = 0;
int gr_xp = 1;				/* previous x and y positions */
int gr_yp = 0;
struct timespec delay;			/* for nanosleep */
Uint32 rmask, gmask, bmask, amask;	/* standard masks for surfaces */


/*
 * Inits all subsystems 
 *
 */

void init()
{
	int ftdierr;

	/* init SDL video and audio */
	printf("Initializing SDL.\n");
	if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) { 
		printf("Could not initialize SDL: %s.\n", SDL_GetError());
		exit(-1);
	}
	printf("SDL initialized.\n");

	/* init the display */
	screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
	if ( screen == NULL ) {
		fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n", SDL_GetError());
		exit(1);
	}
	printf("SDL Screen initialized.\n");

	/* Init SDL_ttf */
	if(TTF_Init()==-1) {
		printf("TTF_Init: %s\n", TTF_GetError());
		exit(2);
	}

	font=TTF_OpenFont("font.ttf", 16);
	if(!font) {
		printf("TTF_OpenFont: %s\n", TTF_GetError());
		exit(2);
	}

	/* Init ftdi */
	ftdi_init (&ftdic);
	if ((ftdierr = ftdi_usb_open(&ftdic, 0x0403, 0x6001)) < 0)
	{
		fprintf(stderr, "unable to open ftdi device: %d (%s)\n", ftdierr, ftdi_get_error_string(&ftdic));
		portopen = 0;
	}
	else
		portopen = 1;

	/* low latency */
	ftdi_set_latency_timer(&ftdic, 1);

	/* baudrate */
	ftdi_set_baudrate(&ftdic, BAUDRATE);

	/* Init Variables */
	delay.tv_nsec = DELAY;	/* 100 ms */
	delay.tv_sec = 0;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
	rmask = 0xff000000;
	gmask = 0x00ff0000;
	bmask = 0x0000ff00;
	amask = 0x000000ff;
#else
	rmask = 0x000000ff;
	gmask = 0x0000ff00;
	bmask = 0x00ff0000;
	amask = 0xff000000;
#endif

}


/*
 * Read temperature from  MSP430 using the FTDI 
 *
 */
int get_temperature(float* tmp)
{
	char t[10];
	int c = 0;


	if (portopen)
	{
		/* temperature comes in a format like T[-]xxx.xx\n */
		c = ftdi_read_data(&ftdic, t, 1);
		if (c==0)	 /* no data available */
			return 1;
		while (t[0] != 'T')
			ftdi_read_data(&ftdic, t, 1);
		/* ok, we have read 'T', read until \n or buffer fills*/
		for (c=0; c<=9; c++) 
		{
			ftdi_read_data(&ftdic, t+c, 1);
			if (t[c] == '\n')
				break;
			if (c==9) 
				return 1;	/* buffer filled, error */
		}

		/* now we have data in t, convert to float */
		*tmp = strtof(t, NULL);
		return 0;
	}
	else
	{
		/* don't modify temperature */
		return 1;
	}
}


/*
 * Generic function to draw text
 *
 */
SDL_Surface* render_text(char* t, SDL_Color c)
{
	SDL_Surface *text_surface;
	if(!(text_surface=TTF_RenderUTF8_Blended(font, t, c))) 
	{
		return NULL;
	} 
	else {
		return text_surface;
	}
}

/*
 * Renders all text and graphics into the screen 
 *
 */
void render(void)
{
	/* get temperature from serial port (if ready) */
	temperror = get_temperature(&temperature);

	/* Lock surface if needed */
	if (SDL_MUSTLOCK(screen)) 
		if (SDL_LockSurface(screen) < 0) 
			return;

	/* title */
	SDL_Surface* title = render_text(TITLE, white);
	if (title != NULL)
	{
		SDL_Rect dest = {screen->w/2-title->w/2, TITLE_Y, title->w, title->h};
		SDL_BlitSurface(title,NULL,screen,&dest);
		SDL_FreeSurface(title);
	}
	title = render_text(CREDITS, white);
	if (title != NULL)
	{
		SDL_Rect dest = {screen->w/2-title->w/2, CREDITS_Y, title->w, title->h};
		SDL_BlitSurface(title,NULL,screen,&dest);
		SDL_FreeSurface(title);
	}


	/* graph */
	SDL_Surface* graph;
	graph = SDL_CreateRGBSurface(SDL_SWSURFACE, GRAPH_W+2, GRAPH_H+2, 32, rmask, gmask, bmask, amask); 
	if (graph != NULL)
	{
		rectangleColor(graph, 0, 0, GRAPH_W+1, GRAPH_H+1, 0xffffffff);
		/* line */
		graph_xpos++;					/* advance */
		if (graph_xpos>GRAPH_W)				/* end? */
		{
			/* clear and restart */
			boxColor(graph, 1, 1, GRAPH_W, GRAPH_H, 0x000000ff);
			graph_xpos = 1;
			gr_xp = 1;
		}
		graph_ypos = (int) temperature + (-MINTEMP);
		lineColor(graph, gr_xp, GRAPH_H-gr_yp, graph_xpos, GRAPH_H-graph_ypos, 0xff0000ff);
		gr_xp = graph_xpos;
		gr_yp = graph_ypos;
		SDL_Rect dest = {screen->w/2-graph->w/2, GRAPH_Y-1, graph->w+2, graph->h+2};
		SDL_BlitSurface(title,NULL,screen,&dest);
		SDL_FreeSurface(graph);
	}
	/* scale */
	SDL_Surface* t;
	t = render_text("-55", white);
	if (t != NULL)
	{
		SDL_Rect dest = {screen->w/2-GRAPH_W/2-t->w-10, GRAPH_Y+GRAPH_H-t->h/2, t->w, t->h};
		SDL_BlitSurface(t,NULL,screen,&dest);
		SDL_FreeSurface(t);
	}

	t = render_text("0", white);
	if (t != NULL)
	{
		SDL_Rect dest = {screen->w/2-GRAPH_W/2-t->w-10, GRAPH_Y+GRAPH_H-55-t->h/2, t->w, t->h};
		SDL_BlitSurface(t,NULL,screen,&dest);
		SDL_FreeSurface(t);
	}
	t = render_text("150", white);
	if (t != NULL)
	{
		SDL_Rect dest = {screen->w/2-GRAPH_W/2-t->w-10, GRAPH_Y-t->h/2, t->w, t->h};
		SDL_BlitSurface(t,NULL,screen,&dest);
		SDL_FreeSurface(t);
	}

	/* temperature text */
	char* tempstring;
	if (!temperror)
	{
		tempstring = malloc(50*sizeof(char));
		sprintf(tempstring, "Temperature: %4.2f", temperature);
	}
	else
		tempstring = "Temperature: Invalid";
	t = render_text(tempstring, white);
	if (t != NULL)
	{
		SDL_Rect dest = {screen->w/2-(GRAPH_W+2)/2, TEMP_Y, t->w, t->h};
		boxColor(screen, screen->w/2-(GRAPH_W+2)/2, TEMP_Y,  (screen->w/2-(GRAPH_W+2)/2)+t->w+20, TEMP_Y+t->h, 0x000000ff);
		SDL_BlitSurface(t,NULL,screen,&dest);
		SDL_FreeSurface(t);
	}

	/* Unlock if needed */
	if (SDL_MUSTLOCK(screen)) 
		SDL_UnlockSurface(screen);

	/* Update screen */
	SDL_UpdateRect(screen, 0, 0, 640, 480);

	/* Small delay */
	nanosleep(&delay, NULL);
}

/* 
 * Cleans all data structures from libs 
 *
 */
void cleanup(void)
{
	printf("Quitting SDL.\n");
	/* Shutdown all subsystems */
	SDL_Quit();
	TTF_CloseFont(font);
	TTF_Quit();

	printf("Quitting ftdi.\n");
	/* Clean ftdi data */
	ftdi_usb_close (&ftdic);
	ftdi_deinit(&ftdic);
}

/* 
 * Main routine
 *
 */
int main(int argc, char** argv) 
{
	int quit = 0;

	/* clean up on exit */
	atexit (cleanup);

	/* Inits everything */
	init();

	/* Enable Unicode translation */
	SDL_EnableUNICODE( 1 );

	/* Loop until an SDL_QUIT event is found */
	while( !quit )
	{

		/* renders the scene */
		render();

		/* Poll for events */
		while( SDL_PollEvent( &event ) )
		{
			switch( event.type ){
				/* Keyboard event */
				/* Pass the event data onto PrintKeyInfo() */
				case SDL_KEYDOWN:
					switch( event.key.keysym.sym )
					{
						case SDLK_ESCAPE:
							quit=1;
							break;
						default:
							break;
					}
				case SDL_KEYUP:
					break;

					/* SDL_QUIT event (window close) */
				case SDL_QUIT:
					quit = 1;
					break;

				default:
					break;
			}

		}

	}


	printf("Quitting...\n");
	exit(0);
}

