/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available 
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Music Player Query Manager.
*
*/

#include "mpquerymanager.h"
#include <QDebug>
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QNetworkProxyFactory>
#include <qmobilityglobal.h>
#include <qnetworksession.h>
#include <QDomElement>
#include <QList>
#include <QFile>
#include <QUrl>
#include <QSslError>
#include <QDir>
#include <QCoreApplication>

#include <thumbnailmanager_qt.h>
#include <thumbnaildata.h>
#include <thumbnailobjectsource.h>

#include "mpdetailssharedialog.h"
#include "mptrace.h"

const int KUndefined = -1;
const int KRecommendationCount = 2;

MpQueryManager::MpQueryManager()
    : mManager(0),
      mDownloadManager(0),
      mThumbnailManager(0)        
                               
{
    TX_ENTRY

    QString privatePathQt( QCoreApplication::applicationDirPath() );
    TX_LOG_ARGS( "Private path: " << privatePathQt );
    QDir dir( privatePathQt );
    QString newDir = "detailsview";
    bool res = dir.mkdir( newDir );
    TX_LOG_ARGS( "New dir creation result: " << res);

    // We're not handling a negative result for directory creation here,
    // instead it will be escalated in DownloadFinished() method.
    privatePathQt = privatePathQt + "/detailsview";
    QString albumArt1( privatePathQt + "/albumOne.png" );
    QString albumArt2( privatePathQt + "/albumTwo.png" );
    mRecommendationAlbumArtsName << albumArt1 << albumArt2;
    TX_LOG_ARGS( "recommendation album art names: " << mRecommendationAlbumArtsName );

    mManager = new QNetworkAccessManager( this );
      
    mDownloadManager = new QNetworkAccessManager( this );
    connect( mDownloadManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( DownloadFinished( QNetworkReply * ) ) );
    
    mThumbnailManager = new ThumbnailManager( this );
    mThumbnailManager->setQualityPreference( ThumbnailManager::OptimizeForQuality );
    mThumbnailManager->setThumbnailSize( ThumbnailManager::ThumbnailSmall );
    QObject::connect( mThumbnailManager, SIGNAL( thumbnailReady( QPixmap , void * , int , int ) ),
            this, SLOT( thumbnailReady( QPixmap , void * , int , int  ) ) );
    // TODO: Use the album art in provided by 10.1 wk16 platform. Current one is in the binary        
    mDefaultRecommendationAlbumArt = QPixmap( ":/mpdetailsviewicons/qtg_large_music_album.svg" );    
     
    TX_EXIT
}

MpQueryManager::~MpQueryManager()
{
    TX_ENTRY
    if ( mManager ) {
       mManager->deleteLater();
     }
    if ( mDownloadManager ) {
       mDownloadManager->deleteLater();
    }
    delete mThumbnailManager;
    TX_EXIT
}


void MpQueryManager::clearNetworkReplies()
{
    disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
    TX_ENTRY_ARGS( "Reply count = " << mReplys.count() );    
    for ( int i = 0; i < mReplys.count(); i++ ) {
        QNetworkReply *reply = mReplys.at( i );
        disconnect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );        
        if ( reply != NULL ) {
            reply->close();
            reply->deleteLater();
            reply = NULL;
        }   
    }
    mReplys.clear();
    TX_EXIT
}


void MpQueryManager::queryLocalMusicStore(QString artist,QString album,QString title)
{
    TX_ENTRY
    mArtist=artist;
    mAlbum=album;
    mTitle=title;
    // TODO: country information handling, MCC
    QString queryURI("http://api.music.ovi.com/1.0/ru/?");
    constructRequest( queryURI );
    TX_LOG_ARGS( "queryURI : " << queryURI );
    retrieveInformation( queryURI );
    TX_EXIT    
}
    
void MpQueryManager::queryInspireMeItems(QString artist,QString album,QString title)
{
    TX_ENTRY    
    mArtist=artist;
    mAlbum=album;
    mTitle=title;
    // start querying inspire me items
    QString queryRecommendation("http://api.music.ovi.com/1.0/gb/releases/recommend/?");
    constructRequest( queryRecommendation );
    TX_LOG_ARGS( "queryRecommendation : " << queryRecommendation );
    retrieveInformation( queryRecommendation );
    TX_EXIT
}

void MpQueryManager::clearRecommendations()
{
    TX_ENTRY    
    mDownloadedAlbumArts = 0;
    mAlbumArtsReadyCount = 0;
    for ( int i = 0; i < KRecommendationCount; i++ ) {
       mRecommendationSongs.clear();
       mRecommendationArtists.clear();
       mRecommendationAlbumArtsLink.clear();
       mRecommendationAlbumArtsMap.clear();      
       QFile file( mRecommendationAlbumArtsName.at( i ) );        
       if ( file.exists() ) {
           if ( file.remove() ) {
               TX_LOG_ARGS( "File removed - " << file.fileName() );
           }
           else {
               TX_LOG_ARGS( "Cannot remove file - " << file.fileName() );
           }
       } else {
           TX_LOG_ARGS( "File doesn't exist - " << file.fileName() );
       }
    }
    TX_EXIT    
}

/*!
 Return recommendation songs
 */
QStringList MpQueryManager::recommendationSongs()
{
    TX_LOG  
    return mRecommendationSongs;
}

/*!
 Return recommendation artists
 */
QStringList MpQueryManager::recommendationArtists()
{
    TX_LOG  
    return mRecommendationArtists;
}

/*!
 Return recommendation album arts links
 */
QStringList MpQueryManager::recommendationAlbumArtsLink()
{
    TX_LOG  
    return mRecommendationAlbumArtsLink;
}

/*!
 Return map of name and pixmap
 */
QMap<QString, QPixmap>  MpQueryManager::recommendationAlbumArtsMap()
{
    TX_LOG  
    return mRecommendationAlbumArtsMap;
}

/*!
 Insert one uri & pixmap item into map
 */
void MpQueryManager::insertMapItem( const QString &uri, const QPixmap &pixmap )
{
    TX_ENTRY_ARGS( "Map Item URI: " << uri );
    mRecommendationAlbumArtsMap.insert( uri, pixmap );
    TX_EXIT
}

/*!
 Slot to call when getting response
 */
void MpQueryManager::retrieveInformationFinished( QNetworkReply* reply )
{
    TX_ENTRY
    QString errorStr;
    int errorLine;
    int errorColumn;
    bool parsingSuccess;
    
    if ( reply->error() == QNetworkReply::NoError )
    {
        parsingSuccess = mDomDocument.setContent( reply, true, &errorStr, &errorLine, &errorColumn );
        if ( parsingSuccess ) {
            handleParsedXML();  //CodeScanner throws a warning mis-interpreting the trailing 'L' to be a leaving function.
        } else {
            // TODO: agree on error handling            
            TX_LOG_ARGS( "XML parsing error" );
        }
    }
    else
    {
        // TODO: agree on error handling
        TX_LOG_ARGS( "Network error in retrieving Information" );
        emit networkError();
    }
    TX_EXIT
}

/*!
 Slot to call when there is network error
 */
void MpQueryManager::retrieveInformationNetworkError( QNetworkReply::NetworkError error )
{
    // TODO: agree on error handling
    TX_ENTRY_ARGS( "Network error for retrieving Information" << error);
    TX_EXIT
}

/*!
 Slot to call when there is ssl error
 */
void MpQueryManager::retrieveInformationSslErrors( const QList<QSslError> &/*error*/ )
{   
    // TODO: agree on error handling
    TX_ENTRY_ARGS( "SSL error for retrieving Information" );
    TX_EXIT
}

/*!
 Slot to call when downloading finished
 */
void MpQueryManager::DownloadFinished( QNetworkReply* reply )
{
    TX_ENTRY_ARGS( "mDownloadedAlbumArts = " << mDownloadedAlbumArts );
    if ( reply->error() == QNetworkReply::NoError )
        {
        QString fileName = mRecommendationAlbumArtsName.at( mDownloadedAlbumArts );
        QByteArray imageData = reply->readAll();
        bool ret = writeImageToFile( imageData, fileName );

        // If file writing went OK, emit a signal with the real filename
        // If it failed, use empty filename (since file was removed in any case)
        if ( ret )
            {
            setAlbumArtUri( mRecommendationAlbumArtsLink.at( mDownloadedAlbumArts), 
                            mRecommendationAlbumArtsName.at( mDownloadedAlbumArts ) );
            }
        else
            {
            setAlbumArtUri(mRecommendationAlbumArtsLink.at( mDownloadedAlbumArts), "");
            }
        }
    else
    {
        TX_LOG_ARGS( "Downloading album art failed!" );
        emit networkError();
    }
    
    mDownloadedAlbumArts++;
    TX_EXIT
}

/*!
 Write the image data to a file with the given filename.
 If writing operation fails for any reason (e.g. OOD),
 returns false, otherwise true.
 */
bool MpQueryManager::writeImageToFile(const QByteArray &aImageData, const QString &aImageFileName )
    {
    bool ret( false );
    TX_ENTRY_ARGS( "imagefile: " << aImageFileName );
    if ( aImageFileName.isEmpty() )
        {
        TX_LOG_ARGS( "Only store two album arts" );
        }
    else
        {
        QFile file( aImageFileName );

        if ( !file.open( QIODevice::ReadWrite ) )
            {
            TX_LOG_ARGS( "Unable to open file" );
            }
        else
            {
            qint64 writtenBytes = file.write( aImageData );

            // Verify file write status
            if ( writtenBytes < aImageData.size() )
                {
                // If write succeeded only partially, or completely failed,
                // remove the file from filesystem to remove risk of corruption
                TX_LOG_ARGS( "Wrote only " << writtenBytes << " bytes, aborting operation!" );
                file.close();
                QFile::remove( mRecommendationAlbumArtsName.at( mDownloadedAlbumArts ) );
                }
            else
                {
                // If write fully succeeded, flush contents
                TX_LOG_ARGS( "Wrote all the bytes (" << writtenBytes << "), flushing and closing!");
                file.flush();
                file.close();
                ret = true;
                }
            }
        }
    TX_LOG_ARGS( "Returning with value: " << ret );
    TX_EXIT
    return ret;
    }

/*!
 Get Atom response from Ovi server based on query
 */
void MpQueryManager::retrieveInformation( const QString &urlEncoded )
{
    TX_ENTRY_ARGS( "urlEconded = " << urlEncoded)
    connect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );    
    QNetworkReply *reply = mManager->get( QNetworkRequest( QUrl( urlEncoded ) ) );
    mReplys.append( reply );
    
    connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
    connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
    TX_EXIT
}


/*!
 Find the most suitable link based on Atom response from Ovi music server
 */
void MpQueryManager::handleParsedXML()
{
    TX_ENTRY
    QDomElement rootElement = mDomDocument.documentElement();
    
    if ( rootElement.attribute( "type" ) == "search" ) {
        TX_LOG_ARGS( "URI response" )
        QString result;
        QDomElement entry = rootElement.firstChildElement( "entry" );
        while ( !entry.isNull() )
        {
            if ( entry.attribute( "type" ) == "musictrack" ) {
                QDomElement link = entry.firstChildElement( "link" );
                while ( !link.isNull() )
                {
                    if ( link.attribute( "rel" ) == "alternate"
                        && link.attribute( "type" ) == "text/html" ) {
                        result = link.attribute( "href" );
                    }
                    link = link.nextSiblingElement( "link" );
                }
            }
            entry = entry.nextSiblingElement( "entry" );
        }
		// Signal that the url was received. Might be empty string.
		emit searchUrlRetrieved( result );
    } else if ( rootElement.attribute( "type" ) == "recommendedTracks" ) {
        TX_LOG_ARGS( "Recommendation response" )
        QDomElement entry = rootElement.firstChildElement( "entry" );
        QNetworkReply *reply;
        int count = 0;
        while ( !entry.isNull() && count < KRecommendationCount )
        {
            if ( entry.attribute( "type" ) == "musictrack" ) {
                QDomElement link = entry.firstChildElement( "link" );
                while ( !link.isNull() )
                {
                    if ( link.attribute( "title" ) == "albumart100" ) {
                        mRecommendationAlbumArtsLink.append( link.attribute( "href" ) );
                        break;
                    } else {
                        link = link.nextSiblingElement( "link" );
                    }                    
                }
                QDomElement metadata = entry.firstChildElement( "metadata" );
                mRecommendationSongs.append( metadata.firstChildElement( "name" ).text() );
                mRecommendationArtists.append( metadata.firstChildElement( "primaryartist" ).text() );
                count++;
            }
            entry = entry.nextSiblingElement( "entry" );
        }          
        
        for (int i = 0; i < KRecommendationCount; i++ ) {
            TX_LOG_ARGS( "song name: " << mRecommendationSongs.at(i) );
            TX_LOG_ARGS( "Artist name: " << mRecommendationArtists.at(i) );
            TX_LOG_ARGS( "Album art link: " << mRecommendationAlbumArtsLink.at(i) );
            
            if ( mRecommendationAlbumArtsLink.at( i ).contains( "http", Qt::CaseInsensitive ) ) {
                reply = mDownloadManager->get( QNetworkRequest( QUrl( mRecommendationAlbumArtsLink.at(i) ) ) );
                mReplys.append( reply );
                connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
                connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
            }             
        }
    } else {
        TX_LOG_ARGS( "Not supported response" )
    }
    TX_EXIT
}

/*!
 Construct the query for fetching URI & recommendations
 */
void MpQueryManager::constructRequest( QString &uri )
{
    TX_ENTRY_ARGS( "uri =" << uri)
    
    QStringList keys;
    keys << "artist" << "albumtitle" << "tracktitle" << "orderby";
    
    // TODO: need to clarify which crition to use for sort, currently hard code to "relevancy"
    // order can be relevancy, alltimedownloads, streetreleasedate, sortname, recentdownloads
    QStringList values;
    values << mArtist << mAlbum << mTitle << QString("relevancy");
    TX_LOG_ARGS( "Artist: " << mArtist ); 
    TX_LOG_ARGS( "Album: " << mAlbum );
    TX_LOG_ARGS( "Title: " << mTitle );
    
    uri += keyValues( keys, values );

    QUrl url(uri);
    uri = url.toEncoded();
    TX_EXIT
}

/*!
 Make a key & value pair string for querying
 */
QString MpQueryManager::keyValues( QStringList keys, QStringList values ) const
{
    TX_ENTRY
    QString str;
    if ( keys.length() != values.length() ) {
        TX_LOG_ARGS( "Error: keys length is not equal to values length" ); 
    } else {
        for ( int i = 0; i < keys.length(); i++ ) {
            QString tValue = values.at( i );
            if ( 0 != tValue.length() )
            {
                str += keys.at( i ) + "=" + values.at( i ) + "&";
            }
        }
    }
    TX_EXIT
    return str.left( str.length() - 1 );
}

/*!
 Sets recommendation album art
*/
void MpQueryManager::setAlbumArtUri( const QString &albumArtUri, const QString &albumArtName )
{
    TX_ENTRY_ARGS( "albumArtUri = " << albumArtUri )
    TX_LOG_ARGS( "albumArtName = " << albumArtName )
    if ( !albumArtUri.isEmpty() && !albumArtName.isEmpty() ) {
        int id = mThumbnailManager->getThumbnail( albumArtName, reinterpret_cast<void *>( const_cast<QString *>( &albumArtUri ) ) );
        if ( id == KUndefined ) {
            // Request failed. Set default album art.
            insertMapItem( albumArtUri, mDefaultRecommendationAlbumArt );
        }
    }
    else {
        // No album art uri. Set default album art.
        insertMapItem( albumArtUri, mDefaultRecommendationAlbumArt );
    }
    TX_EXIT
}

/*!
 Slot to handle the recommendation album art 
*/
void MpQueryManager::thumbnailReady(
        const QPixmap& pixmap,
        void *data,
        int /*id*/,
        int error  )
{
    TX_ENTRY
    // TODO: Hkn: use qobject_cast
    QString uri = *( reinterpret_cast<QString *>( data ) );
    TX_LOG_ARGS( "Uri: " << uri );
    
    if ( error == 0 ) {
        TX_LOG_ARGS( "album art link: " << uri );
        insertMapItem( uri, pixmap );
    } else {
        insertMapItem( uri, mDefaultRecommendationAlbumArt );
    }
    if(++mAlbumArtsReadyCount == KRecommendationCount) {
        emit recommendationAlbumArtsReady();
    }    
    TX_EXIT
}
