/*
 * 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.
 */

#ifndef MEDIADATABASE_H_INCLUDED
#define MEDIADATABASE_H_INCLUDED

#include <qobject.h>
#include <qstring.h>
#include <qdict.h>
#include <qurl.h>
#include <qintcache.h>
#include <qintdict.h>
#include "sqlite_source/sqlite3.h"
#include "media.h"

class QDateTime;

const QString dbSafeString(const QString &in, const bool escapePercentage = false);
const QString dbFormatDate(const QDateTime &in);
uint fastHashString(const QString &str);
uint elfHashString(const QString &str);

class MediaDatabase : public QObject
{
	Q_OBJECT
	
public:
	MediaDatabase(const QString &mediaDBFilename);
	~MediaDatabase();

	// accesor
	inline Media *media(const QString& url) { return media(MediaLocation(url)); }
	Media *media(const MediaLocation& location);
	inline Media *media(unsigned long mediaID) 
	{  
		Media *media = mediaids_[mediaID];
		if (!media) 
			media = locateMedia(mediaID);
		return media;
	}
	
	bool hasChanged() { return hasChanged_; }

	void beginUpdate();
	void endUpdate();
	
	void clearCache()
	{
		mediaids_.clear();
		cache_.clear();
	}
	
	void ensurePreparedStatementsAreValid();
	
	// db management
	void save(QWidget* guiWidget = 0);
	void load(QWidget* guiWidget = 0);
	
	sqlite3 *db() { return db_; };

	void dbDebug(const QString &msg = "");
	
signals:
	void databaseCompacted();
	void preparingStatements(sqlite3 *db);
	void finalizingStatements(sqlite3 *db);
	
private:
	friend class PlayList;
	friend class Media;
	friend class MediaAudio;
	friend class MediaVideo;
	friend class MediaMetadataExt;	
	friend class MediaMetadata;
	friend class MediaFileInfo;

	QString dbFilename_;
	QDict<Media> cache_;
	QIntDict<Media> mediaids_;
	bool hasChanged_;
	bool transactionOpen_;
	int updateCount_;
	
	MediaVideoCache *cacheMediaVideo_;
	MediaAudioCache *cacheMediaAudio_;
	MediaMetadataExtCache *cacheMediaMetadataExt_;
	MediaMetadataCache *cacheMediaMetadata_;
	MediaFileInfoCache *cacheMediaFileInfo_;

	sqlite3 * db_;
	QString lastErrorMessage;
		
	sqlite3_stmt *stmtSelectMediaIDByMediaID;
	sqlite3_stmt *stmtSelectMediaIDByHash;
	sqlite3_stmt *stmtSelectMediaIDByFilename;
	sqlite3_stmt *stmtSelectFilenameByMediaID;
		
	sqlite3_stmt *stmtSelectMediaMetadataByMediaID;
	sqlite3_stmt *stmtSelectMediaFileInfoByMediaID;
	sqlite3_stmt *stmtSelectMediaAudioByMediaID;
	sqlite3_stmt *stmtSelectMediaVideoByMediaID;
	sqlite3_stmt *stmtSelectMediaMetadataExtByMediaID;
	
	sqlite3_stmt *stmtDeleteMediaByMediaID;
	sqlite3_stmt *stmtDeleteMediaMetadataExtByMediaID;
	
	sqlite3_stmt *stmtInsertMedia;
	sqlite3_stmt *stmtInsertMediaMetaData;
	sqlite3_stmt *stmtInsertMediaFileInfo;
	sqlite3_stmt *stmtInsertMediaAudio;
	sqlite3_stmt *stmtInsertMediaVideo;
	sqlite3_stmt *stmtInsertMediaMetadataExt;
	
	//statements for loadMediaHeuristically:
	sqlite3_stmt *stmtSelectMediaIDLocationIDByFilenameFilesizeLastModified;
	sqlite3_stmt *stmtSelectMediaIDByLocationID;		
	sqlite3_stmt *stmtUpdateMediaHashByMediaID;
	sqlite3_stmt *stmtUpdateMediaLocationByLocationID;
	
	sqlite3_stmt *stmtInsertMediaLocation;
	sqlite3_stmt *stmtSelectLocationIDByLocation;

	bool isDBOpen() { return db_ != 0; };
	bool openDB(const QString & db);
	bool createDB(const QString & db);
	void prepareStatements();
	void finalizeStatements();
	void saveDB();
	void closeDB();
	void compactDB();
	
	QString getDBSchema();
		
	//int getMediaIDbyFilename(const QString &filename);
	QString getFilenameByMediaID(const int mediaID);

	bool deleteMediaByID(const int mediaID);
	
	Media *locateMedia(unsigned long mediaID);
	
	bool loadMediaHeuristically(const MediaLocation& location, Media *media);
	
	int lastInsertID() { return sqlite3_last_insert_rowid(db_); }
};

#endif // MEDIADATABASE_H_INCLUDED
