#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <termios.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <asm/types.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <unistd.h>
#include <curses.h>
#include <string.h>

const CAPTION_LASTSCAN = "Last Scan:";
const CAPTION_STATUS = "Status:";
const CAPTION_DEVFOUND = "Devices found:";

/*

VIKING Bluetooth Scanner v1.0a
Copyright (c) 2004 dataholic <dataholic@spamzed.com>

you know how its done!

compile with:

gcc viking.c -o viking -lbluetooth -lcurses

:: NOTES ::

this is version 1.0  , soon there will be a version with more features

:: SPEECH ::

if you want the speech to work, install festival from:

http://www.cstr.ed.ac.uk/projects/festival/download.html

Debian users use apt-get install festival

this sourcecode is released under the GPL license
http://www.gnu.org/copyleft/gpl.html#SEC3
*/

int devs_found =0;
FILE *logfile=0;

enum {SCREEN_ITEM_STATUS = 1, SCREEN_ITEM_LASTSCAN = 2, SCREEN_ITEM_FOUNDNEW=3, SCREEN_ITEM_DEVFOUND};

inquiry_info devs_cache[1024]; 
int runbtscan();
int speak(const char *what);
void sighandler(int sig);
void ontimer(int sig);
void gettimestamp(char *out);
void update_status_item(int itemid, const char *caption);
void logtxt(const char *what);
void tellstatus();

void tellstatus()
{
char temp[200];
gettimestamp((char*)&temp);	
sprintf((char*)&temp,"status report, %d devices found",devs_found);
speak((const char*)&temp);
sleep(5);
}

void add_dev_to_cache(inquiry_info *pinfo)
{
//devs_found++ required before calling this function

devs_cache[devs_found] = *pinfo;

}

int dev_in_cache(inquiry_info *pinfo)
{
	
int x=0;
int retval=0;
char cache_addr[18];
char his_addr[18];
	
	
ba2str(&(pinfo->bdaddr), his_addr);
	
	for (x=0;x<(devs_found+1);x++)
	{
	ba2str(&(devs_cache[x].bdaddr), cache_addr);
		
		if (strcmp((const char*)&cache_addr,(const char*)&his_addr) == 0)
		{
			
			retval =1;
			break;
		}

	}		
return retval;	
}

void logtxt(const char *what)
{
fwrite(what,1,strlen(what),logfile);	
fwrite("\r\n",1,2,logfile);
}

void gettimestamp(char *out)
{
time_t result=0;  
static struct tm mytime;
int x=0;
result=time(NULL);

memcpy((void *)&mytime,(void *)gmtime(&result), sizeof(struct tm));

sprintf(out,"%s",asctime(&mytime));
//filter out breaks
	for (x=0;x<strlen((const char*)out);x++)
	{
		if (out[x] == '\r' || out[x] == '\n' )
		{
			out[x] =0;
		}
	}
}


int main() 
{

int timesfailed=0;
char buffer[1024];
char temp[100];
int timeslooped=0;
//set up signal handlers
signal(SIGINT,sighandler);
signal(SIGALRM,ontimer);

//set up status time
//alarm(3);	//doesnt work yet, interrupts bluez api's :(
	
//open logfile
logfile = fopen("./logfile.txt","a+");

gettimestamp((char*)&temp);
sprintf((char*)&buffer,"VIKING started up @ %s",temp);
logtxt((const char*)&buffer);

//init curses
initscr();
	
//init screen
clear();
move(1,1);
printw("%s","VIKING Bluetooth Scanner (c) 2004, written by dataholic ");
refresh();

//print status indicators
move(3,1);
printw("*** SCAN STATUS ***");
move(5,1);
printw("%s ",CAPTION_STATUS);
move(6,1);
printw("%s ",CAPTION_LASTSCAN);
move(7,1);
printw("%s ",CAPTION_DEVFOUND);
move(10,1);
printw("*** FOUND DEVICES ***");
refresh();


	
while (1)
{	

gettimestamp((char*)&buffer);
update_status_item(SCREEN_ITEM_LASTSCAN,buffer);
update_status_item(SCREEN_ITEM_STATUS,"Scanning for devices...");
sprintf((char*)&buffer,"%d",devs_found);
update_status_item(SCREEN_ITEM_DEVFOUND,buffer);

//move cursor for fancyness
move(20,1);
refresh();
	if ( runbtscan() != 0 ) 
	{
		timesfailed++;
		
		
		if (timesfailed > 10 ) 
		{
			sprintf((char*)&buffer,"Scan failed %d times, aborting!",timesfailed);
			update_status_item(SCREEN_ITEM_STATUS,(const char*)&buffer);
			speak(&buffer);
			break;
		}
		else
		{
			sprintf((char*)&buffer,"Scan failed, retrying...");
			update_status_item(SCREEN_ITEM_STATUS,(const char*)&buffer);
			sprintf((char*)&buffer,"warning, scan failed, retrying");
			speak(&buffer);
			sleep(10);
			
		}
	}
	else
	{
//		printf("OK\r\n");
	}
	
timeslooped++;
		if (timeslooped > 6 )
		{
			update_status_item(SCREEN_ITEM_STATUS,"Reporting status to user");
			tellstatus();
			timeslooped =0;
		}
}

logtxt("VIKING quited");
exit(0);
}

int speak(const char *what)
{
const char execstring[1024];
/*
	
	festival -b '(voice_%VOICENAME%)' '(tts "/tmp/speak.txt" nil)'

*/
	
sprintf((char*)&execstring,"echo \x22%s\x22 > /tmp/speak.txt",what);
system(execstring);	
//speak to me. miaww
sprintf((char*)&execstring,"festival -b '(voice_ked_diphone)' '(tts \x22/tmp/speak.txt\x22 nil)'");
system(execstring);

return 0;
}


int runbtscan()
{
	inquiry_info *info = NULL;
	int num_rsp, length, flags;
	char addr[18];
	char name[248];
	char buffer[1024];
	char bufferdatetime[250];
	int i, opt, dd;
	int dev_id =-1; //let hci_get_route find it
	

	length  = 8;  /* ~10 seconds */
	num_rsp = 100;
	flags = 0;

	if (dev_id < 0) {
		dev_id = hci_get_route(NULL);
		if (dev_id < 0) {
			perror("Device is not available");
			return -1;
		}
	}

	
num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
	if (num_rsp < 0) {
		perror("Inquiry failed");
		return -1;
	}
		

	dd = hci_open_dev(dev_id);
	if (dd < 0) {
		perror("HCI device open failed");
		free(info);
		exit(1);
	}

	for (i = 0; i < num_rsp; i++) {
		memset(name, 0, sizeof(name));
		if (hci_read_remote_name(dd, &(info+i)->bdaddr, sizeof(name), name, 100000) < 0)
			strcpy(name, "no name");
		ba2str(&(info+i)->bdaddr, addr);
		
		if (dev_in_cache((info+i)) == 0) 
		{
		
		 		 
		 sprintf(&buffer,"found bluetooth device, named .. %s",name);
		 speak((const char*)&buffer);
		 //add to GUI list
		 devs_found++;
		 gettimestamp((char*)&bufferdatetime);
		 sprintf(&buffer,"%s\t\t%s, last seen: %s",addr,name,bufferdatetime);
		 update_status_item(SCREEN_ITEM_FOUNDNEW,(const char*)&buffer);
		 logtxt((const char*)&buffer);
		 //add to cache
		 add_dev_to_cache((info+i));
		}
		else
		{
		sprintf((char*)&buffer,"%s already in list...",addr);
		update_status_item(SCREEN_ITEM_STATUS,(const char*)&buffer);
		}			
		
	}

	close(dd);
	free(info);
	return 0;
}


void update_status_item(int itemid, const char *caption)
{
	
//first move cursor based on itemid
	switch (itemid)
	{
		
		case SCREEN_ITEM_STATUS:
		move(5,17);		
		break;
			
		case SCREEN_ITEM_LASTSCAN:
		move(6,17);		
		break;
		
		case SCREEN_ITEM_DEVFOUND:
		move(7,17);
		break;
		
		case SCREEN_ITEM_FOUNDNEW:
		move(11 + devs_found,1);
//SINGLE LINE:		move(11,1);
		break;
	
	}
clrtoeol();
printw("%s",caption);	
refresh();	
}

void sighandler(int sig)
{
endwin();
fclose(logfile);
printf("\r\n\r\n\x22...and that bitch is slippin!...\x22 -- Ice Cube \r\n;)\r\n\r\n");
//speak("we dont need that smiling mother fucker! going to get some beer, catch ya later on the flipside, mate");
speak("bye bye, mate");

exit(0);
}

void ontimer(int sig)
{
char temp[100];
gettimestamp((char*)&temp);
move(20,1);
printw("timer called @ %s",temp);	
}

