//
// C++ Implementation: soundprovider
//
// Description: 
//
//
// Author: Eitzenberger Thomas <kde@atpc310d>, (C) 2009
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "wiktionaryprovider.h"

#include <QCryptographicHash>
#include <QFile>
#include <QHttp>

#include <KDebug>
#include <kio/scheduler.h>

const char* WiktionaryProvider::PROVIDER_CLASS_NAME = "wiktionary";

WiktionaryProvider::WiktionaryProvider(QObject* parent) : SoundProvider(parent)
{
  kDebug() << "CREATED";
  m_localCache = true;
}

WiktionaryProvider::~WiktionaryProvider()
{
  if (m_taskData.size() != 0) {
    kDebug() << "pending tasks !";
    foreach (TaskData* data, m_taskData) {
      kDebug() << "task still pending" << data->job << " for url " << data->remoteUrl;
    }
  }
  kDebug() << "DELETED";
}

KUrl WiktionaryProvider::lookupSoundUrlForItem(QString lesson, QString item, QString lang, int loc)
{
  kDebug() << item << " in lang:" << lang;
  KUrl soundurl;
  if (lang == "en") {
    if (item.startsWith("to ")) {
      item = item.mid(3);
      kDebug() << "Searching for verb " << item << "without 'to '";
    }
    soundurl = lookupWiktionaryFor(item, "En-us", loc);
    if (soundurl.isEmpty()) {
      soundurl = lookupWiktionaryFor(item, "En-ca", loc);
    }
  } else if (lang == "de") {
    if (item.startsWith("der ") || item.startsWith("die ") || item.startsWith("das ")) {
      item = item.mid(4);
      kDebug() << "Stripping german article from " << item;
    }
    soundurl = lookupWiktionaryFor(item, "De", loc);
    if (soundurl.isEmpty()) {
      soundurl = lookupWiktionaryFor(item, "De-at", loc);
    }
  } else if (lang == "fr") {
    soundurl = lookupWiktionaryFor(item, "Fr", loc);
    if (soundurl.isEmpty()) {
      soundurl = lookupWiktionaryFor(item, "Fr-Paris", loc);
    }
  } else {
    kDebug() << "Language " << lang << " is not supported !"; 
    kDebug() << "trying default language code for wiktionary without an dialect";
    
    lang[0] = lang[0].toUpper();
    soundurl = lookupWiktionaryFor(item, lang, loc);
    
  }
  return soundurl;
}

KUrl WiktionaryProvider::lookupWiktionaryFor(QString item, QString lang, int loc) {

  QString localCacheName = getLocalCacheName(PROVIDER_CLASS_NAME, item, lang);
  QString filename = lang + "-" + item + ".ogg";  

  if (loc == LOCALCACHE) {
    kDebug() << item << "in lang:" << lang << " LOCALLY ";
    QFile localFile(localCacheName);
    if (localFile.open(QIODevice::ReadOnly)) {
      localFile.close();
      kDebug() << "FOUND LOCAL COPY of sound file " << localCacheName << " for item:" << item;
      return KUrl("file://" + localCacheName);
    } else {
      localFile.close();
      kDebug() << "NO LOCAL COPY of sound file " << localCacheName << " found for item:" << item;
      return KUrl("");
    }
  } else {
    kDebug() << item << "in lang:" << lang << " REMOTELY";

  /* mediawiki stores the files under commons in separate sub folders due to the enormous  
    * number of files the filesys is not able to cope with millions of files in one folder, so 
    * the rule is take the md5 hex hash of the filename (all lowercase, space replaced by "_") 
    * and sore it under /md5[0]/md5[0:1]/filename
  */
    QCryptographicHash md5( QCryptographicHash::Md5 );
    QString md5str = QString(filename);
    QByteArray ba = QByteArray(md5str.toUtf8());
    md5.addData (ba.data(), ba.size());
    md5str = md5.result().toHex();
    KUrl soundurl = KUrl("http://upload.wikimedia.org/wikipedia/commons/" + md5str[0] + "/" + md5str[0] + md5str[1] + "/" + filename);

  // start the transferjob. As mediawiki does not respond with 404 
  // but sends an error page instead 
  // its no way to do a stat job and check for error 
  // but i have to do the copy job and check for is error page
    KIO::SimpleJob* soundDownloadJob = KIO::get(soundurl, KIO::NoReload, KIO::HideProgressInfo); 
    TaskData* data = new TaskData();
    data->cacheName = getLocalCacheName(PROVIDER_CLASS_NAME, item, lang);
    data->soundData = new QByteArray();
    data->remoteUrl = soundurl;
    data->item = item;
    data->job = soundDownloadJob;
    m_taskData.insert(soundDownloadJob, data);
    connect(soundDownloadJob,  SIGNAL(result(KJob*)),
            this, SLOT(slotResult(KJob*)));
    connect(soundDownloadJob,  SIGNAL(data(KIO::Job*, const QByteArray&)),
            this, SLOT(slotData(KIO::Job*, const QByteArray&)));
    connect(soundDownloadJob, SIGNAL(infoMessage(KJob*, const QString&, const QString&)), this, SLOT(infomsg(KJob*, const QString&, const QString&)));
    return soundurl;
  }
}

// just for debug reasons
void WiktionaryProvider::infomsg(KJob* job, const QString& msg, const QString&) {
  kDebug() << msg << " for " << m_taskData.value(job)->cacheName;
}

void WiktionaryProvider::slotData(KIO::Job* job, const QByteArray& bytes) {
  // we shall abort if no local cache supported or active and 
  // just signal sucess when downloaded more then 1500 bytes 
  // (error page is not more then 870 :O) 
  TaskData* data = m_taskData.value(job);
  QByteArray* bytearr = data->soundData;
  //TODO deal with NULL !
  if (!supportsLocalCache() || !localCacheActive()) {
    if (bytearr->length() + bytes.length() > 1500) {
      kDebug() << "REMOTE URL OK and no caching supported -> " << 
          "aborting download for " << data->remoteUrl;
      KIO::Scheduler::cancelJob(data->job);
      bytearr->clear();
      delete bytearr;
      emit soundFileLoaded(data->item, data->remoteUrl); 
      return;
    }
  }
  bytearr->append(bytes);
}

/// checks if transferred page is an error page and sets m_soundurl accordingly
void WiktionaryProvider::slotResult(KJob *job) {

  TaskData* data = m_taskData.value(job);
  QByteArray* bytes = data->soundData;
  QString cacheName = data->cacheName;
  m_taskData.remove(job);
  
  if (((KIO::TransferJob*)job)->isErrorPage() || job->error()) {
    if (job->error()) {
      kDebug() << "Download ERROR:" << job->errorText();
    }
    kDebug() << "Download NOT ok for " << cacheName;
    delete bytes;
  } else {
    if (!localCacheActive() || !supportsLocalCache()) {
      kDebug() << "Download OK but no cache avail so using " << data->remoteUrl;
      bytes->clear();
      delete bytes;
      emit soundFileLoaded(data->item, data->remoteUrl); 
      return;
    }
    kDebug() << "Download OK for " << cacheName;
    QFile localFile(cacheName);
    if (!localFile.open(QIODevice::WriteOnly)) {
      kDebug() << " unable to write sound data to file " << cacheName;
      emit soundFileLoaded(data->item, data->remoteUrl); 
    } else {
      kDebug() << "written " << localFile.write(*bytes) << "bytes";
      localFile.close();
      emit soundFileLoaded(data->item, KUrl("file://" + cacheName)); 
    }
    bytes->clear();
    delete bytes;
  }
  disconnect(job,  SIGNAL(result(KJob*)),
             this, SLOT(slotResult(KJob*)));
  disconnect(job,  SIGNAL(data(KIO::Job*, const QByteArray&)),
             this, SLOT(slotData(KIO::Job*, const QByteArray&)));
  disconnect(job, SIGNAL(infoMessage(KJob*, const QString&, const QString&)), this, SLOT(infomsg(KJob*, const QString&, const QString&)));
}

void WiktionaryProvider::abortDownloadSoundUrl(KUrl remoteUrl) {
  foreach (TaskData* data, m_taskData) {
    if (data->remoteUrl == remoteUrl) {
      kDebug() << "cancelling task" << data->job << " for url " << data->remoteUrl;
      KIO::Scheduler::cancelJob(data->job);
    }
  }
}

#include "wiktionaryprovider.moc"
