/*
    This file is part of kdepim.

    Copyright (c) 2006 Christian Weilbach <christian@whiletaker.homeip.net>
    Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "API_MetaWeblog.h"
#include "xmlrpcjob.h"
#include <kdebug.h>

using namespace KBlog;

QString APIMetaWeblog::getFunctionName( blogFunctions type )
{
  switch ( type ) {
    case bloggerGetUserInfo:    return "metaWeblog.getUserInfo";
    case bloggerGetUsersBlogs:  return "metaWeblog.getUsersBlogs";
    case bloggerGetCategories:  return "metaWeblog.getCategories";
    case bloggerGetRecentPosts: return "metaWeblog.getRecentPosts";
    case bloggerNewPost:        return "metaWeblog.newPost";
    case bloggerNewMedia:	return "metaWeblog.newMediaObject";
    case bloggerEditPost:       return "metaWeblog.editPost";
    case bloggerDeletePost:     return "metaWeblog.deletePost";
    case bloggerGetPost:        return "metaWeblog.getPost";
    case bloggerGetTemplate:    return "metaWeblog.getTemplate";
    case bloggerSetTemplate:    return "metaWeblog.setTemplate";
    default: return QString::null;
  }
}




KIO::Job *APIMetaWeblog::createUserInfoJob()
{
   kdDebug() << "createUserInfoJob(): getUserInfo is not available in MetaWeblog API." << endl;
   return 0;
}

KIO::Job *APIMetaWeblog::createListFoldersJob()
{
   kdDebug() << "createListFoldersJob(): getUsersBlogs is not available in MetaWeblog API." << endl;
   return 0;
}

KIO::TransferJob *APIMetaWeblog::createListItemsJob( const KURL &url )
{
  // TODO: Check if we're already authenticated. If not, do it!
//   if ( isValid() ) {
    kdDebug() << "Fetch List of Posts..." << endl;
    QValueList<QVariant> args( defaultArgs( url.url() ) );
    args << QVariant( mDownloadCount );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerGetRecentPosts ), args, false );
//   } else {
//     warningNotInitialized();
//     return 0;
//   }
}

KIO::TransferJob *APIMetaWeblog::createListCategoriesJob( const KURL &url ){
    kdDebug() << "Fetch List of Categories..." << endl;
    QValueList<QVariant> args( defaultArgs( url.url() ) );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerGetCategories ), args, false );
}

KIO::TransferJob *APIMetaWeblog::createDownloadJob( const KURL &url )
{
//   if ( isValid() ){
    kdDebug() << "Fetch Posting with url " << url.url() << endl;
    QValueList<QVariant> args( defaultArgs( url.url() ) );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerGetPost ), args, false );
//   } else {
//     warningNotInitialized();
//     return 0;
//   }
}

KIO::TransferJob *APIMetaWeblog::createUploadJob( const KURL &url, KBlog::BlogPosting *posting )
{
  if ( !posting ) {
    kdDebug() << "APIMetaWeblog::createUploadJob: posting=0" << endl;
    emit error( "Empty Posting." );
    return 0;
}
//   if ( isValid() ){
    kdDebug() << "Uploading Posting with url " << url.url() << endl;

    QValueList<QVariant> args( defaultArgs( posting->postID() ) );
    QMap<QString, QVariant> map;
    QValueList<QVariant> list;
    list.append( QString( posting->category() ) );
    map["categories"]=list;
    map["description"]=posting->content();
    map["title"]=posting->title();
    if (posting->creationDateTime().isValid())
      map["dateCreated"]=posting->creationDateTime();
    else
      map["dateCreated"]=QDateTime::currentDateTime();
    args << map;
    args << QVariant( posting->publish(), 0 );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerEditPost ), args, false );
//   } else {
//     warningNotInitialized();
//     return 0;
//   }
}

KIO::TransferJob *APIMetaWeblog::createUploadNewJob( KBlog::BlogPosting *posting )
{
  if ( !posting ) {
    kdDebug() << "APIMetaWeblog::createUploadNewJob: posting=0" << endl;
    emit error( "Empty Posting." );
    return 0;
  }
//   if ( isValid() ){
    kdDebug() << "Creating new Posting with blogid " << posting->blogID() << " at url " << mServerURL << endl;
    QValueList<QVariant> args( defaultArgs( posting->blogID() ) );
    QMap<QString, QVariant> map;
    QValueList<QVariant> list;
    list.append( QString( posting->category() ) );
    map["categories"]=list;
    map["description"]=posting->content();
    map["title"]=posting->title();
    if (posting->creationDateTime().isValid())
      map["dateCreated"]=posting->creationDateTime();
    else
      map["dateCreated"]=QDateTime::currentDateTime();
    args << map;
    args << QVariant( posting->publish(), 0 );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerNewPost ), args, false );
//   } else {
//     warningNotInitialized();
//     return 0;
//   }
}

KIO::TransferJob *APIMetaWeblog::createUploadMediaJob( const KURL &url, KBlog::BlogMedia *media ){
    kdDebug() << "APIMetaWeblog::createUploadMediaJob: name="<< media->name() << endl;
    QValueList<QVariant> args( defaultArgs( media->blogID() ) );
    QMap<QString, QVariant> map;
    QValueList<QVariant> list;
    map["name"]=media->name();
    map["type"]=media->mimetype();
    map["bits"]=media->data();
    args << map;
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerNewMedia ), args, false );
}

KIO::Job *APIMetaWeblog::createRemoveJob( const KURL &/*url*/, const QString &postid )
{
kdDebug() << "APIMetaWeblog::createRemoveJob: postid=" << postid << endl;
//   if ( isValid() ){
    QValueList<QVariant> args( defaultArgs( postid ) );
    args << QVariant( /*publish=*/true, 0 );
    return KIO::xmlrpcCall( mServerURL, getFunctionName( bloggerDeletePost ), args, false );
//   } else {
//     warningNotInitialized();
//     return 0;
//   }
}

bool APIMetaWeblog::interpretUserInfoJob( KIO::Job *job )
{
  kdDebug()<<  "APIMetaWeblog::interpretUserInfoJob not implemented for MetaWeblog API" << endl;
  return false;
}

void APIMetaWeblog::interpretListFoldersJob( KIO::Job *job )
{
kdDebug() << "APIMetaWeblog::interpretListFoldersJob  not implemented for MetaWeblog API" << endl;
}

void APIMetaWeblog::interpretListCategoriesJob( KIO::Job *job ){
kdDebug() << "APIMetaWeblog::interpretCategoriesJob" << endl;
  KIO::XmlrpcJob *trfjob = dynamic_cast<KIO::XmlrpcJob*>(job);
  if ( job->error() || !trfjob ) {
    emit error( job->errorString() );
  } else {
kdDebug() << "APIMetaWeblog::interpretCategoriesJob, no error!" << endl;
    QValueList<QVariant> result( trfjob->response() );
//     kdDebug () << "TOP: " << message[0].typeName() << endl;
// 
//     const QMap<QString, QVariant> categories = message[0].toMap();
//     const QValueList<QString> categoryNames = categories.keys();
// 
//     QValueList<QString>::ConstIterator it = categoryNames.begin();
//     QValueList<QString>::ConstIterator end = categoryNames.end();
//     for ( ; it != end; ++it ) {
//       kdDebug () << "MIDDLE: " << ( *it ) << endl;
// 
//       const QString name( *it );
// 
//       const QMap<QString, QVariant> category = categories[ *it ].toMap();
//       const QString description( category["description"].toString() );
// 
//       if (  !name.isEmpty() ) {
//         emit categoryInfoRetrieved( name, description );
//          kdDebug()<< "Emitting categorieInfoRetrieved( name=" << name << " description=" << description << " ); " << endl;
//       }
//     }
//     kdDebug() << "Emitting fetchingCategoriesFinished()" << endl;
//     emit fetchingCategoriesFinished();
  if( result[ 0 ].type()!=QVariant::Map && result[ 0 ].type()!=QVariant::List ){ // include fix for not metaweblog standard compatible apis with array of structs instead of struct of structs, e.g. wordpress
    kdDebug () << "Could not list categories out of the result from the server." << endl;
  }
  else {
    if( result[ 0 ].type()==QVariant::Map ){
      const QMap<QString, QVariant> categories = result[0].toMap();
      const QValueList<QString> categoryNames = categories.keys();

    QValueList<QString>::ConstIterator it = categoryNames.begin();
    QValueList<QString>::ConstIterator end = categoryNames.end();
    for ( ; it != end; ++it ) {
      kdDebug () << "MIDDLE: " << ( *it ) << endl;
      const QString name( *it );
      const QMap<QString, QVariant> category = categories[ *it ].toMap();
      const QString description( category["description"].toString() );
      if (  !name.isEmpty() ) {
        emit categoryInfoRetrieved( name, description );
        kdDebug()<< "Emitting categorieInfoRetrieved( name=" << name << " description=" << description << " ); " << endl;
      }
     }
    }
    if( result[ 0 ].type()==QVariant::List ){// include fix for not metaweblog standard compatible apis with array of structs instead of struct of structs, e.g. wordpress
      const QValueList<QVariant> categories = result[ 0 ].toList();
      QValueList<QVariant>::ConstIterator it = categories.begin();
      QValueList<QVariant>::ConstIterator end = categories.end();
      for ( ; it != end; ++it ) {
        kdDebug () << "MIDDLE: " << ( *it ).typeName() << endl;
        const QMap<QString, QVariant> category = ( *it ).toMap();
        const QString description( category["description"].toString() );
        const QString name( category["categoryName"].toString() );
        if (  !name.isEmpty() ) {
          emit categoryInfoRetrieved( name, description );
          kdDebug()<< "Emitting categorieInfoRetrieved( name=" << name << " description=" << description << " ); " << endl;
        }
      }
  kdDebug() << "Emitting fetchingCategoriesFinished()" << endl;
  emit fetchingCategoriesFinished();
  }
  }
}
}


bool APIMetaWeblog::interpretListItemsJob( KIO::Job *job )
{
  kdDebug(5800)<<"APIMetaWeblog::interpretDownloadItemsJob"<<endl;
  KIO::XmlrpcJob *trfjob = dynamic_cast<KIO::XmlrpcJob*>(job);
  bool success = false;
  if ( job->error() || !trfjob ) {
    emit error( job->errorString() );
    success = false;
  } else {
    //array of structs containing ISO.8601 dateCreated, String userid, String postid, String content;
    // TODO: Time zone for the dateCreated!
    QValueList<QVariant> message( trfjob->response() );
    kdDebug () << "TOP: " << message[ 0 ].typeName() << endl;

    const QValueList<QVariant> postReceived = message[ 0 ].toList();
    QValueList<QVariant>::ConstIterator it = postReceived.begin();
    QValueList<QVariant>::ConstIterator end = postReceived.end();
    success = true;
    for ( ; it != end; ++it ) {
      BlogPosting posting;
      kdDebug () << "MIDDLE: " << ( *it ).typeName() << endl;
      const QMap<QString, QVariant> postInfo = ( *it ).toMap();
      if ( readPostingFromMap( &posting, postInfo ) ) {
//         KCal::Journal *j = journalFromPosting( &posting ); // not needed in kbloggers
//          dumpBlog( &posting );
         kdDebug() << "Emitting itemOnServer( posting with postID()=" << posting.postID() << "); " << endl;
         emit itemOnServer( posting ); //KURL( posting.postID() ) );
//         kdDebug() << "Emitting itemDownloaded( j=" << j << ", uid=" << j->uid()
//                   << ", postID=" << posting.postID() << ", fpr="
//                   << posting.fingerprint() << "); " << endl;
//         emit itemDownloaded( j, j->uid(), KURL( posting.postID() ),
//                              posting.fingerprint(), posting.postID() );
      } else {
         kdDebug() << "readPostingFromMap failed! " << endl;
         emit error( "Couldn't read posting." );
         success = false;
      }
    }
    kdDebug() << "Emitting fetchingPostsFinished() " << endl;
    emit fetchingPostsFinished();
  }
  return success;
}

bool APIMetaWeblog::interpretDownloadItemsJob( KIO::Job *job ){
  kdDebug(5800)<<"APIMetaWeblog::interpretDownloadItemsJob"<<endl;
  KIO::XmlrpcJob *trfjob = dynamic_cast<KIO::XmlrpcJob*>(job);
  bool success = false;
  if ( job->error() || !trfjob ) {
    emit error( job->errorString() );
    success = false;
  } else {
    success = true;
    //array of structs containing ISO.8601 dateCreated, String userid, String postid, String content;
    // TODO: Time zone for the dateCreated!
    QValueList<QVariant> message( trfjob->response() );
    kdDebug () << "TOP: " << message[ 0 ].typeName() << endl;
    BlogPosting posting;
    const QMap<QString, QVariant> postInfo = message[ 0 ].toMap();
    if ( readPostingFromMap( &posting, postInfo ) ) {
//         KCal::Journal *j = journalFromPosting( &posting ); // not needed in kbloggers
//          dumpBlog( &posting );
       kdDebug() << "Emitting itemOnServer( posting with postID()=" << posting.postID() << "); " << endl;
       emit itemOnServer( posting ); //KURL( posting.postID() ) );
//         kdDebug() << "Emitting itemDownloaded( j=" << j << ", uid=" << j->uid()
//                   << ", postID=" << posting.postID() << ", fpr="
//                   << posting.fingerprint() << "); " << endl;
//         emit itemDownloaded( j, j->uid(), KURL( posting.postID() ),
//                              posting.fingerprint(), posting.postID() );
    } else {
       kdDebug() << "readPostingFromMap failed! " << endl;
       emit error( "Couldn't read posting." );
       success = false;
    }
  }
 return success;
}

bool APIMetaWeblog::interpretUploadJob( KIO::Job *job )
{
  kdDebug(5800)<<"APIMetaWeblog::interpretUploadJob"<<endl;
  KIO::XmlrpcJob *trfjob = dynamic_cast<KIO::XmlrpcJob*>(job);
  bool success = false;
  if ( job->error() || !trfjob ) {
    kdDebug() << "APIMetaWeblog::interpretUploadJob job->error()" << endl;
    emit error( job->errorString() );
    success = false;
  } else {
    //array of structs containing ISO.8601 dateCreated, String userid, String postid, String content;
    // TODO: Time zone for the dateCreated!
    QValueList<QVariant> message( trfjob->response() );
    kdDebug () << "TOP: " << message[ 0 ].typeName() << endl;
    QString id = message[ 0 ].toString();
    kdDebug() << "MIDDLE: id=" << id << endl;

    emit uploadPostId( id.toInt() );
    kdDebug() << "Emitting uploadPostId( " << id.toInt() << " )" << endl;
    success = true;
  }
  return success;
}

bool APIMetaWeblog::interpretUploadMediaJob( KIO::Job *job ){
kdDebug() << "APIMetaWeblog::interpretUploadMediaJob" << endl;
  KIO::XmlrpcJob *trfjob = dynamic_cast<KIO::XmlrpcJob*>(job);
  bool success = false;
  if ( job->error() || !trfjob ) {
    emit error( job->errorString() );
  } else {
kdDebug() << "APIMetaWeblog::interpretUploadMediaJob, no error!" << endl;
    QValueList<QVariant> message( trfjob->response() );
    kdDebug () << "TOP: " << message[0].typeName() << endl;

    const QMap<QString, QVariant> resultStruct = message[0].toMap();
    const QString url = resultStruct["url"].toString();
kdDebug() << "APIMetaWeblog::interpretUploadMediaJob url="<< url << endl;

      if (  !url.isEmpty() ) {
        emit mediaInfoRetrieved( url );
         kdDebug()<< "Emitting mediaInfoRetrieved( url=" << url  << " ); " << endl;
      }
    success = true;
   }
   return success;
}

bool APIMetaWeblog::readPostingFromMap( BlogPosting *post, const QMap<QString, QVariant> &postInfo )
{
  // FIXME:
  if ( !post ) return false;
  QStringList mapkeys = postInfo.keys();
  kdDebug() << endl << "Keys: " << mapkeys.join(", ") << endl << endl;

  QString fp( QString::null );
  
  QDateTime dt( postInfo[ "dateCreated" ].toDateTime() );
  if ( dt.isValid() && !dt.isNull() ) {
    post->setCreationDateTime( dt );
    QString fp = dt.toString( Qt::ISODate );
  }
  dt = postInfo[ "postDate" ].toDateTime();
  if ( dt.isValid() && !dt.isNull() ) {
    post->setDateTime( dt );
    fp = dt.toString( Qt::ISODate );
  }
  dt = postInfo[ "lastModified" ].toDateTime();
  if ( dt.isValid() && !dt.isNull() ) {
    post->setModificationDateTime( dt );
    fp = dt.toString( Qt::ISODate );
  }
  post->setFingerprint( fp );

  post->setUserID( postInfo[ "userid" ].toString() );
  post->setPostID( postInfo[ "postid" ].toString() );

  QString title( postInfo[ "title" ].toString().utf8() );
  QString description( postInfo[ "description" ].toString().utf8() );
//   QString contents( postInfo[ "content" ].toString() ); // FIXME delete if not necessary
  QValueList<QVariant> categories( postInfo[ "categories" ].toList() );

    // TODO: Extract title and cats from the old-style blogger api without extensions
/*
  if ( (title.isEmpty() || description.isEmpty() ) && !contents.isEmpty()  ) {
    // we don't have both title and description, so use the content (ie. it's an
    // old-style blogger api, not the extended drupal api.

    kdDebug() << "No title and description given, so it's an old-style "
                 "Blogger API without extensions" << endl;
    QString catTagOpen = mTemplate.categoryTagOpen();
    QString catTagClose = mTemplate.categoryTagClose();
    QString titleTagOpen = mTemplate.titleTagOpen();
    QString titleTagClose = mTemplate.titleTagClose();

    int catStart = contents.find( catTagOpen, 0, false ) + catTagOpen.length();
    int catEnd = contents.find( catTagClose, 0, false );
kdDebug() << "  catTagOpen = " << catTagOpen << ", catTagClose = " << catTagClose << ", start - end : " << catStart <<" - " << catEnd << endl;
    if ( catEnd > catStart ) {
      category = contents.mid( catStart, catEnd - catStart );
      kdDebug() << "Found a category \"" << category << "\"" << endl;
      contents = contents.remove( catStart - catTagOpen.length(),
              catEnd - catStart + catTagClose.length() + catTagOpen.length() );
    }
    int titleStart = contents.find( titleTagOpen, 0, false ) + titleTagOpen.length();
    int titleEnd = contents.find( titleTagClose, 0, false );
kdDebug() << "  titleTagOpen = " << titleTagOpen << ", titleTagClose = " << titleTagClose << ", start - end : " << titleStart <<" - " << titleEnd << endl;
    kdDebug() << "Title start and end: " << titleStart << ", " << titleEnd << endl;
    if ( titleEnd > titleStart ) {
      title = contents.mid( titleStart, titleEnd - titleStart );
      contents = contents.remove( titleStart - titleTagOpen.length(),
              titleEnd - titleStart + titleTagClose.length() + titleTagOpen.length() );
    }
    kdDebug() << endl << endl << endl << "After treatment of the special tags, we have a content of: "<< endl << contents << endl;
  }
*/
  post->setTitle( title );
  post->setContent( description );
  if ( !categories.isEmpty() ){
    QString category = ( *categories.begin() ).toString();
    kdDebug() << "Category: " <<  category  << endl;
    post->setCategory( category );
  }
  return true;
}



