/*
 * Copyright (C) 2006-2007	Andre Beckedorf
 * 					 		<evilJazz _AT_ katastrophos _DOT_ net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <qstring.h>
#include <qdatastream.h>
#include <qtextcodec.h>

#include "media.h"
#include "mediaidentifier.h"
#include "mediadatabase.h"
#include "mplayer.h"
#include "quasar.h"
#include "configuration.h"
#include "debug.h"

MediaLocation::MediaLocation()
	: QUrl()
{
}

MediaLocation::MediaLocation(const QString& url)
	: QUrl(url)
{
}


MediaAudio::MediaAudio(Media *parent)
	:	codec(""),
		format(0),
		bitrate(0),
		sampleRate(0),
		channels(0),
		owner(parent)
{
}

/* class MediaAudio */

bool MediaAudio::save()
{
	// insert audio related information for the media
	if (owner->isAudio() || owner->isVideo())
	{
		sqlite3_stmt *vm = owner->owningMediaDatabase()->stmtInsertMediaAudio;
		
		sqlite3_bind_int(vm, 1, owner->mediaid_);
		sqlite3_bind_text(vm, 2, codec.utf8(), -1, SQLITE_TRANSIENT);
		sqlite3_bind_int(vm, 3, format);
		sqlite3_bind_int(vm, 4, bitrate);
		sqlite3_bind_int(vm, 5, sampleRate);
		sqlite3_bind_int(vm, 6, channels);	

		sqlite3_step(vm);
		DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaAudio"));
		sqlite3_reset(vm);
		
		return true;
	}
	else
		return false;
}

bool MediaAudio::load()
{
	DHPRINTF("MediaAudio::load() media_id = %d", owner->mediaid_);
	
	if (owner->mediaid_ == 0) 
		return false;
	
	// load audio info...
	if (owner->isAudio() || owner->isVideo())
	{
		sqlite3_stmt *vm = owner->owningMediaDatabase()->stmtSelectMediaAudioByMediaID;
		sqlite3_bind_int(vm, 1, owner->mediaid_);
		if (sqlite3_step(vm) == SQLITE_ROW)
		{
			codec = QString::fromUtf8((const char *)sqlite3_column_text(vm, 1));
			format = sqlite3_column_int(vm, 2);
			bitrate = sqlite3_column_int(vm, 3);
			sampleRate = sqlite3_column_int(vm, 4);
			channels = sqlite3_column_int(vm, 5);
			
			sqlite3_reset(vm);
			
			return true;
		}
		else
		{
			DHOP(owner->owningMediaDatabase()->dbDebug());
			sqlite3_reset(vm);
			return false;
		}
	}
	else
		return false;
}

/* class MediaVideo */

MediaVideo::MediaVideo(Media *parent)
	: 	codec(""),
		bitrate(0),
		width(0),
		height(0),
		FPS(0),
		aspectRatio(0),
		owner(parent)
{
}

bool MediaVideo::save()
{
	// insert video related information for the media
	if (owner->isVideo())
	{
		sqlite3_stmt *vm = owner->owningMediaDatabase()->stmtInsertMediaVideo;
		
		sqlite3_bind_int(vm, 1, owner->mediaid_);
		sqlite3_bind_text(vm, 2, codec.utf8(), -1, SQLITE_TRANSIENT);
		sqlite3_bind_int(vm, 3, bitrate);
		sqlite3_bind_int(vm, 4, width);
		sqlite3_bind_int(vm, 5, height);
		sqlite3_bind_double(vm, 6, FPS);
		sqlite3_bind_double(vm, 7, aspectRatio);

		sqlite3_step(vm);
		DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaVideo"));
		sqlite3_reset(vm);

		return true;
	}
	else
		return false;
}

bool MediaVideo::load()
{
	DHPRINTF("MediaVideo::load() media_id = %d", owner->mediaid_);
	
	if (owner->mediaid_ == 0) 
		return false;
	
	// load video info...
	if (owner->isVideo())
	{
		sqlite3_stmt *vm = owner->owningMediaDatabase()->stmtSelectMediaVideoByMediaID;
		sqlite3_bind_int(vm, 1, owner->mediaid_);
		if (sqlite3_step(vm) == SQLITE_ROW)
		{
			codec = QString::fromUtf8((const char *)sqlite3_column_text(vm, 1));
			bitrate = sqlite3_column_int(vm, 2);
			width = sqlite3_column_int(vm, 3);
			height = sqlite3_column_int(vm, 4);
			FPS = sqlite3_column_double(vm, 5);
			aspectRatio = sqlite3_column_double(vm, 6);				

			sqlite3_reset(vm);
			
			return true;
		}
		else
		{
			DHOP(owner->owningMediaDatabase()->dbDebug());
			sqlite3_reset(vm);
			return false;
		}
	}
	else
		return false;
}

/* class MediaMetadataExt */

MediaMetadataExt::MediaMetadataExt(Media *parent)
	: owner(parent)
{
}

bool MediaMetadataExt::save()
{
	//DPRINTF("MediaMetadataExt::save() media_id = %d", owner->mediaid_);
	sqlite3_stmt *vm;
	
	// delete all metadata tags for mediaid
	vm = owner->owningMediaDatabase()->stmtDeleteMediaMetadataExtByMediaID;
	sqlite3_bind_int(vm, 1, owner->mediaid_);
	sqlite3_step(vm);
	DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaMetaDataExt"));			
	sqlite3_reset(vm);
	
	// insert new metadata tags
	vm = owner->owningMediaDatabase()->stmtInsertMediaMetadataExt;
	for (MetadataMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		for (MetadataEntry::ConstIterator it2 = it.data().begin(); it2 != it.data().end(); ++it2)
		{
			sqlite3_bind_int(vm, 1, owner->mediaid_);
			sqlite3_bind_text(vm, 2, it.key(), -1, SQLITE_TRANSIENT);
			sqlite3_bind_text(vm, 3, (*it2).utf8(), -1, SQLITE_TRANSIENT);
			sqlite3_step(vm);
			DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaMetaDataExt"));			
			sqlite3_reset(vm);
		}
	}
	return true;
}

bool MediaMetadataExt::load()
{
	DHPRINTF("MediaMetadataExt::load() media_id = %d", owner->mediaid_);
	
	if (owner->mediaid_ == 0) 
		return false;
	
	sqlite3_stmt *vm = owner->owningMediaDatabase()->stmtSelectMediaMetadataExtByMediaID;
	sqlite3_bind_int(vm, 1, owner->mediaid_);
	while (sqlite3_step(vm) == SQLITE_ROW)
	{
		QCString tag((const char *)sqlite3_column_text(vm, 1));
		QString value = QString::fromUtf8((const char *)sqlite3_column_text(vm, 2));
		
		//DPRINTF("%s = %s", (const char *)tag, value.latin1());
		
		MetadataMap::ConstIterator it = map.find(tag);
		if (it == map.end())
			map.insert(tag, QValueList<QString>());

		map[tag].append(value);
	}

	sqlite3_reset(vm);
	
	return true;
}

/* class MediaMetadata */

MediaMetadata::MediaMetadata(Media *parent)
	: 	trackNo(""),
		title(""),
		artist(""),
		album(""),
		playTime(""),
		genre(""),
		length(0),
		year(0),
		compilation(0),
		owner(parent)	
{
//	DPRINTF("new media internal for %d", (int)parent);
}

MediaMetadata::~MediaMetadata()
{
//	DPRINTF("remove media internal for %d", (int)owner);
	
	if (owner && owner->medint_ == this)
		owner->medint_ = NULL;
}

bool MediaMetadata::save()
{
	sqlite3_stmt *vm;

	// insert meta data for the media
	vm = owner->owningMediaDatabase()->stmtInsertMediaMetaData;
	// UPDATE media SET playtime = ?2, track = ?3, title = ?4, album = ?5, artist = ?6, genre = ?7, year = ?8, compilation = ?9 WHERE media_id = ?1;
	
	sqlite3_bind_int(vm, 1, owner->mediaid_);
	sqlite3_bind_int(vm, 2, length);
	sqlite3_bind_text(vm, 3, (trackNo == "-" ? QString::number(0) : trackNo).utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_text(vm, 4, title.utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_text(vm, 5, album.utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_text(vm, 6, artist.utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_text(vm, 7, genre.utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_int(vm, 8, year);
	sqlite3_bind_int(vm, 9, compilation);
	sqlite3_step(vm);
	DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaMetaData"));	
	sqlite3_reset(vm);
		
	return true;
}

bool MediaMetadata::load()
{
	DHPRINTF("MediaMetadata::load() media_id = %d", owner->mediaid_);
	
	if (owner->mediaid_ == 0) 
		return false;
	
	bool retval = false;
	sqlite3_stmt *vm;

	// load meta data...
	vm = owner->owningMediaDatabase()->stmtSelectMediaMetadataByMediaID;
	// SELECT playtime, track, title, album, artist, genre, year, compilation FROM media WHERE media_id = ?1;

	sqlite3_bind_int(vm, 1, owner->mediaid_);
	DHOP(owner->owningMediaDatabase()->dbDebug("sqlite3_bind_int(vm, 1, owner->mediaid_);"));		
	
	int errno = sqlite3_step(vm);
	DHPRINTF("sqlite3_step errno: %d", errno);
	if (errno == SQLITE_ROW)
	{
		length  = sqlite3_column_int(vm, 0);
		trackNo = QString::fromUtf8((const char *)sqlite3_column_text(vm, 1));
		title   = QString::fromUtf8((const char *)sqlite3_column_text(vm, 2));
		album   = QString::fromUtf8((const char *)sqlite3_column_text(vm, 3));
		artist  = QString::fromUtf8((const char *)sqlite3_column_text(vm, 4));
		genre   = QString::fromUtf8((const char *)sqlite3_column_text(vm, 5));
		year    = sqlite3_column_int(vm, 6);
		compilation = sqlite3_column_int(vm, 7); 
		updatePlaytime();
		retval = true;
	}
	else
	{
		DHOP(owner->owningMediaDatabase()->dbDebug("MediaMetadata::load()"));		
	}
	sqlite3_reset(vm);	
	
	return retval;
}

void MediaMetadata::updatePlaytime()
{
	playTime = QString().sprintf("%3d:%02d", length / 60, length % 60);
}


/* class MediaFileInfo */

MediaFileInfo::MediaFileInfo(Media *parent)
	:	mediaType(MediaFileInfo::MEDIATYPE_AUDIO),
		locationHash(0),
		location(),
		fileSize(0),
		lastModified(),
		owner(parent)	
{
//	DPRINTF("new media internal for %d", (int)parent);
}

MediaFileInfo::~MediaFileInfo()
{
//	DPRINTF("remove media internal for %d", (int)owner);
	
	if (owner && owner->medinfint_ == this)
		owner->medinfint_ = NULL;
}

bool MediaFileInfo::save()
{
	sqlite3_stmt *vm;

	// insert meta data for the media
	vm = owner->owningMediaDatabase()->stmtInsertMediaFileInfo;
	// UPDATE media SET hash = ?2, location_id = ?3, filename = ?4, type = ?5, filesize = ?6, modified_date = ?7 WHERE media_id = ?1;
	
	sqlite3_bind_int(vm, 1, owner->mediaid_);
	
	locationHash = elfHashString(location.toString());
	sqlite3_bind_int(vm, 2, locationHash);
	
	// splitting the location string manually into path and filename
	// since QUrl::fileName() and QUrl::dirPath() behave in an unwanted manner
	// when the location string contains a question mark or ampersand.
	QString path = location.toString();
	int splitPos = path.findRev('/');
	QString filename = path.mid(splitPos + 1);
	path = path.left(splitPos + 1);

	int locationID = 0;
	
	// try to resolve existing entry for the current location...
	sqlite3_stmt *vm2 = owner->owningMediaDatabase()->stmtSelectLocationIDByLocation;
	sqlite3_bind_text(vm2, 1, path.utf8(), -1, SQLITE_TRANSIENT);
	if (sqlite3_step(vm2) == SQLITE_ROW)
	{
		// if found, use the ID...
		locationID = sqlite3_column_int(vm2, 0);
	}
	else
	{
		// ... otherwise add new item and use the new assigned ID.
		sqlite3_stmt *vm3 = owner->owningMediaDatabase()->stmtInsertMediaLocation;
		sqlite3_bind_null(vm3, 1);
		sqlite3_bind_text(vm3, 2, path.utf8(), -1, SQLITE_TRANSIENT);
		sqlite3_step(vm3);
		locationID = sqlite3_last_insert_rowid(owner->owningMediaDatabase()->db());		
		sqlite3_reset(vm3);
	}
	sqlite3_reset(vm2);	
	
	sqlite3_bind_int(vm, 3, locationID);
	
	sqlite3_bind_text(vm, 4, filename.utf8(), -1, SQLITE_TRANSIENT);
	
	sqlite3_bind_int(vm, 5, mediaType);
	sqlite3_bind_int(vm, 6, fileSize);
	QString lastMod = dbFormatDate(lastModified);
	sqlite3_bind_text(vm, 7, lastMod.utf8(), -1, SQLITE_TRANSIENT);
		
	sqlite3_step(vm);
	DHOP(owner->owningMediaDatabase()->dbDebug("stmtInsertMediaFileInfo"));	
	sqlite3_reset(vm);
		
	return true;
}

bool MediaFileInfo::load()
{
	DPRINTF("MediaFileInfo::load() media_id = %d", owner->mediaid_);
	
	if (owner->mediaid_ == 0) 
		return false;
	
	bool retval = false;
	sqlite3_stmt *vm;

	// load meta data...
	vm = owner->owningMediaDatabase()->stmtSelectMediaFileInfoByMediaID;
	// SELECT hash, location | filename, type, filesize, modified_date FROM media WHERE media_id = ?1 AND media.location_id = media_location.location_id;
	
	sqlite3_bind_int(vm, 1, owner->mediaid_);
	DOP(owner->owningMediaDatabase()->dbDebug("sqlite3_bind_int(vm, 1, owner->mediaid_);"));		
	
	int errno = sqlite3_step(vm);
	DPRINTF("sqlite3_step errno: %d", errno);
	if (errno == SQLITE_ROW)
	{
		locationHash = sqlite3_column_int(vm, 0);
		
		// TODO: resolve location by locationID and fill location with constructed data...
		//int locationID = sqlite3_column_int(vm, 1);
		//QString filename = QString::fromUtf8((const char *)sqlite3_column_text(vm, 2));
		location = MediaLocation(QString::fromUtf8((const char *)sqlite3_column_text(vm, 1)));
		
		mediaType = (MediaFileInfo::MediaType)sqlite3_column_int(vm, 2);
		fileSize = sqlite3_column_int(vm, 3);
		DHPRINTF("fileSize = %d", (int)fileSize);
		
		QString lastMod = QString::fromUtf8((const char *)sqlite3_column_text(vm, 4));
		DHPRINTF("lastMod = %s", (const char *)lastMod.latin1());
		
		QDate date;
		// TODO: Get rid of stupid sanity check...
		int y = lastMod.mid(0,4).toInt(); if (y > 2020) y = 2020;
		int m = lastMod.mid(4,2).toInt(); if (m > 12)   m = 12;
		int d = lastMod.mid(6,2).toInt(); if (d > 31)   d = 31;
		if (!date.setYMD(y, m, d))
			date.setYMD(1980, 10, 04);
		
		QTime time;
		//TODO: Get rid of stupid sanity check...
		int h = lastMod.mid(8,2).toInt();  if (h > 24) h = 24;
			m = lastMod.mid(10,2).toInt(); if (m > 59) m = 59;
		int s = lastMod.mid(12,2).toInt(); if (s > 59) s = 59;
		time.setHMS(h, m, s);
		
		lastModified.setDate(date);
		lastModified.setTime(time);			
		DPRINTF("parsed lastMod -> _lastModified = %s", (const char *)lastModified.toString().latin1());
		
		retval = true;
	}
	else
	{
		DOP(owner->owningMediaDatabase()->dbDebug("MediaFileInfo::load()"));		
	}
	sqlite3_reset(vm);	
	
	return retval;
}


Media::Media(QObject *parent, const char *name)
	: QObject(parent, name),
	  medint_(NULL),
	  medinfint_(NULL),
	  mediaid_(0)
{
}

Media::~Media()
{
}

void Media::initializeFromLocation(const MediaLocation &location)
{
	DHENTERMETHOD("Media::initializeFromLocation(%s)", (const char*)(location.toString().utf8()));
	
	MediaFileInfo *mfi = mediaFileInfo();
	
	mfi->location = location;
	mfi->locationHash = elfHashString(location.toString());
	
	identify(location);
	
	if (location.isLocalFile() && QFile::exists(location.toString()))
	{
		QFileInfo fileInfo = QFileInfo(location.toString()); 
		mfi->lastModified = fileInfo.lastModified();
		mfi->fileSize = fileInfo.size();
		updateDisplayInformation(location);
		DHOP(dumpInfo());
	}
	
	DHEXITMETHOD("Media::initializeFromLocation(%s)", (const char*)(location.toString().utf8()));
}

bool Media::loadByID(int mediaid)
{
	DHENTERMETHOD("Media::loadByID(%d)", mediaid);
	
	sqlite3_stmt *vm = owningMediaDatabase()->stmtSelectMediaIDByMediaID;
	sqlite3_bind_int(vm, 1, mediaid);
	DHOP(owningMediaDatabase()->dbDebug("stmtSelectMediaIDByMediaID"));
	
	bool retval = loadFromDBVM(vm);
	
	DHEXITMETHOD("Media::loadByID(%d)", mediaid);
	return retval;
}

bool Media::loadByLocation(const MediaLocation &location)
{
	DHENTERMETHOD("Media::loadByLocation(%s)", (const char*)(location.toString().utf8()));
	
	sqlite3_stmt *vm = owningMediaDatabase()->stmtSelectMediaIDByFilename;
	
	QString path = location.dirPath();
	if (path.right(1) != "/") path += "/"; // IMPORTANT!!
	
	QString filename = location.fileName();
	sqlite3_bind_text(vm, 1, path.utf8(), -1, SQLITE_TRANSIENT);
	sqlite3_bind_text(vm, 2, filename.utf8(), -1, SQLITE_TRANSIENT);
	DHOP(owningMediaDatabase()->dbDebug("stmtSelectMediaIDByFilename"));
	
	bool retval = loadFromDBVM(vm);
	
	DHEXITMETHOD("Media::loadByLocation(%s)", (const char*)(location.toString().utf8()));
	return retval;
}

bool Media::loadByHash(uint hash)
{
	DHENTERMETHOD("Media::loadByHash(%d)", hash);
	
	sqlite3_stmt *vm = owningMediaDatabase()->stmtSelectMediaIDByHash;
	sqlite3_bind_int(vm, 1, hash);
	DHOP(owningMediaDatabase()->dbDebug("stmtSelectMediaIDByHash"));
	
	bool retval = loadFromDBVM(vm);
	
	DHEXITMETHOD("Media::loadByHash(%d)", hash);
	return retval;
}

bool Media::loadFromDBVM(sqlite3_stmt *vm)
{	
	int retval = false;
	
	if (sqlite3_step(vm) == SQLITE_ROW)
	{
		DHPRINTF("loading from database...");		
		
		mediaid_ = sqlite3_column_int(vm, 0);
		DPRINTF("mediaid = %d", (int)mediaid_);
		
		retval = true;
	}
	sqlite3_reset(vm);
		
	return retval;
}

MediaAudio * Media::mediaAudio(bool autoNew) const
{ 
	MediaDatabase *mediaDatabase = owningMediaDatabase();
	Media *self = const_cast<Media*>(this);
	MediaAudio *mediaaudio = mediaDatabase->cacheMediaAudio_->find((int)self);

	if (mediaaudio)
	{
		DHPRINTF("Media::mediaAudio() existing : %d", (int)mediaaudio);
		return mediaaudio;
	}
	else if (autoNew)
	{
		mediaaudio = new MediaAudio(self);
		if (!mediaDatabase->cacheMediaAudio_->insert((int)self, mediaaudio))
		{
			// hell will most likely break loose, if we enter here...
			// should not happen.
			delete mediaaudio;
			mediaaudio = NULL;
		}
		else
			mediaaudio->load();		
		
		DHPRINTF("Media::mediaAudio() new : %d", (int)mediaaudio);
		return mediaaudio;
	}
	else
		return NULL;
}

MediaVideo * Media::mediaVideo(bool autoNew) const
{ 
	MediaDatabase *mediaDatabase = owningMediaDatabase();
	Media *self = const_cast<Media*>(this);
	MediaVideo *mediavideo = mediaDatabase->cacheMediaVideo_->find((int)self);

	if (mediavideo)
		return mediavideo;
	else if (autoNew)
	{
		mediavideo = new MediaVideo(self);
		if (!mediaDatabase->cacheMediaVideo_->insert((int)self, mediavideo))
		{
			// hell will most likely break loose, if we enter here...
			// should not happen.
			delete mediavideo;
			mediavideo = NULL;
		}
		else
			mediavideo->load();		
			
		return mediavideo;
	}
	else
		return NULL;
}

MediaMetadataExt * Media::mediaMetadataExt(bool autoNew) const
{
	MediaDatabase *mediaDatabase = owningMediaDatabase();
	Media *self = const_cast<Media*>(this);
	MediaMetadataExt *mediametadata = mediaDatabase->cacheMediaMetadataExt_->find((int)self);

	if (mediametadata)
	{
		DHPRINTF("Media::mediaMetadataExt() existing : %d", (int)mediametadata);
		return mediametadata;
	}
	else if (autoNew)
	{
		mediametadata = new MediaMetadataExt(self);
		if (!mediaDatabase->cacheMediaMetadataExt_->insert((int)self, mediametadata))
		{
			// hell will most likely break loose, if we enter here...
			// should not happen.
			delete mediametadata;
			mediametadata = NULL;
		}
		else
			mediametadata->load();
			
		DHPRINTF("Media::mediaMetadataExt() new : %d", (int)mediametadata);
		return mediametadata;
	}
	else
		return NULL;
}

MediaMetadata * Media::mediaMetadataEx(bool autoNew) const
{
	if (medint_)
		return medint_;
	else
	{
		MediaDatabase *mediaDatabase = owningMediaDatabase();
		Media *self = const_cast<Media*>(this);
		self->medint_ = mediaDatabase->cacheMediaMetadata_->find((int)self);

		if (self->medint_)
		{
			DHPRINTF("Media::mediaMetadata() existing via lookup : %d", (int)self->medint_);
			return self->medint_;
		}
		else if (autoNew)
		{
			self->medint_ = new MediaMetadata(self);
			if (!mediaDatabase->cacheMediaMetadata_->insert((int)self, self->medint_))
			{
				// hell will most likely break loose, if we enter here...
				// should not happen.
				delete self->medint_;
				self->medint_ = NULL;
			}
			else
				self->medint_->load();
				
			DHPRINTF("Media::mediaMetadata() new : %d", (int)self->medint_);
			return self->medint_;
		}
		else
			return NULL;
	}
}

MediaFileInfo * Media::mediaFileInfoEx(bool autoNew) const
{
	if (medinfint_)
		return medinfint_;
	else
	{
		MediaDatabase *mediaDatabase = owningMediaDatabase();
		Media *self = const_cast<Media*>(this);
		self->medinfint_ = mediaDatabase->cacheMediaFileInfo_->find((int)self);

		if (self->medinfint_)
		{
			DHPRINTF("Media::mediaFileInfo() existing via lookup : %d", (int)self->medinfint_);
			return self->medinfint_;
		}
		else if (autoNew)
		{
			self->medinfint_ = new MediaFileInfo(self);
			if (!mediaDatabase->cacheMediaFileInfo_->insert((int)self, self->medinfint_))
			{
				// hell will most likely break loose, if we enter here...
				// should not happen.
				delete self->medinfint_;
				self->medinfint_ = NULL;
			}
			else
				self->medinfint_->load();
				
			DHPRINTF("Media::mediaFileInfo() new : %d", (int)self->medinfint_);
			return self->medinfint_;
		}
		else
			return NULL;
	}
}

void Media::saveToDB()
{
	DHENTERMETHOD("Media::saveToDB()");

	sqlite3_stmt *vm;
	MediaDatabase *mediaDatabase = owningMediaDatabase();

	DHPRINTF("Location: %s", location().toString().latin1());
	
	if (mediaid_) 
 	{
		DHPRINTF("updating existing media by mediaid: %d", mediaid_);
		vm = mediaDatabase->stmtDeleteMediaByMediaID;
		sqlite3_bind_int(vm, 1, mediaid_);
		sqlite3_step(vm);
		sqlite3_reset(vm);
		
		DHOP(mediaDatabase->dbDebug("stmtDeleteMediaByMediaID"));
	
		vm = mediaDatabase->stmtInsertMedia;
		sqlite3_reset(vm);		
		sqlite3_bind_int(vm, 1, mediaid_);
	}
	else
	{
		vm = mediaDatabase->stmtInsertMedia;
		sqlite3_reset(vm);

		DHPRINTF("inserting new media...");
		sqlite3_bind_null(vm, 1);
	}
		
	sqlite3_step(vm);
	DHOP(mediaDatabase->dbDebug("stmtInsertMedia"));
	sqlite3_reset(vm);
	
	if (!mediaid_)
		mediaid_ = mediaDatabase->lastInsertID();

	DHPRINTF("new media ID is %d", mediaid_);
	DHOP(dumpInfo());

	MediaMetadata *mediainternal = mediaMetadata(false);
	if (mediainternal)
		mediainternal->save();

	MediaFileInfo *mediaFileInfoInternal = mediaFileInfo(false);
	if (mediaFileInfoInternal)
		mediaFileInfoInternal->save();
		
	if (mediaFileInfoInternal->mediaType != MediaFileInfo::MEDIATYPE_UNKNOWN)
	{
		MediaAudio *mediaaudio = mediaAudio(false);
		if (mediaaudio)
			mediaaudio->save();
			
		MediaVideo *mediavideo = mediaVideo(false);
		if (mediavideo)
			mediavideo->save();
		
		MediaMetadataExt *mediametadata = mediaMetadataExt(false);
		if (mediametadata)
			mediametadata->save();
	}
	
	DHEXITMETHOD("Media::saveToDB()");
}

void Media::identify(const MediaLocation &location)
{
	MediaIdentifier::singleton().identify(this, location);
}

void Media::updateDisplayInformation(const MediaLocation &location)
{
	QString tracknumber = metadata("TRACK");
	if (tracknumber.isNull() || tracknumber.isEmpty())
		mediaMetadata()->trackNo = "-";
	else
		mediaMetadata()->trackNo = tracknumber;

	QString title = metadata("TITLE");
	if (title.isNull() || title.isEmpty())
		if (location.isLocalFile())
			mediaMetadata()->title = location.fileName();
		else
			mediaMetadata()->title = location.toString();
	else
		mediaMetadata()->title = title;

	mediaMetadata()->artist = metadata("ARTIST");
	mediaMetadata()->album = metadata("ALBUM");
	mediaMetadata()->genre = metadata("GENRE");
	
	QString year = metadata("DATE");
	if (!year.isNull() && !year.isEmpty())
		mediaMetadata()->year = year.toInt();

	// TODO: compilation info ZERO for now...
	mediaMetadata()->compilation = 0;
	
	mediaMetadata()->updatePlaytime();
}

QString Media::metadata(const QCString& key, int pos) const
{
	MediaMetadataExt * mediametadata = mediaMetadataExt();

	MetadataMap::ConstIterator it = mediametadata->map.find(key);

	if (it == mediametadata->map.end())
		return QString::null;

	if (pos < 0 || pos >= it.data().count())
		return it.data().last();

	return *(it.data().at(pos));
}

QStringList Media::metadataStringList() const
{
	QStringList list;
	MediaMetadataExt * mediametadata = mediaMetadataExt();
	
	for (MetadataMap::ConstIterator it = mediametadata->map.begin(); it != mediametadata->map.end(); ++it)
		for (MetadataEntry::ConstIterator it2 = it.data().begin(); it2 != it.data().end(); ++it2)
			list << QString("%1 = %2").arg(it.key()).arg((*it2).latin1());

	return list;
}

void Media::dumpInfo() const
{
	DENTERMETHOD("Media::dumpInfo()");
	
	DPRINTF("--------------------------------");
	DPRINTF("Media ID: <%d>", mediaid_);	
	DPRINTF("Location: <%s>", location().toString().latin1());
	DPRINTF("Last-Modified: <%s>", mediaFileInfo()->lastModified.toString().latin1());
	DPRINTF("Filesize: %d", mediaFileInfo()->fileSize);
	
	DPRINTF("Length: %d", mediaMetadata()->length);

	DPRINTF("Type: %s", (isAudio() ? "audio" : (isVideo() ? "video" : "no idea...")));
	
	DPRINTF("Track: %s", mediaMetadata()->trackNo.latin1());
	DPRINTF("Artist: %s", mediaMetadata()->artist.latin1());
	DPRINTF("Title: %s", mediaMetadata()->title.latin1());
	DPRINTF("Album: %s", mediaMetadata()->album.latin1());
	DPRINTF("Playtime: %s", mediaMetadata()->playTime.latin1());
	
	DPRINTF("--------------------------------");
		
	if (isVideo())
		DPRINTF("Video: Format=<%s> Bitrate=%d Size=%dx%d Fps=%f Aspect=%f ",
			   mediaVideo()->codec.latin1(), mediaVideo()->bitrate, mediaVideo()->width, mediaVideo()->height, mediaVideo()->FPS, mediaVideo()->aspectRatio);
			   
	if (isVideo() || isAudio())
		DPRINTF("Audio: Codec=<%s> Format=%d Bitrate=%d Nch=%d",
			   mediaAudio()->codec.latin1(), mediaAudio()->format, mediaAudio()->bitrate, mediaAudio()->channels);

	DPRINTF("Metadata:");
	for (MetadataMap::ConstIterator it = mediaMetadataExt()->map.begin(); it != mediaMetadataExt()->map.end(); ++it) {
		for (MetadataEntry::ConstIterator it2 = it.data().begin(); it2 != it.data().end(); ++it2)
			DPRINTF("  %s=%s", static_cast<const char *>(it.key()),
					static_cast<const char *>((*it2).local8Bit()));
	}
	DPRINTF("--------------------------------");
	
	DEXITMETHOD("Media::dumpInfo()");
}
