// (c) 2004 Mark Kretschmann <markey@web.de>
// (c) 2004 Christian Muehlhaeuser <chris@chris.de>
// (c) 2004 Sami Nieminen <sami.nieminen@iki.fi>
// See COPYING file for licensing information.

#ifndef AMAROK_COLLECTIONDB_H
#define AMAROK_COLLECTIONDB_H

#include "engineobserver.h"
#include "dbenginebase.h"
#include <kurl.h>
#include <tqdir.h>            //stack allocated
#include <tqobject.h>         //baseclass
#include <tqptrqueue.h>       //baseclass
#include <tqsemaphore.h>      //stack allocated
#include <tqstringlist.h>     //stack allocated

class CoverFetcher;
class MetaBundle;
class Scrobbler;


class DbConnectionPool : TQPtrQueue<DbConnection>
{
    public:
        DbConnectionPool();
       ~DbConnectionPool();

        const DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; }
        const DbConfig *getDbConfig() const { return m_dbConfig; }
        void createDbConnections();

        DbConnection *getDbConnection();
        void putDbConnection( const DbConnection* /* conn */ );

    private:
        static const int POOL_SIZE = 5;
        TQSemaphore m_semaphore;
        DbConnection::DbConnectionType m_dbConnType;
        DbConfig *m_dbConfig;
};


class CollectionDB : public TQObject, public EngineObserver
{
    TQ_OBJECT
  

    friend class SimilarArtistsInsertionJob;

    signals:
        void scanStarted();
        void scanDone( bool changed );
        void databaseEngineChanged();

        void scoreChanged( const TQString &url, int score );

        void coverFetched( const TQString &artist, const TQString &album );
        void coverRemoved( const TQString &artist, const TQString &album );
        void coverFetcherError( const TQString &error );

        void similarArtistsFetched( const TQString &artist );

    public:
        static CollectionDB *instance();

        TQString escapeString( TQString string ) { return m_dbConnPool->escapeString(string); }
        int getType() { return m_dbConnPool->getDbConnectionType(); }


        /**
         * This method returns a static DbConnection for components that want to use
         * the same connection for the whole time. Should not be used anywhere else
         * but in CollectionReader.
         *
         * @return static DbConnection
         */
        DbConnection *getStaticDbConnection();

        /**
         * Returns the DbConnection back to connection pool.
         *
         * @param conn DbConnection to be returned
         */
        void returnStaticDbConnection( DbConnection *conn );

        //sql helper methods
        TQStringList query( const TQString& statement, DbConnection *conn = NULL );
        int insert( const TQString& statement, const TQString& table, DbConnection *conn = NULL );

        //table management methods
        bool isEmpty();
        bool isValid();
        void createTables( DbConnection *conn = NULL );
        void dropTables( DbConnection *conn = NULL );
        void clearTables( DbConnection *conn = NULL );
        void moveTempTables( DbConnection *conn );

        uint artistID( TQString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL );
        uint albumID( TQString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL );
        uint genreID( TQString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL );
        uint yearID( TQString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL );

        bool isDirInCollection( TQString path );
        bool isFileInCollection( const TQString &url );
        void removeDirFromCollection( TQString path );
        void removeSongsInDir( TQString path );
        void removeSongs( const KURL::List& urls );
        void updateDirStats( TQString path, const long datetime, DbConnection *conn = NULL );

        //song methods
        bool addSong( MetaBundle* bundle, const bool incremental = false, DbConnection *conn = NULL );

        /**
         * The @p bundle parameter's url() will be looked up in the Collection
         * @param bundle this will be filled in with tags for you
         * @return true if in the collection
         */
        bool bundleForUrl( MetaBundle* bundle );
        TQValueList<MetaBundle> bundlesByUrls( const KURL::List& urls );
        void addAudioproperties( const MetaBundle& bundle );

        void updateTags( const TQString &url, const MetaBundle &bundle, const bool updateView = true );
        void updateURL( const TQString &url, const bool updateView = true );

        //statistics methods
        int addSongPercentage( const TQString &url, int percentage );
        int getSongPercentage( const TQString &url  );
        void setSongPercentage( const TQString &url , int percentage );

        //artist methods
        TQStringList similarArtists( const TQString &artist, uint count );

        //album methods
        void checkCompilations( const TQString &path, const bool temporary = false, DbConnection *conn = NULL );
        void setCompilation( const TQString &album, const bool enabled, const bool updateView = true );
        TQString albumSongCount( const TQString &artist_id, const TQString &album_id );
        bool albumIsCompilation( const TQString &album_id );

        //list methods
        TQStringList artistList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList albumList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList genreList( bool withUnknowns = true, bool withCompilations = true );
        TQStringList yearList( bool withUnknowns = true, bool withCompilations = true );

        TQStringList albumListOfArtist( const TQString &artist, bool withUnknown = true, bool withCompilations = true );
        TQStringList artistAlbumList( bool withUnknown = true, bool withCompilations = true );

        TQStringList albumTracks( const TQString &artist_id, const TQString &album_id );

        //cover management methods
        /** Returns the image from a given URL, network-transparently.
         * You must run TDEIO::NetAccess::removeTempFile( tmpFile ) when you are finished using the image;
         **/
        static TQImage fetchImage(const KURL& url, TQString &tmpFile);
        /** Saves images located on the user's filesystem */
        bool setAlbumImage( const TQString& artist, const TQString& album, const KURL& url );
        /** Saves images obtained from CoverFetcher */
        bool setAlbumImage( const TQString& artist, const TQString& album, TQImage img, const TQString& amazonUrl = TQString() );

        TQString findImageByMetabundle( MetaBundle trackInformation, const uint = 1 );
        TQString findImageByArtistAlbum( const TQString &artist, const TQString &album, const uint width = 1 );
        TQString albumImage( MetaBundle trackInformation, const uint width = 1 );
        TQString albumImage( const uint artist_id, const uint album_id, const uint width = 1 );
        TQString albumImage( const TQString &artist, const TQString &album, const uint width = 1 );

        bool removeAlbumImage( const uint artist_id, const uint album_id );
        bool removeAlbumImage( const TQString &artist, const TQString &album );

        //local cover methods
        void addImageToAlbum( const TQString& image, TQValueList< TQPair<TQString, TQString> > info, DbConnection *conn = NULL );
        TQString getImageForAlbum( const TQString& artist, const TQString& album, uint width = 0 );
        TQString notAvailCover( int width = 0 );

        void applySettings();

    protected:
        CollectionDB();
        ~CollectionDB();

        TQCString md5sum( const TQString& artist, const TQString& album, const TQString& file = TQString() );
        void engineTrackEnded( int finalPosition, int trackLength );
        /** Manages regular folder monitoring scan */
        void timerEvent( TQTimerEvent* e );

    public slots:
        void fetchCover( TQWidget* parent, const TQString& artist, const TQString& album, bool noedit );
        void scanMonitor();
        void startScan();
        void stopScan();

    private slots:
        void dirDirty( const TQString& path );
        void coverFetcherResult( CoverFetcher* );
        void similarArtistsFetched( const TQString& artist, const TQStringList& suggestions );

    private:
        //bump DATABASE_VERSION whenever changes to the table structure are made. will remove old db file.
        static const int DATABASE_VERSION = 18;
        static const int DATABASE_STATS_VERSION = 3;
        static const int MONITOR_INTERVAL = 60; //sec
        static const bool DEBUG = false;

        void initialize();
        void destroy();
        void customEvent( TQCustomEvent* );

        //general management methods
        void createStatsTable();
        void dropStatsTable();
        void scanModifiedDirs();

        TQCString makeWidthKey( uint width );
        TQString artistValue( uint id );
        TQString albumValue( uint id );
        TQString genreValue( uint id );
        TQString yearValue( uint id );

        uint IDFromValue( TQString name, TQString value, bool autocreate = true, const bool temporary = false,
                          const bool updateSpelling = false, DbConnection *conn = NULL );

        TQString valueFromID( TQString table, uint id );

        //member variables
        TQString m_amazonLicense;
        TQString m_cacheArtist;
        uint m_cacheArtistID;
        TQString m_cacheAlbum;
        uint m_cacheAlbumID;

        DBEngine* m_dbEngine;
        DbConnectionPool *m_dbConnPool;

        bool m_monitor;
        TQDir m_cacheDir;
        TQDir m_coverDir;
};


#endif /* AMAROK_COLLECTIONDB_H */
