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


#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include <tools.h>

#include "downloadstatus.h"

// the current use of this lock may lead to deadlocking if a clientthread that is holding the lock dies before mutex.Unlock() is called
cMutex downloadstatus::mutex;

/**
 * Inititalize.
 */
downloadstatus::downloadstatus()
	:_snippetfile(-1),_snippets(NULL),_length(0)
{
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	_snippetsize = sizeof(snippet);
}

/**
 * Write the last thing to disk.
 */
downloadstatus::~downloadstatus(){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	if (_length){
		munmap(_snippets, _length);
	}
	if (_snippetfile >=0){
		close(_snippetfile);
	}
}

/**
 * Change the directory we are talking about.
 * This function does no chdir() call but creates a save-file in the new directory.
 * \param dir The dir where the downloadstatus should be remembered.
 * \return 0 on success, -1 otherwise.
 */
int
downloadstatus::switchdir(string dir){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	const int errmsgsize = 1024;
	char errmsg[errmsgsize];
	mutex.Lock();
	string snippetfilename = dir + "/downloadstatus.videotransport.vdr";
	_snippetfile = open(snippetfilename.c_str(), O_RDWR | O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
	if (_snippetfile < 0){
		strerror_r(errno, errmsg, errmsgsize);
		esyslog("unable to create downloadstatus (%s): %s", snippetfilename.c_str(), errmsg);
		mutex.Unlock();
		return -1;
	}
	struct stat stats;
	fstat(_snippetfile, &stats); // we allow no failures :) (there are unlikely because the file opened)
	_length = (stats.st_size / _snippetsize); // integer arithmetic should do the right thing.
	///poi was +1; does this really do the right thing?
	ftruncate(_snippetfile, _length * _snippetsize); // ensure proper file length
	_snippets = (snippet*)mmap(NULL, _length * _snippetsize, PROT_READ|PROT_WRITE, MAP_SHARED, _snippetfile, 0);
	if (_snippets == MAP_FAILED){
		strerror_r(errno, errmsg, errmsgsize);
		esyslog("unable to mmap() fd %i: %s", _snippetfile, errmsg);
		close(_snippetfile);
		_snippetfile = -1;
		mutex.Unlock();
		return -1;
	}
	mutex.Unlock();
	return 0;
}

/**
 * Give access to a snippet object.
 * This function will return a reference to a snippet, therefore it may be used for reading and writing.
 * \return The reference. If this is not possible for some reason (e.g. programmer forgot to use switchdir() before),
 * a reference to a private snippet is returned. Additionally an error message is logged.
 */

snippet &
downloadstatus::operator[](unsigned int which){
	//dsyslog("%s: begin", __PRETTY_FUNCTION__);
	const int errmsgsize = 1024;
	char errmsg[errmsgsize];
	mutex.Lock();
	if (which >= _length){
		munmap(_snippets, _length);
		_length = which + 1; // because the array is supposed to start at 1, not at 0 (there is no vdr.000)
		ftruncate(_snippetfile, _length * _snippetsize); // ensure proper file length
		_snippets = (snippet*)mmap(NULL, _length * _snippetsize, PROT_READ|PROT_WRITE, MAP_SHARED, _snippetfile, 0);
		if (_snippets == MAP_FAILED){
			strerror_r(errno, errmsg, errmsgsize);
			esyslog("unable to mmap() fd %i: %s. Returning dummy", _snippetfile, errmsg);
			close(_snippetfile);
			_snippetfile = -1;
			mutex.Unlock();
			return _dummy;
		}
	}
	mutex.Unlock();
	return _snippets[which];
}


