/***************************************************************************
 * copyright            : (c) 2004 Pierpaolo Di Panfilo                    *
 *                        (c) 2005-2006 Seb Ruiz <me@sebruiz.net>          *
 *                        (c) 2006 Bart Cerneels <bart.cerneels@gmail.com> *
 *                        (c) 2006 Adam Pigg <adam@piggz.co.uk>            *
 *                        (c) 2006 Bonne Eggleston <b.eggleston@gmail.com> *
 * See COPYING file for licensing information                              *
 ***************************************************************************/

#ifndef PLAYLISTBROWSERITEM_H
#define PLAYLISTBROWSERITEM_H

#include "dynamicmode.h"
#include "podcastbundle.h"
#include "podcastsettings.h"

#include <kdialogbase.h> // StreamEditor baseclass
#include <tdeio/job.h>
#include <klineedit.h>
#include <tdelistview.h>
#include <kurl.h>

#include <tqdom.h>
#include <tqfile.h>
#include <tqhttp.h>
#include <tqptrlist.h>
#include <tqtimer.h>     // Podcast loading animation
#include <tqurl.h>

class MetaBundle;
class PlaylistTrackItem;
class TrackItemInfo;

namespace TDEIO { class Job; class TransferJob; class CopyJob; } //podcast downloads

/**
 *  RTTI VALUES
 *  1000 - PlaylistCategory
 *  1001 - PlaylistEntry
 *  1002 - PlaylistTrackItem
 *  1003 - StreamEntry
 *  1004 - SmartPlaylist
 *  1005 - DynamicEntry (Dynamic)
 *  1006 - PodcastChannel
 *  1007 - PodcastEpisode
 */


/* A base class to be able to use polymorphism and avoid tons of casts */
class PlaylistBrowserEntry :  public TQObject, public TDEListViewItem
{
    TQ_OBJECT
  
    public:
        PlaylistBrowserEntry( TQListViewItem *parent, TQListViewItem *after )
            : TDEListViewItem( parent, after) { m_kept = true; }
        PlaylistBrowserEntry( TQListView *parent, TQListViewItem *after )
            : TDEListViewItem( parent, after) { m_kept = true; }
        PlaylistBrowserEntry( TQListViewItem *parent, TQListViewItem *after, const TQString &name )
            : TDEListViewItem( parent, after, name) { m_kept = true; }

        virtual TQDomElement xml() const { return TQDomElement(); }
        TQListViewItem* parent() const { return TDEListViewItem::parent(); }

        bool isKept() const { return m_kept; }  // if kept == true, then it will be saved
        void setKept( bool k );                 // to the cache files. If false, non-renameable

        virtual void updateInfo();
        virtual void setDynamic( bool ) {};

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotRenameItem();
        virtual void slotPostRenameItem( const TQString newName );
        virtual void showContextMenu( const TQPoint & ) {};

    protected:
        virtual int compare( TQListViewItem*, int, bool ) const; //reimplemented

        /** Interval of the download pixmap animation, in milliseconds */
        static const int ANIMATION_INTERVAL = 250;

    private:
        bool    m_kept;
};

class DynamicEntry : public PlaylistBrowserEntry, public DynamicMode
{
    TQ_OBJECT
  
    public:
        DynamicEntry( TQListViewItem *parent, TQListViewItem *after, const TQString &title );
        DynamicEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
        ~DynamicEntry() { };

        virtual TQString text( int column ) const;

        virtual TQDomElement xml() const;

        static const int RTTI = 1005;
        int rtti() const { return RTTI; }

    public slots:
        virtual void slotDoubleClicked();
        virtual void showContextMenu( const TQPoint & );
};

class PlaylistCategory : public PlaylistBrowserEntry
{
    TQ_OBJECT
  
    public:
        PlaylistCategory( TQListView *parent, TQListViewItem *after, const TQString &, bool isFolder=false );
        PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQString &, bool isFolder=true );
        PlaylistCategory( TQListView *parent, TQListViewItem *after, const TQDomElement &xmlDefinition, bool isFolder=false);
        PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
        PlaylistCategory( PlaylistCategory *parent, TQListViewItem *after, const TQString &t, const int id );

        ~PlaylistCategory() { };

        const TQString &title() const { return m_title; }
        bool  isFolder() { return m_folder; }

        void  paintCell( TQPainter*, const TQColorGroup&, int, int, int );

        void  setId( const int id ) { m_id = id; }
        const int id() const { return m_id; }

        virtual TQDomElement xml() const;

        int   rtti() const { return RTTI; }
        static const int RTTI = 1000;    //category item

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotRenameItem();
        virtual void showContextMenu( const TQPoint & );

    protected:
        void  okRename( int col );

    private:

        void setXml( const TQDomElement &xml );

        TQString m_title;
        int     m_id;
        bool    m_folder;
};


class PlaylistEntry :  public PlaylistBrowserEntry
{
    TQ_OBJECT
  

    friend class PlaylistTrackItem;
    friend class TrackItemInfo;
    friend class PlaylistCategory;

    public:
        PlaylistEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &, int tracks=0, int length=0 );
        PlaylistEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
        ~PlaylistEntry();

        void sortChildItems ( int /*column*/, bool /*ascending*/ ) { /* Don't sort its children */ }; //reimplemented

        void load();

        const KURL &url()                      const { return m_url; }
        void        setUrl( const TQString &u )       { m_url.setPath( u ); }
        int         trackCount()               const { return m_trackCount; }
        int         length()                   const { return m_length; }
        bool        isDynamic()                const { return m_dynamic; }
        bool        isLoaded()                 const { return m_loaded; }

        void        setDynamic( bool );

        int         compare( TQListViewItem* i, int col ) const; //reimpl.
        KURL::List  tracksURL();    //returns the list of tracks url
        void        insertTracks( TQListViewItem *after, KURL::List list );
        void        insertTracks( TQListViewItem *after, TQValueList<MetaBundle> bundles );
        // isLast is used to avoid saving the playlist to disk every time a track is removed
        // when removing a list of tracks from the playlist
        void        removeTrack( TQListViewItem *item, bool isLast = true );

        //returns a list of track information
        TQPtrList<TrackItemInfo> trackList()     const { return m_trackList; }
        TQPtrList<TrackItemInfo> droppedTracks() const { return tmp_droppedTracks; }

        void  setOpen( bool );
        void  setup();
        void  paintCell( TQPainter*, const TQColorGroup&, int, int, int );

        virtual TQDomElement xml() const;

        virtual void  updateInfo();

        //rtti is used to distinguish different kinds of list view items
        int   rtti() const { return RTTI; }
        static const int RTTI = 1001;    //playlist item

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotPostRenameItem( const TQString newName );
        virtual void showContextMenu( const TQPoint & );

    signals:
        void startingLoading();
        void loaded();

    private slots:
        void slotAnimation();

    private:
        void customEvent( TQCustomEvent* e );
        void startAnimation();
        void stopAnimation();

        KURL                 m_url;                 //playlist url
        int                  m_length;              //total length in seconds
        int                  m_trackCount;          //track counter
        TQPtrList<TrackItemInfo> m_trackList;        //tracks in playlist
        TQPtrList<TrackItemInfo> tmp_droppedTracks;  //tracks dropped to the playlist while it wasn't been loaded
        bool                 m_loading;
        bool                 m_loaded;              //playlist loaded
        bool                 m_dynamic;             //the playlist is scheduled for dynamic mode rotation
        TQPixmap             *m_loading1, *m_loading2;    //icons for loading animation
        TQTimer               m_animationTimer;
        uint                 m_iconCounter;
        PlaylistTrackItem   *m_lastTrack;
};

class PlaylistTrackItem : public PlaylistBrowserEntry
{
    TQ_OBJECT
  
    friend class TrackItemInfo;

    public:
        PlaylistTrackItem( TQListViewItem *parent, TQListViewItem *after, TrackItemInfo *info );

        const KURL    &url();
        TrackItemInfo *trackInfo() const { return m_trackInfo; }

        int rtti() const { return RTTI; }
        static const int RTTI = 1002;    //track item

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotRenameItem() { /* Do nothing */ };
        virtual void showContextMenu( const TQPoint & );

    private:
        TrackItemInfo *m_trackInfo;
};

/// Stored in the database
class PodcastEpisode : public PlaylistBrowserEntry
{
        TQ_OBJECT
  

    public:
        PodcastEpisode( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xml,
                        const int feedType, const bool &isNew=false );
        PodcastEpisode( TQListViewItem *parent, TQListViewItem *after, PodcastEpisodeBundle &bundle );

        void  downloadMedia();
        void setOnDisk( bool d = true );
        TQListViewItem *itemChannel() { return m_parent; }


        const bool isNew() const { return m_bundle.isNew(); }

        void setNew( const bool &n = true );
        void setListened( const bool &n = true ) { setNew( !n ); }

        // for convenience
        const int     dBId()        const { return m_bundle.dBId(); }
        const KURL    url()         const { return m_bundle.url(); }
        const TQString title()       const { return m_bundle.title(); }
        const TQString author()      const { return m_bundle.author(); }
        const TQString date()        const { return m_bundle.date(); }
        const TQString type()        const { return m_bundle.type(); }
        const TQString description() const { return m_bundle.description(); }
        const TQString guid()        const { return m_bundle.guid(); }
        const int     duration()    const { return m_bundle.duration(); }
        const KURL   &localUrl()    const { return m_bundle.localUrl(); }
        void  setLocalUrlBase( const TQString &s );
        void setLocalUrl( const KURL &localUrl );

        void  setup();
        void  paintCell( TQPainter*, const TQColorGroup&, int, int, int );

        virtual void  updateInfo();

        void addToMediaDevice();

        int    rtti() const { return RTTI; }
        static const int RTTI = 1007;              //PodcastEpisode
        static void createLocalDir( const KURL &localDir );

    signals:
        void downloadFinished();
        void downloadAborted();

    public slots:
        const bool isOnDisk();
        virtual void slotDoubleClicked();
        virtual void slotRenameItem() { /* Do nothing */ };
        virtual void showContextMenu( const TQPoint & );

    private slots:
        void abortDownload();
        void downloadResult( TDEIO::Job * transferJob );
        void slotAnimation();
        void redirected( TDEIO::Job * job,const KURL & redirectedUrl );

    private:
        enum FeedType{ RSS=0, ATOM=1 };

        virtual int compare( TQListViewItem*, int, bool ) const; //reimplemented

        void associateWithLocalFile();

        void startAnimation();
        void stopAnimation();
        void updatePixmap();

        TQListViewItem *m_parent;           //podcast channel it belongs to
        PodcastEpisodeBundle m_bundle;
        KURL        m_localUrl;

        bool        m_fetching;
        TQTimer      m_animationTimer;
        uint        m_iconCounter;

        TDEIO::StoredTransferJob* m_podcastEpisodeJob;
        TQString m_filename;

        bool        m_downloaded;       //marked as downloaded in cached xml
        bool        m_onDisk;
};

/// Stored in the database
class PodcastChannel : public PlaylistBrowserEntry
{
        TQ_OBJECT
  

    public:
        PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url,
                        const TQDomNode &channelSettings );
        PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url );
        PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const KURL &url,
                        const TQDomNode &channelSettings, const TQDomDocument &xml );
        PodcastChannel( TQListViewItem *parent, TQListViewItem *after, const PodcastChannelBundle &pcb );

        enum MediaFetch{ STREAM=0, AUTOMATIC=1 };

        void  setNew( const bool n = true );
        bool  hasNew() const { return m_new; }
        // iterate over all children and explicitly check if there are any episodes which have not been listened
        // to.  Mark the channel as new/listened after doing this.
        void  checkAndSetNew();

        void  setListened( const bool n = true ); // over rides each child so it has been listened

        void  setOpen( bool open ); // if !m_polished, load the children. Lazy loading to improve start times
        void  load();
        const bool isPolished() const { return m_polished; }

        void  configure();
        void  fetch();
        void  rescan();

        const KURL   &url()         const { return m_url; }
        const KURL    link()        const { return m_bundle.link(); }
        const TQString title()       const { return m_bundle.title(); }
        const TQString description() const { return m_bundle.description(); }
        const TQString copyright()   const { return m_bundle.copyright(); }

        const bool autoscan()     const { return m_bundle.autoscan(); }
        const bool autotransfer() const { return m_bundle.autotransfer(); }
        const int  fetchType()    const { return m_bundle.fetchType(); }
        const bool hasPurge()     const { return m_bundle.hasPurge(); }
        const int  purgeCount()   const { return m_bundle.purgeCount(); }
        const TQString &saveLocation() const { return m_bundle.saveLocation(); }
        PodcastSettings *getSettings() const
        {
            return new PodcastSettings( title(), saveLocation(),
                                        autoscan(), fetchType(), autotransfer(),
                                        hasPurge(), purgeCount() );
        }

        void setParent( PlaylistCategory *newParent );
        void setSettings( PodcastSettings *settings );
        void setXml( const TQDomNode &xml, const int feedType );

        virtual void  updateInfo();

        int rtti() const { return RTTI; }
        static const int RTTI = 1006;              //podcastchannel

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotRenameItem() { /* Do nothing */ };
        virtual void showContextMenu( const TQPoint & );

    private slots:
        void abortFetch();
        void downloadChildQueue();
        void fetchResult( TDEIO::Job* job );
        void slotAnimation();

    private:
        enum FeedType{ RSS=0, ATOM=1 };
        static const int EPISODE_LIMIT = 10; //Maximum number of episodes initially shown

        bool containsItem( TQDomElement xml );
        void downloadChildren();
        const bool episodeExists( const TQDomNode &xml, const int feedType );
        void purge();
        void restorePurged();
        void removeChildren();
        void setDOMSettings( const TQDomNode &channelSettings );
        void startAnimation();
        void stopAnimation();

        PodcastChannelBundle     m_bundle;

        /// loading all of the podcast episodes during startup can be very inefficient.
        /// When the user expands the podcast for the first time, we load up the episodes.
        bool                     m_polished;

        KURL                     m_url;            //remote xml url
        bool                     m_fetching;
        bool                     m_updating;
        TQTimer                   m_animationTimer;
        uint                     m_iconCounter;
        bool                     m_new;
        bool                     m_hasProblem;

        TDEIO::TransferJob        *m_podcastJob;
        PlaylistCategory        *m_parent;        // category it belongs to
        TQString                  m_podcastCurrentUrl;
        TQPtrList<PodcastEpisode> m_podcastDownloadQueue;
        bool                     m_settingsValid;
};

class StreamEntry : public PlaylistBrowserEntry
{
    TQ_OBJECT
  
    public:
        StreamEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &, const TQString &t );
        StreamEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );
        ~StreamEntry() { };

        void  setURL  ( KURL u )    { m_url = u; }
        void  setTitle( TQString t ) { m_title = t; }

        void  setup();
        void  paintCell( TQPainter*, const TQColorGroup&, int, int, int );

        const KURL    &url()   const { return m_url; }
        const TQString &title() const { return m_title; }

        virtual TQDomElement xml() const;

        virtual void updateInfo();

        int    rtti() const { return RTTI; }
        static const int RTTI = 1003;    //stream item

    public slots:
        virtual void slotDoubleClicked();
        virtual void showContextMenu( const TQPoint & );

    protected:
        TQString m_title;
        KURL    m_url;
};

class LastFmEntry : public StreamEntry
{
    TQ_OBJECT
  
    public:
        LastFmEntry( TQListViewItem *parent, TQListViewItem *after, const KURL &u, const TQString &t )
            : StreamEntry( parent, after, u, t ) { }
        LastFmEntry( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition )
            : StreamEntry( parent, after, xmlDefinition ) { }
        virtual TQDomElement xml() const;

    public slots:
        virtual void slotRenameItem() { /* Do nothing */ }

    public:
        int    rtti() const { return RTTI; }
        static const int RTTI = 1008;    //lastfm item
};

class StreamEditor : public KDialogBase
{
    public:
        StreamEditor( TQWidget *parent, const TQString &title, const TQString &url, bool readonly = false );

        KURL    url()  const { return KURL( m_urlLineEdit->text() ); }
        TQString name() const { return m_nameLineEdit->text().replace( "\n", " " ); }

    private:
        KLineEdit *m_urlLineEdit;
        KLineEdit *m_nameLineEdit;

};

class SmartPlaylist : public PlaylistBrowserEntry
{
    TQ_OBJECT
  
    public:
        SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQString &name, const TQString &query );
        SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQString &name,
                         const TQString &urls, const TQString &tags );
        SmartPlaylist( TQListViewItem *parent, TQListViewItem *after, const TQDomElement &xmlDefinition );

        bool        isDynamic()     const { return m_dynamic; }
        bool        isEditable()    const { return !m_xml.isNull(); }
        bool        isTimeOrdered(); //returns yes if the ordering is based on a time attribute
        TQString     query();
        TQString     title()         const { return m_title; }
        virtual TQDomElement xml()   const { return m_xml;   }

        int   length();
        void  setDynamic( bool );
        void  setXml( const TQDomElement &xml );

        int   rtti() const { return RTTI; }
        static const int RTTI = 1004;    //smart playlist item

    public slots:
        virtual void slotDoubleClicked();
        virtual void slotPostRenameItem( const TQString newName );
        virtual void showContextMenu( const TQPoint & );

    private:
        // for xml playlists, this member is computed on demand
        TQString         m_sqlForTags;

        TQString         m_title;
        TQDomElement     m_xml;
        TQListViewItem  *m_after;
        bool            m_dynamic;

        // Build the query for a given xml object. If \p for expand is true,
        // insert (*ExpandString*) as placeholders for childrens' filters
        static TQString xmlToQuery( const TQDomElement &xml, bool forExpand = false );
};

//this class is used to store information of a playlist track
class TrackItemInfo
{
    public:
        TrackItemInfo( const MetaBundle &mb );
        ~TrackItemInfo() {}
        const KURL    &url()    const { return m_url;    }
        const TQString &album()  const { return m_album; }
        const TQString &artist() const { return m_artist;  }
        const TQString &title()  const { return m_title;  }
        const int     length()  const { return m_length; }

    private:
        KURL    m_url;
        TQString m_artist;
        TQString m_album;
        TQString m_title;
        int     m_length;
};

/*!
    @brief Implement a shoutcast playlist category

    On open, download the shoutcast genre XML file.

    Process the file and add each genre as a ShoutcastGenre
    style PlaylistCategory
*/
class ShoutcastBrowser : public PlaylistCategory
{
        TQ_OBJECT
  
    public:
        ShoutcastBrowser( PlaylistCategory* parent );
        void setOpen( bool open );

    public slots:
        virtual void slotDoubleClicked();

    private slots:
        void doneGenreDownload( TDEIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed );
        void jobFinished( TDEIO::Job *job );
        void slotAnimation();

    private:
        bool          m_downloading;
        TDEIO::CopyJob *m_cj;
        TQPixmap      *m_loading1, *m_loading2;    //icons for loading animation
        TQTimer        m_animationTimer;
};

/*!
    @brief Implement a shoutcast genre category

    On open, download the shoutcast station list XML file.

    Process the file and add each station as a StreamEntry
*/
class ShoutcastGenre : public PlaylistCategory
{
        TQ_OBJECT
  
    public:
        ShoutcastGenre( ShoutcastBrowser *browser, TQListViewItem *after, TQString genre );
        void setOpen( bool open );
        void appendAlternateGenre( TQString alternateGenre ) { m_alternateGenres << alternateGenre; }

    public slots:
        virtual void slotDoubleClicked();

    private slots:
        void doneListDownload( TDEIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed );
        void jobFinished( TDEIO::Job *job );
        void slotAnimation();

    private:
        void          startGenreDownload( TQString genre, TQString tmppath );
        bool          m_downloading;
        TQString       m_genre;
        TQPixmap      *m_loading1, *m_loading2;    //icons for loading animation
        TQTimer        m_animationTimer;
        TQStringList   m_alternateGenres;
        TQStringList   m_stations;
        int           m_totalJobs;
        int           m_completedJobs;
};

#endif
