/***************************************************************************
 * copyright            : (C) 2006 Chris Muehlhaeuser <chris@chris.de>     *
 *                      : (C) 2006 Seb Ruiz <me@sebruiz.net>               *
 *                      : (C) 2006 Ian Monroe <ian@monroe.nu>              *
 *                      : (C) 2006 Mark Kretschmann <markey@web.de>        *
 **************************************************************************/

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

#ifndef AMAROK_LASTFM_H
#define AMAROK_LASTFM_H

#include "metabundle.h"

#include <tqhttp.h>
#include <tqobject.h>
#include <tqserversocket.h>
#include <tqurl.h>
#include <tqvaluelist.h>

#include <tdeconfigdialog.h>

class KLineEdit;
class TDEAction;
class KProcIO;
class KURL;
class TQSocket;
class TQTimer;

namespace TDEIO { class Job; }

/* AmarokHttp is a hack written so that lastfm code could easily use something proxy aware.
   DO NOT use this class for anything else, use TDEIO directly instead. */
class AmarokHttp : public TQObject
{
    TQ_OBJECT
  

    public:
    AmarokHttp ( const TQString & hostname, TQ_UINT16 port = 80, TQObject* parent = 0 );
    int get ( const TQString & path );
    TQHttp::State state() const;
    TQByteArray readAll ();
    TQHttp::Error error();

    signals:
    void requestFinished ( int id, bool error );

    protected slots:
    void slotData(TDEIO::Job*, const TQByteArray& );
    void slotResult(TDEIO::Job*);

    protected:
    TQString m_hostname;
    TQ_UINT16 m_port;
    TQString  m_path;
    TQHttp::State m_state;
    TQHttp::Error m_error;
    bool m_done;
    TQByteArray m_result;
};


namespace LastFm
{
    class WebService;

    class Controller : public TQObject
    {
        TQ_OBJECT
  

        public:
            static Controller* instance();

            KURL        getNewProxy( TQString genreUrl, bool useProxy );
            int         changeStation ( TQString url );

            bool        isPlaying() const { return m_service != 0; }
            WebService* getService() const { return m_service; }
            TQString     getGenreUrl() const { return m_genreUrl; }

            static bool    checkCredentials();
            static TQString createCustomStation();
            static TQString stationDescription( TQString url = TQString() );   // necessary for translation

        public slots:
            void playbackStopped();
            void ban();
            void love();
            void skip();

        private:
            Controller();
            void setActionsEnabled( bool enable );

            static Controller *s_instance;
            TQPtrList<TDEAction> m_actionList;

            TQString     m_genreUrl;
            WebService* m_service;
    };

    class WebService : public TQObject
    {
        TQ_OBJECT
  

        public:
            enum DataType { Artist, Album, Track };

            WebService( TQObject* parent, bool useProxy );
            ~WebService();

            bool handshake( const TQString& username, const TQString& password );

            bool    changeStation( TQString url );
            TQString currentUsername() const { return m_username;  }
            TQString currentPassword() const { return m_password;  }
            TQString currentStation()  const { return m_station;   }
            TQString session()         const { return m_session;   }
            TQUrl    streamUrl()       const { return m_streamUrl; }

            void friends( TQString username );
            void neighbours( TQString username );

            void recentTracks( TQString username );
            void userTags( TQString username );

            void recommend( int type, TQString username, TQString artist, TQString token = TQString() );

            void recommendArtist( TQString username, TQString artist )
            {    recommend( WebService::Artist, username, artist ); }

            void recommendAlbum( TQString username, TQString artist, TQString album )
            {    recommend( WebService::Album, username, artist, album ); }

            void recommendTrack( TQString username, TQString artist, TQString track )
            {    recommend( WebService::Track, username, artist, track ); }

            /**
                Verify with server that a supplied user/pass combo is valid. Password
                should be MD5 hashed.
            **/
            void verifyUser( const TQString& user, const TQString& pass );

            bool cancel();
            bool wasCanceled() const { return m_wasCanceled; }

            TQString proxyUrl() { return m_proxyUrl; }

        public slots:
            void requestMetaData();
            void enableScrobbling( bool enabled );

            void love();
            void skip();
            void ban();

        signals:
            void actionStarted();
            void actionFinished();

            void stationChanged( TQString url, TQString name );
            void songQueued();

            void metaDataResult( const MetaBundle &bundle );
            void enableScrobblingDone();

            void loveDone();
            void skipDone();
            void banDone();

            void friendsResult( const TQString& username, const TQStringList& friends );
            void neighboursResult( const TQString& username, const TQStringList& friends );

            void recentTracksResult( const TQString& username, TQValueList< TQPair<TQString, TQString> > songs );
            void userTagsResult( const TQString& username, const TQStringList& tags );

        private:
            enum errorCode { E_NOCONTENT    = 1, E_NOMEMBERS = 2, E_NOFANS = 3, E_NOAVAIL = 4, E_NOSUBSCRIBER = 5,
                             E_NONEIGHBOURS = 6, E_NOSTOPPED = 7, E_OTHER  = 0 };

            void        showError( int code, TQString message = TQString() );

            bool m_useProxy;

            TQString     parameter( const TQString keyName, const TQString data )      const;
            TQStringList parameterArray( const TQString keyName, const TQString data ) const;
            TQStringList parameterKeys( const TQString keyName, const TQString data )  const;

            TQString m_username;     // login username
            TQString m_password;     // login password
            TQString m_station;      // the url of the station
            TQString m_session;      // session id that last.fm provides
            TQString m_baseHost;     // who are we connecting to?
            TQString m_basePath;     // where are we connecting to!
            TQUrl    m_streamUrl;    // last.fm webserver for direct connection (proxy connects to this)
            bool    m_subscriber;   // self explanatory

            KProcIO* m_server;

            TQString    m_proxyUrl;
            MetaBundle m_metaBundle;

            bool m_deletionUnsafe;
            bool m_wasCanceled;

        private slots:
            void readProxy();
            void metaDataFinished( int id, bool error );
            void fetchImageFinished( TDEIO::Job* );
            void enableScrobblingFinished( int id, bool error );

            void loveFinished( int id, bool error );
            void skipFinished( int id, bool error );
            void banFinished( int id, bool error );

            void friendsFinished( int id, bool error );
            void neighboursFinished( int id, bool error );

            void recentTracksFinished( int id, bool error );
            void userTagsFinished( int id, bool error );

            void recommendFinished( int id, bool error );
    };

    class Bundle
    {
        public:
            Bundle() {};
            Bundle( const Bundle& bundle);
            TQString imageUrl() const { return m_imageUrl; }
            void setImageUrl( const TQString& imageUrl ) { m_imageUrl = imageUrl; }

            TQString artistUrl() const {  return m_artistUrl; }
            void setArtistUrl( const TQString& theValue )  {  m_artistUrl = theValue; }

            TQString albumUrl() const {  return m_albumUrl; }
            void setAlbumUrl( const TQString& theValue )  {  m_albumUrl = theValue; }

            TQString titleUrl() const {  return m_titleUrl; }
            void setTitleUrl( const TQString& theValue )  {  m_titleUrl = theValue; }

            void detach(); // for being able to apply TQDeepCopy<>

        private:
            TQString m_imageUrl;
            TQString m_albumUrl;
            TQString m_artistUrl;
            TQString m_titleUrl;
    };

    // We must implement this because TQServerSocket has one pure virtual method.
    // It's just used for finding a free port.
    class MyServerSocket : public TQServerSocket
    {
        public:
            MyServerSocket() : TQServerSocket( TQ_UINT16( 0 ) ) {}

        private:
            void newConnection( int ) {}

    };

    class LoginDialog : public KDialogBase
    {
        TQ_OBJECT
  

        public:
            LoginDialog( TQWidget *parent );

        protected slots:
            void slotOk();

        private:
            KLineEdit *m_userLineEdit;
            KLineEdit *m_passLineEdit;

    };

    class CustomStationDialog : public KDialogBase
    {
        TQ_OBJECT
  

        public:
            CustomStationDialog( TQWidget *parent );

            TQString text() const;

        private:
            KLineEdit *m_edit;
    };
}

#endif /*AMAROK_LASTFM_H*/
