/*
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * Author:
 *         Stefan Denk <denk@ira.uka.de>
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * $Id: clientthread.cc,v 1.5 2006/01/05 10:31:44 denk Exp $
 *
 */

#include <channels.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "clientthread.h"
#include "dvdrvidtrans.h"
#include "downloadstatus.h"
#include "helper.h"

#include <recording.h>

/**
 * The thread initialized but not started yet.
 * \param start: start time in seconds since the epoch rounded
 * to the minute.
 * \param stop: stop time in seconds since the epoch rounded to
 * the minute.
 * \param channel: channel to record. Since only valid vdr
 * cChannels can be provided, channels not locally available
 * cannot be fetched from the network.
 * \param source: a single video source (transport address, hash tupel)
 * \param threadnumber: which minute of the recording this thread covers
 * \param total: the total amount of minutes of the recording
 * \param name: the filename entered by the user in the timer menu (will be used to build $downloaddir/name/vdr-timestamp/xxx.vdr)
 */
clientthread::clientthread(time_t start, time_t stop, const cChannel * channel, cvideosource source, int threadnumber, int total, const char * name)
	: _start(start), _stop(stop), _channel(channel), _source(source), _threadnumber(threadnumber), _total(total), _name(name)
{
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	copydownloaddir();
}


/**
 * Copies the directory name where the download should be saved from the main thread.
 * The main assumption here is that we are survived by the main thread.
 * \todo Check whether the assumption is true.
 */
void
clientthread::copydownloaddir(){
	const vtmain * mainthread = vtmain::getthread();
	_dldir = mainthread->downloaddir;
}


int clientthread::tcp_connect(const char *hostname, const char *port)
{
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	int sockfd, n;
	struct addrinfo hints, *res, *ressave;

	bzero(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ( (n = getaddrinfo(hostname, port, &hints, &res)) != 0)
	{
		//dsyslog("%s: CLIENT %d: tcp_connect error for host %s, port %s: %s", __PRETTY_FUNCTION__, _threadnumber, hostname, port, gai_strerror(n));
	}
	
	
	if ( res == NULL )
	{
		esyslog("%s: CLIENT %d: tcp_connect error for %s, %s", __PRETTY_FUNCTION__, _threadnumber, hostname, port);
		return -1;
		///\bug error handling!!!!
	}

	ressave = res;

	do
	{
		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if ( sockfd < 0 )
			continue;	// ignore this one
		if ( connect(sockfd, res->ai_addr, res->ai_addrlen) == 0 )
			break;		// success
		close(sockfd);
	} while ( (res = res->ai_next) != NULL );

	freeaddrinfo(ressave);

	return sockfd;
}

void
clientthread::Action(){
	
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	printf("CLIENT %d: begin\n", _threadnumber);
	int sockfd;
	ssize_t bytesread = 0, byteswritten = 0;
	FILE * recvfd;
	unsigned int filecount;
	string savedir, answer, request;
	string infofile;	// info.vdr
	FILE *infofd;
	string infotext;
	string buf, serveraddress, serverport, serverprotocol;
	string channelname;
	time_t starttime;
	char currentfile[MAXPATHLENGTH];
	char buffer[HUGEBUFFSIZE];
	string dlstatusdir;
	downloadstatus _dlstatus;
	struct firsthashes data_for_request;
	struct firsthashes * dfr;
	int marker1, marker2;
	struct stat savedirstat;
	struct tm tm;
	char date[30];
		
	dfr = &data_for_request;
	
	// build the savedir string: downloaddir/channel/starttime
	if (_name)
	{
	       	channelname = _name;
		int pos;
		while (pos != -1)
		{
			pos = channelname.find(" ");
			channelname[pos] = '_';
		}
	}
	else channelname = getchannelname();
	starttime = getstarttime() - ( _threadnumber - 1 ) * 60;
	savedir += _dldir;
	if ( savedir.rfind('/') != savedir.size() ) savedir += '/';	// add trailing '/' if missing
	savedir += channelname;
	// if this is the first thread to run, create the savedir
	bzero(&savedirstat, sizeof(savedirstat));
	stat(savedir.c_str(), &savedirstat);
	if (!S_ISDIR(savedirstat.st_mode))
	{
		//dsyslog("%s: CLIENT %d: creating %s", __PRETTY_FUNCTION__, _threadnumber, savedir.c_str());
		mkdir(savedir.c_str(), 0755);
	}
	savedir += "/";
	localtime_r(&starttime, &tm);
	snprintf(date, 30, "%d-%02d-%02d.%02d:%02d.%s", tm.tm_year + 1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, "50.99.rec");
	savedir += date;
	bzero(&savedirstat, sizeof(savedirstat));
	stat(savedir.c_str(), &savedirstat);
	if (!S_ISDIR(savedirstat.st_mode))
	{
		//dsyslog("%s: CLIENT %d: creating %s", __PRETTY_FUNCTION__, _threadnumber, savedir.c_str());
		mkdir(savedir.c_str(), 0755);
		Recordings.AddByName(savedir.c_str());
	}
	// make sure that current file and viddata file can be constructed later on
	if (savedir.size() > MAXPATHLENGTH-11) savedir.erase(savedir.size()-11, savedir.size());

	if ( _dlstatus.switchdir(savedir) == 0 )
	{
		_dlstatus[_total];
		if (_dlstatus[_threadnumber].state == finished)
		{
			//dsyslog("%s: CLIENT %d: snippet %d already downloaded - skipping", __PRETTY_FUNCTION__, _threadnumber, _threadnumber);
			return;
		}
		_dlstatus[_threadnumber].state = dirty;
	}
	else
	{
		_dlstatus[_threadnumber].state = missing;
		setstate(failure);
		return;
	}	

	
	// create the info.vdr file
	infofile = savedir + "/info.vdr";
	bzero(&savedirstat, sizeof(savedirstat));
	if ( (stat(infofile.c_str(), &savedirstat)) == -1)
	{
		infofd = fopen(infofile.c_str(), "w");
		infotext = _channel->GetChannelID().ToString();
		fprintf(infofd, "C %s\n", infotext.c_str()); 
		///\todo also store EPG data in the infofile - the user might want to see what was transferred (so far he sees only the channel and timestamp).
		fclose(infofd);
	}
	
	_source.first3hashes(dfr);
	request = "GET ";
	request += uinttostr(dfr->hash1, 10);
	request += ",";
	request += uinttostr(dfr->hash2, 10);
	request += ",";
	request += uinttostr(dfr->hash3, 10);
	request += ",";
	request += uinttostr(dfr->length, 10);
	request += "\n";
	
	// transport is expected to look like tcp://my.hostname.com:12345
	buf = _source.transport();
	marker1 = buf.rfind(":")+1;
	marker2 = buf.find("/")+2;
	serverport = buf.substr(marker1, buf.size());
	serveraddress = buf.substr(marker2, marker1-marker2-1);
	serverprotocol = buf.substr(0, marker1-2);
	///\bug check for valid input and, if possible, force the user to enter something sensible!
	
	printf("CLIENT %d is sending request: %s to %s:%s\n", _threadnumber, request.substr(0, request.size()-1).c_str(), serveraddress.c_str(), serverport.c_str());
	sockfd = tcp_connect(serveraddress.c_str(), serverport.c_str());

	if ( sockfd == -1 )
	{
		//dsyslog("%s: CLIENT %d: allocation of socket descriptor failed, exiting", __PRETTY_FUNCTION__, _threadnumber);
		return;
	}
	
	if ( write(sockfd, request.c_str(), request.size()) == -1 ) 
	{
		//dsyslog("%s: CLIENT %d: error while sending request - terminating clientthread", __PRETTY_FUNCTION__, _threadnumber);
		setstate(failure);
		close(sockfd);
		return;
	}
	else
	{
		bytesread = recv(sockfd, buffer, 3, 0);
		answer.assign(buffer, bytesread);
		
		if( answer.substr(0,3) == "200") 	// answer starts with 200 (OK) ?
		{
			answer.erase(0, 3);
		
			filecount = 1;
			snprintf(currentfile, MAXPATHLENGTH, "%s%s%03i%s", savedir.c_str(), "/", _threadnumber, ".vdr");			
			recvfd = fopen(currentfile, "w");
			if (recvfd == NULL) 
			{
				//dsyslog("%s: CLIENT %d: unable to open %s for writing - exiting", __PRETTY_FUNCTION__, _threadnumber, currentfile);
				setstate(failure);
				close(sockfd);
				return;
			}
			while ( (bytesread = recv(sockfd, buffer, HUGEBUFFSIZE, 0)) > 0 )
			{
				byteswritten = 0;
				while ( byteswritten < bytesread )
				{
					byteswritten += fwrite(buffer + byteswritten, 1, bytesread - byteswritten, recvfd);
				}
			}//while
			fclose(recvfd);
			close(sockfd);
			setstate(successful);
			_dlstatus[_threadnumber].state = finished;
		}
		else
		{
			//dsyslog("%s: CLIENT %d: Bad request: Server sent %s as answer. Terminating...", __PRETTY_FUNCTION__, _threadnumber, answer.c_str());
			setstate(failure);
			close(sockfd);
			return;
		}
		setstate(successful);
		close(sockfd);
		//dsyslog("%s: CLIENT %d: successful, shutting down.", __PRETTY_FUNCTION__, _threadnumber);
	}
}

