/*
 * (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: serverthread.cc,v 1.4 2006/01/05 10:31:44 denk Exp $
 *
 */

// vidtrans
#include "helper.h"
#include "serverthread.h"

serverthread::serverthread(string transport, watchdog *wd, vdrrecordings * recording)
	: _transport(transport), _wd(wd), _recording(recording){

}

serverthread::csubserverthread::csubserverthread(int connfd, watchdog * _wd, vdrrecordings * recording)
	: _connfd(connfd), _watchdog(_wd), _recording(recording)
{	
}
void
serverthread::Action(){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	
	int listenfd, connfd;
	socklen_t len;
	struct sockaddr_in cliaddr, servaddr;
	int pos, port;
	_wd->Start();
	
	listenfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	pos = _transport.rfind(":");
	port = strtouint(_transport.substr(pos+1), 10);
	servaddr.sin_port = htons(port);
		
	bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	listen(listenfd, LISTENQ);

	while (true){
		len = sizeof(cliaddr);
		connfd = accept(listenfd, (SA *) &cliaddr, &len);
		if (connfd == -1){
			// don't use strerror because we are multithreaded
			char ebuf[1000];
			strerror_r(errno, ebuf, 1000);
			//dsyslog("%s: accept: %s", __PRETTY_FUNCTION__, ebuf);
		} else {
			spawn_subserverthread(connfd, _wd, _recording);
		}
	}
}

void
serverthread::spawn_subserverthread(int connfd, watchdog * mywatchdog, vdrrecordings * recording){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	csubserverthread * myserver;
	myserver = new csubserverthread(connfd, mywatchdog, recording);
	myserver->Start();
}

void
serverthread::csubserverthread::Action(){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	
	string strrequest, stranswer;
	char request[BUFFSIZE], buffer[BUFFSIZE], *newline;
	ssize_t bytesread = 0;
	ssize_t bytessent = 0;
	ssize_t justsent = 0;
	ssize_t len = 0;
	uint32_t bytescopied = 0;
	FILE * reqfd;
	struct fileandpos file;
	struct fileandpos * fileptr;
	uint32_t hash1, hash2, hash3;
	uint32_t length;
	unsigned long filesize;
	string nextfile, title, channel;
	string * next;
	int testval;
	unsigned long sleeptime;

	next = &nextfile;
	fileptr = &file;

	///\todo implement buffering as there is no guarantee that we receive chunks of sensible size to trigger IO operations
	if ( (len = recv(_connfd, request, BUFFSIZE, 0)) > 0 )
	{
		request[len]='\0';		// null terminate the char array (client does not send end of string)
		if ( (newline = strchr(request, '\n')) != NULL ) *newline = '\0';	// remove the newline from the end of the line
		if ( (newline = strchr(request, '\r')) != NULL ) *newline = '\0';	// also remove the dos newline from the end of the line
		
		strrequest = request;

		if( strrequest.substr(0,4) != "GET ") 	// recvline starts with GET 0x20 ?
		{
			stranswer = "400";
			send(_connfd, stranswer.c_str(), stranswer.size(), 0);
			// terminates connection if invalid request is sent
		}
		else
		{
			sscanf((strrequest.substr(4,strrequest.size()-1)).c_str(),"%u,%u,%u,%u", &hash1, &hash2, &hash3, &length); 	
			if (_recording->startsending(hash1, hash2, hash3, fileptr) == 0) 
			{
				printf("SERVER sending %d bytes as answer to request %s\n", length, strrequest.c_str());
				stranswer = "200";
				send(_connfd, stranswer.c_str(), stranswer.size(), 0);		// send OK

				reqfd = fopen(file.path, "r");
				
				fseek(reqfd, 0L, SEEK_END);
				filesize = ftell(reqfd);
				fseek(reqfd, 0L, SEEK_SET);
				
				fseek(reqfd, file.position, 0);
				
				testval = filesize - ( file.position + length );
				
				if ( testval > 0 )		// request can be fulfilled from a single recfile	
				{
					while ( bytescopied < length ) 
					{	
						if ( (length - bytescopied) < BUFFSIZE ) bytesread = fread(buffer, 1, length - bytescopied, reqfd);
						else bytesread = fread(buffer, 1, BUFFSIZE, reqfd);
						sleeptime = _watchdog->getsleeptime(bytesread);
						sleep(sleeptime);
						while( bytessent < bytesread )
						{
							justsent += send(_connfd, buffer + bytessent, bytesread - bytessent, 0);
							if ( justsent == -1 )
							{
								break;
							}
							else bytessent += justsent;
							justsent = 0;
						}//while
						bytescopied += bytessent;
						bytessent = 0;
					}//while
				}
				else				// several files are needed to process the request
				{
					while ( bytescopied < length )
					{
						if ( (length - bytescopied) < BUFFSIZE ) 
						{
							bytesread = fread(buffer, 1, length - bytescopied, reqfd);
						}
						else bytesread = fread(buffer, 1, BUFFSIZE, reqfd);
						
						if ( feof(reqfd) )	// hit end of file
						{
							clearerr(reqfd);
							fclose(reqfd);
							_recording->getnextrecfile(file.path, next); ///\bug check for error (-1)
							if ( (reqfd = fopen(nextfile.c_str(), "r")) == NULL )
							{
								break;
							}
						}
						bytessent = 0;
						sleeptime = _watchdog->getsleeptime(bytesread);
						sleep(sleeptime);
						while ( bytessent < bytesread )
						{
							justsent += send(_connfd, buffer + bytessent, bytesread - bytessent, 0);
							if ( justsent == -1 )
							{
								break;
							}
							else bytessent += justsent;
						}//while
						bytescopied = bytescopied + bytessent;
					}//while	
				}
				fclose(reqfd);
				printf("SERVER sent %d bytes as answer to request %s\n", bytescopied, strrequest.c_str());
			}
			else
			{
				stranswer = "400";
				send(_connfd, stranswer.c_str(), stranswer.size(), 0);
				// terminate connection as request cannot be served 
			}
		}//else
	}//if
	close(_connfd);
}

