#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>

#include <iostream>
#include <sstream>
using namespace std;

#include <SDL/SDL.h>

bool Quit=false, Calibrate=false;

void handleUserInput()
{
	SDL_Event UserEvent;
	
	while(SDL_PollEvent(&UserEvent))
		{
			if(UserEvent.type==SDL_QUIT)
				{
					Quit=true;
					return;
				}
			
			if(UserEvent.type==SDL_KEYDOWN && UserEvent.key.keysym.sym==SDLK_q)
				{
					Quit=true;
					return;
				}
			
			if(UserEvent.type==SDL_KEYDOWN && UserEvent.key.keysym.sym==SDLK_c)
				{
					Calibrate=true;
					continue;
				}
		}
}

void printHelp(string Cmd)
{
	cout<<"Usage: "<<Cmd<<" /dev/ttyUSB0"<<endl;
	cout<<"where \'ttyUSB0\' should be replaced by the TTY device you are using"<<endl;
}

int main(int argc,char** argv)
{
	string SerialDevice;
	
	if(argc!=2)
		{
			printHelp(argv[0]);
			return 0;
		}
	
	//the first argument is our serial device
	SerialDevice=argv[1];
	
	if(SerialDevice=="-h" || SerialDevice=="--help")
		{
			printHelp(argv[0]);
			return 0;
		}
	
	//our measured color and color for calibration
	int ColorR=0, ColorG=0, ColorB=0;
	int CalibR=1, CalibG=1, CalibB=1; //not zero to avoid devision by 0
	
	//for SDL
	SDL_Surface *SDL_Screen;
	
	//for handling input data
	string ColorString;
	char CharFromTTY;
	
	//for TTY
	struct termios TTYio, OldTTYio; //settings
	int TTYfd; //file descriptor
	
	//initializing our color display
	if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
		{
			cerr<<"FATAL: "<<SDL_GetError()<<endl;
			return -1;
		}
		
	if( !(SDL_Screen=SDL_SetVideoMode(200, 200, 24, SDL_HWSURFACE | SDL_DOUBLEBUF )) )
		{
			cerr<<"FATAL: "<<SDL_GetError()<<endl;
			return -2;
		}
	
	//open the tty
	TTYfd=open(SerialDevice.c_str(), O_RDONLY | O_NONBLOCK);
	if (TTYfd <0)
		{
			cerr<<"FATAL: Error opening "<<SerialDevice<<" for reading..."<<endl;
			return -3;
		}
	
	//save current settings
	tcgetattr(TTYfd, &OldTTYio);
	
	//set baud rate to 9600
	cfsetispeed(&TTYio, B9600);
	
	//these are the setting we want when reading from TTY
	//memset(&TTYio,0,sizeof(TTYio));
	TTYio.c_iflag=0;
	TTYio.c_oflag=0;
	TTYio.c_cflag=CS8|CREAD|CLOCAL;
	TTYio.c_lflag=0;
	TTYio.c_cc[VMIN]=1;
	TTYio.c_cc[VTIME]=5;
	
	//apply new settings
	tcsetattr(TTYfd, TCSANOW, &TTYio);
	
	//main loop
	while (!Quit)
		{
			//handle keyboard input
			handleUserInput();
			
			//read from tty
			if (read(TTYfd, &CharFromTTY, 1)>0)
				{
					if(CharFromTTY=='\n') //all color info has now been transfered to TTY from arduino, and it's terminated with a newline
						{
							cout<<"Received color defined by: \'"<<ColorString;
							
							//simple decoding using C++ streams => NB! no error checking performed what-so-ever!
							stringstream SS(ColorString);
							SS>>ColorR>>ColorG>>ColorB;
							
							//reset our storage string for receiving data
							ColorString="";
							
							cout<<"\' which was decoded as ("<<ColorR<<","<<ColorG<<","<<ColorB<<")"<<endl;
							
							if(Calibrate)
								{
									cout<<"This is a calibration measurement."<<endl;
									CalibR=ColorR;
									CalibG=ColorG;
									CalibB=ColorB;
									
									//to avoid devision by 0
									if(CalibR==0)
										CalibR=1;
									
									if(CalibG==0)
										CalibG=1;
									
									if(CalibB==0)
										CalibB=1;
									
									Calibrate=false;
								}
							else
								{
									ColorR=255.0f*(1024.0f-(float)ColorR)/(1024.0f-(float)CalibR);
									ColorG=255.0f*(1024.0f-(float)ColorG)/(1024.0f-(float)CalibG);
									ColorB=255.0f*(1024.0f-(float)ColorB)/(1024.0f-(float)CalibB);
									
									//cap at 255
									if(ColorR>255)
										ColorR=255;
									
									if(ColorG>255)
										ColorG=255;
									
									if(ColorB>255)
										ColorB=255;
								}
							
							cout<<"Drawing color: ("<<ColorR<<","<<ColorG<<","<<ColorB<<") calibrated to ("<<CalibR<<","<<CalibG<<","<<CalibB<<")\n###"<<endl;
						}
					else
						ColorString+=CharFromTTY;
				}
			
			//draw the color onscreen and update
			SDL_FillRect( SDL_Screen, &SDL_Screen->clip_rect, SDL_MapRGB( SDL_Screen->format, ColorR, ColorG, ColorB ) );
			SDL_UpdateRect(SDL_Screen, 0, 0, 0, 0);
		}
	
	//reset tty to old settings
	tcsetattr(TTYfd, TCSANOW, &OldTTYio);
	
	//deinitialize SDL
	SDL_Quit();
	
	return 0;
}
