/*
    KNode, the KDE newsreader
    Copyright (c) 1999-2005 the KNode authors.
    See file AUTHORS for details

    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.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
*/

#include <stdlib.h>

#include <tqdir.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqprogressbar.h>

#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kseparator.h>
#include <tdeapplication.h>
#include <kdebug.h>

#include "knfolder.h"
#include "knglobals.h"
#include "kncleanup.h"
#include "knconfig.h"
#include "knfoldermanager.h"
#include "kngroupmanager.h"
#include "knarticlemanager.h"
#include "knnntpaccount.h"


KNCleanUp::KNCleanUp() :  d_lg(0)
{
}


KNCleanUp::~KNCleanUp()
{
  delete d_lg;
}


void KNCleanUp::start()
{
  if ( mColList.isEmpty() )
    return;

  d_lg = new ProgressDialog( mColList.count() );
  d_lg->show();

  for ( TQValueList<KNArticleCollection*>::Iterator it = mColList.begin(); it != mColList.end(); ++it ) {
    if ( (*it)->type() == KNCollection::CTgroup ) {
      d_lg->showMessage( i18n("Deleting expired articles in <b>%1</b>").arg( (*it)->name() ) );
      kapp->processEvents();
      expireGroup( static_cast<KNGroup*>( (*it) ) );
      d_lg->doProgress();
    }
    else if ( (*it)->type() == KNCollection::CTfolder ) {
      d_lg->showMessage( i18n("Compacting folder <b>%1</b>").arg( (*it)->name() ) );
      kapp->processEvents();
      compactFolder( static_cast<KNFolder*>( (*it) ) );
      d_lg->doProgress();
    }
  }

  delete d_lg;
  d_lg=0;
}


void KNCleanUp::reset()
{
  mColList.clear();
  if(d_lg) {
    delete d_lg;
    d_lg=0;
  }
}


void KNCleanUp::expireGroup( KNGroup *g, bool showResult )
{
  int expDays=0, idRef=0, foundId=-1, delCnt=0, leftCnt=0, newCnt=0, firstArtNr=g->firstNr(), firstNew=-1;
  bool unavailable=false;
  KNRemoteArticle *art, *ref;

  if (!g)
    return;

  KNConfig::Cleanup *conf = g->activeCleanupConfig();

  g->setNotUnloadable(true);

  if (!g->isLoaded() && !knGlobals.groupManager()->loadHeaders(g)) {
    g->setNotUnloadable(false);
    return;
  }

  //find all expired
  for(int i=0; i<g->length(); i++) {
    art=g->at(i);
    if(art->isRead())
      expDays = conf->maxAgeForRead();
    else
      expDays = conf->maxAgeForUnread();

    unavailable = false;
    if ((art->articleNumber() != -1) && conf->removeUnavailable())
      unavailable = (art->articleNumber() < firstArtNr);

    art->setExpired( (art->date()->ageInDays() >= expDays) || unavailable );
  }

  //save threads
  if (conf->preserveThreads()) {
    for(int i=0; i<g->length(); i++) {
      art=g->at(i);
      if(!art->isExpired()) {
        idRef=art->idRef();
        while(idRef!=0) {
          ref=g->byId(idRef);
          ref->setExpired(false);
          idRef=ref->idRef();
        }
      }
    }
  }

  //restore threading
  for(int i=0; i<g->length(); i++) {
    art=g->at(i);
    if(!art->isExpired()) {
      idRef=art->idRef();
      foundId=0;
      while(foundId==0 && idRef!=0) {
        ref=g->byId(idRef);
        if(!ref->isExpired()) foundId=ref->id();
        idRef=ref->idRef();
      }
      art->setIdRef(foundId);
    }
  }

  //delete expired
  for(int i=0; i<g->length(); i++) {
    art=g->at(i);
    if(art->isExpired()) {
      if(art->isRead())
        g->decReadCount();
      delCnt++;
      if (art->hasContent())
        knGlobals.articleManager()->unloadArticle(art, true);
    }
    else if(art->isNew() && !art->isRead()) {
      if(firstNew==-1)
        firstNew=i;
      newCnt++;
    }
  }

  g->setNotUnloadable(false);

  if(delCnt>0) {
    g->saveStaticData(g->length(), true);
    g->saveDynamicData(g->length(), true);
    g->decCount(delCnt);
    g->setNewCount(newCnt);
    g->setFirstNewIndex(firstNew);
    g->saveInfo();
    knGlobals.groupManager()->unloadHeaders(g, true);
  }
  else
    g->syncDynamicData();

  conf->setLastExpireDate();
  g->saveInfo();
  leftCnt=g->count();

  kdDebug(5003) << "KNCleanUp::expireGroup() : " << g->groupname() << ": "
    << delCnt << " deleted , " << leftCnt << " left" << endl;

  if(showResult)
    KMessageBox::information(knGlobals.topWidget,
      i18n("<b>%1</b><br>expired: %2<br>left: %3").arg(g->groupname()).arg(delCnt).arg(leftCnt));
}


void KNCleanUp::compactFolder(KNFolder *f)
{
  KNLocalArticle *art;

  if (!f)
    return;

  TQDir dir(f->path());

  if(!dir.exists())
    return;

  f->setNotUnloadable(true);

  if (!f->isLoaded() && !knGlobals.folderManager()->loadHeaders(f)) {
    f->setNotUnloadable(false);
    return;
  }

  f->closeFiles();
  TQFileInfo info(f->m_boxFile);
  TQString oldName=info.fileName();
  TQString newName=oldName+".new";
  KNFile newMBoxFile(info.dirPath(true)+"/"+newName);

  if( (f->m_boxFile.open(IO_ReadOnly)) && (newMBoxFile.open(IO_WriteOnly)) ) {
    TQTextStream ts(&newMBoxFile);
    ts.setEncoding(TQTextStream::Latin1);
    for(int idx=0; idx<f->length(); idx++) {
      art=f->at(idx);
      if(f->m_boxFile.at(art->startOffset())) {
        ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
        art->setStartOffset(newMBoxFile.at());
        while(f->m_boxFile.at() < (uint)art->endOffset())
          ts << f->m_boxFile.readLineWnewLine();
        art->setEndOffset(newMBoxFile.at());
        newMBoxFile.putch('\n');
      }
    }

    f->syncIndex(true);
    newMBoxFile.close();
    f->closeFiles();

    dir.remove(oldName);
    dir.rename(newName, oldName);
  }

  f->setNotUnloadable(false);
}


//===============================================================================================


KNCleanUp::ProgressDialog::ProgressDialog(int steps)
 : TQDialog(knGlobals.topWidget, 0, true)
{
  const int w=400,
            h=160;

  p_rogress=0;
  s_teps=steps;

  setCaption(kapp->makeStdCaption(i18n("Cleaning Up")));

  setFixedSize(w,h);
  TQFrame *top=new TQFrame(this);
  top->setGeometry(0,0, w,h);

  TQVBoxLayout *topL=new TQVBoxLayout(top, 10);

  TQLabel *l=new TQLabel(i18n("Cleaning up. Please wait..."), top);
  topL->addWidget(l);

  KSeparator *sep=new KSeparator(top);
  topL->addWidget(sep);

  m_sg=new TQLabel(top);
  topL->addWidget(m_sg);

  p_bar=new TQProgressBar(top);
  topL->addWidget(p_bar);
  p_bar->setTotalSteps(100*s_teps);
  p_bar->setProgress(1);


  if(knGlobals.topWidget->isVisible()) {
    int x, y;
    x=(knGlobals.topWidget->width()-w)/2;
    y=(knGlobals.topWidget->height()-h)/2;
    if(x<0 || y<0) {
      x=0;
      y=0;
    }
    x+=knGlobals.topWidget->x();
    y+=knGlobals.topWidget->y();
    move(x,y);
  }
}


KNCleanUp::ProgressDialog::~ProgressDialog()
{
}


void KNCleanUp::ProgressDialog::showMessage(const TQString &s)
{
  m_sg->setText(s);
}


void KNCleanUp::ProgressDialog::doProgress()
{
  p_rogress++;
  p_bar->setProgress(p_rogress*100);
}


void KNCleanUp::ProgressDialog::closeEvent(TQCloseEvent *)
{
  // do nothing => prevent that the user closes the window
}
