/*
    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 <tqlayout.h>
#include <tqregexp.h>
#include <tqapplication.h>
#include <tqcursor.h>

#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdeglobalsettings.h>
#include <kdebug.h>
#include <tdeio/netaccess.h>
#include <tdetempfile.h>
#include <tdefiledialog.h>

#include "knwidgets.h"
#include "knglobals.h"
#include "utilities.h"



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

KNFile::KNFile(const TQString& fname)
 : TQFile(fname), filePos(0), readBytes(0)
{
  buffer.resize(512);
  dataPtr=buffer.data();
  dataPtr[0]='\0';
}


KNFile::~KNFile()
{
}


const TQCString& KNFile::readLine()
{
  filePos=at();
  readBytes=TQFile::readLine(dataPtr, buffer.size()-1);
  if(readBytes!=-1) {
    while ((dataPtr[readBytes-1]!='\n')&&(static_cast<uint>(readBytes+2)==buffer.size())) {  // don't get tricked by files without newline
      at(filePos);
      if (!increaseBuffer() ||
         (readBytes=TQFile::readLine(dataPtr, buffer.size()-1))==-1) {
        readBytes=1;
        break;
      }
    }
  } else
    readBytes=1;

  dataPtr[readBytes-1] = '\0';
  return buffer;
}


const TQCString& KNFile::readLineWnewLine()
{
  filePos=at();
  readBytes=TQFile::readLine(dataPtr, buffer.size()-1);
  if(readBytes!=-1) {
    while ((dataPtr[readBytes-1]!='\n')&&(static_cast<uint>(readBytes+2)==buffer.size())) {  // don't get tricked by files without newline
      at(filePos);
      if (!increaseBuffer() ||
         (readBytes=TQFile::readLine(dataPtr, buffer.size()-1))==-1) {
        dataPtr[0] = '\0';
        break;
      }
    }
  }
  else dataPtr[0] = '\0';

  return buffer;
}


int KNFile::findString(const char *s)
{
  TQCString searchBuffer;
  searchBuffer.resize(2048);
  char *buffPtr = searchBuffer.data(), *pos;
  int readBytes, currentFilePos;

  while (!atEnd()) {
    currentFilePos = at();
    readBytes = readBlock(buffPtr, 2047);
    if (readBytes == -1)
      return -1;
    else
      buffPtr[readBytes] = 0;       // terminate string

    pos = strstr(buffPtr,s);
    if (pos == 0) {
      if (!atEnd())
        at(at()-strlen(s));
      else
        return -1;
    } else {
      return currentFilePos + (pos-buffPtr);
    }
  }

  return -1;
}


bool KNFile::increaseBuffer()
{
  if(buffer.resize(2*buffer.size())) {;
    dataPtr=buffer.data();
    dataPtr[0]='\0';
    kdDebug(5003) << "KNFile::increaseBuffer() : buffer doubled" << endl;
    return true;
  }
  else return false;
}


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

TQString KNSaveHelper::lastPath;

KNSaveHelper::KNSaveHelper(TQString saveName, TQWidget *parent)
  : p_arent(parent), s_aveName(saveName), file(0), tmpFile(0)
{
}


KNSaveHelper::~KNSaveHelper()
{
  if (file) {       // local filesystem, just close the file
    delete file;
  } else
    if (tmpFile) {      // network location, initiate transaction
      tmpFile->close();
      if (TDEIO::NetAccess::upload(tmpFile->name(),url, 0) == false)
        KNHelper::displayRemoteFileError();
      tmpFile->unlink();   // delete temp file
      delete tmpFile;
    }
}


TQFile* KNSaveHelper::getFile(const TQString &dialogTitle)
{
  url = KFileDialog::getSaveURL(lastPath + s_aveName, TQString(), p_arent, dialogTitle);

  if (url.isEmpty())
    return 0;

  lastPath = url.upURL().url();

  if (url.isLocalFile()) {
    if (TQFileInfo(url.path()).exists() &&
        (KMessageBox::warningContinueCancel(knGlobals.topWidget,
                                            i18n("<qt>A file named <b>%1</b> already exists.<br>Do you want to replace it?</qt>").arg(url.path()),
                                            dialogTitle, i18n("&Replace")) != KMessageBox::Continue)) {
      return 0;
    }

    file = new TQFile(url.path());
    if(!file->open(IO_WriteOnly)) {
      KNHelper::displayExternalFileError();
      delete file;
      file = 0;
    }
    return file;
  } else {
    tmpFile = new KTempFile();
    if (tmpFile->status()!=0) {
      KNHelper::displayTempFileError();
      delete tmpFile;
      tmpFile = 0;
      return 0;
    }
    return tmpFile->file();
  }
}


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

TQString KNLoadHelper::l_astPath;

KNLoadHelper::KNLoadHelper(TQWidget *parent)
  : p_arent(parent), f_ile(0)
{
}


KNLoadHelper::~KNLoadHelper()
{
  delete f_ile;
  if (!t_empName.isEmpty())
    TDEIO::NetAccess::removeTempFile(t_empName);
}


KNFile* KNLoadHelper::getFile( const TQString &dialogTitle )
{
  if (f_ile)
    return f_ile;

  KURL url = KFileDialog::getOpenURL(l_astPath,TQString(),p_arent,dialogTitle);

  if (url.isEmpty())
    return 0;

  l_astPath = url.url(-1);
  l_astPath.truncate(l_astPath.length()-url.fileName().length());

  return setURL(url);
}


KNFile* KNLoadHelper::setURL(KURL url)
{
  if (f_ile)
    return f_ile;

  u_rl = url;

  if (u_rl.isEmpty())
    return 0;

  TQString fileName;
  if (!u_rl.isLocalFile()) {
    if (TDEIO::NetAccess::download(u_rl, t_empName, 0))
      fileName = t_empName;
  } else
    fileName = u_rl.path();

  if (fileName.isEmpty())
    return 0;

  f_ile = new KNFile(fileName);
  if(!f_ile->open(IO_ReadOnly)) {
    KNHelper::displayExternalFileError();
    delete f_ile;
    f_ile = 0;
  }
  return f_ile;
}


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


// **** keyboard selection dialog *********************************************
int KNHelper::selectDialog(TQWidget *parent, const TQString &caption, const TQStringList &options, int initialValue)
{
  KDialogBase *dlg=new KDialogBase(KDialogBase::Plain, caption, KDialogBase::Ok|KDialogBase::Cancel,
                                   KDialogBase::Ok, parent);
  TQFrame *page = dlg->plainPage();
  TQHBoxLayout *pageL = new TQHBoxLayout(page,8,5);

  KNDialogListBox *list = new KNDialogListBox(true, page);
  pageL->addWidget(list);

  TQString s;
  for ( TQStringList::ConstIterator it = options.begin(); it != options.end(); ++it ) {
    s = (*it);
    s.replace(TQRegExp("&"),"");   // remove accelerators
    list->insertItem(s);
  }

  list->setCurrentItem(initialValue);
  list->setFocus();
  restoreWindowSize("selectBox", dlg, TQSize(247,174));

  int ret;
  if (dlg->exec())
    ret = list->currentItem();
  else
    ret = -1;

  saveWindowSize("selectBox", dlg->size());
  delete dlg;
  return ret;
}

// **** window geometry managing *********************************************

void KNHelper::saveWindowSize(const TQString &name, const TQSize &s)
{
  TDEConfig *c=knGlobals.config();
  c->setGroup("WINDOW_SIZES");
  c->writeEntry(name, s);
}


void KNHelper::restoreWindowSize(const TQString &name, TQWidget *d, const TQSize &defaultSize)
{
  TDEConfig *c=knGlobals.config();
  c->setGroup("WINDOW_SIZES");

  TQSize s=c->readSizeEntry(name,&defaultSize);

  if(s.isValid()) {
    TQRect max = TDEGlobalSettings::desktopGeometry(TQCursor::pos());
    if ( s.width() > max.width() ) s.setWidth( max.width()-5 );
    if ( s.height() > max.height() ) s.setHeight( max.height()-5 );
    d->resize(s);
  }
}

// **** scramble password strings **********************************************

const TQString KNHelper::encryptStr(const TQString& aStr)
{
  uint i,val,len = aStr.length();
  TQCString result;

  for (i=0; i<len; i++)
  {
    val = aStr[i] - ' ';
    val = (255-' ') - val;
    result += (char)(val + ' ');
  }

  return result;
}


const TQString KNHelper::decryptStr(const TQString& aStr)
{
  return encryptStr(aStr);
}

// **** rot13 *******************************************************************

TQString KNHelper::rot13(const TQString &s)
{
  TQString r(s);

  for (int i=0; (uint)i<r.length(); i++) {
    if ( r[i] >= TQChar('A') && r[i] <= TQChar('M') ||
         r[i] >= TQChar('a') && r[i] <= TQChar('m') )
         r[i] = (char)((int)TQChar(r[i]) + 13);
    else
      if  ( r[i] >= TQChar('N') && r[i] <= TQChar('Z') ||
            r[i] >= TQChar('n') && r[i] <= TQChar('z') )
        r[i] = (char)((int)TQChar(r[i]) - 13);
  }

  return r;
}

// **** text rewraping *********************************************************

int findBreakPos(const TQString &text, int start)
{
  int i;
  for(i=start;i>=0;i--)
    if(text[i].isSpace())
      break;
  if(i>0)
    return i;
  for(i=start;i<(int)text.length();i++)   // ok, the line is to long
    if(text[i].isSpace())
      break;
  return i;
}


void appendTextWPrefix(TQString &result, const TQString &text, int wrapAt, const TQString &prefix)
{
  TQString txt=text;
  int breakPos;

  while(!txt.isEmpty()) {

    if((int)(prefix.length()+txt.length()) > wrapAt) {
      breakPos=findBreakPos(txt,wrapAt-prefix.length());
      result+=(prefix+txt.left(breakPos)+"\n");
      txt.remove(0,breakPos+1);
    } else {
      result+=(prefix+txt+"\n");
      txt=TQString();
    }
  }
}


TQString KNHelper::rewrapStringList(TQStringList text, int wrapAt, TQChar quoteChar, bool stopAtSig, bool alwaysSpace)
{
  TQString quoted, lastPrefix, thisPrefix, leftover, thisLine;
  int breakPos;

  for(TQStringList::Iterator line=text.begin(); line!=text.end(); ++line) {

    if(stopAtSig && (*line)=="-- ")
      break;

    thisLine=(*line);
    if (!alwaysSpace && (thisLine[0]==quoteChar))
      thisLine.prepend(quoteChar);  // second quote level without space
    else
      thisLine.prepend(TQString(quoteChar)+' ');

    thisPrefix=TQString();
    TQChar c;
    for(int idx=0; idx<(int)(thisLine.length()); idx++) {
      c=thisLine.at(idx);
      if( (c==' ') ||
          (c==quoteChar) || (c=='>') ||(c=='|') || (c==':') || (c=='#') || (c=='[') || (c=='{'))
        thisPrefix.append(c);
      else
        break;
    }

    thisLine.remove(0,thisPrefix.length());
    thisLine = thisLine.stripWhiteSpace();

    if(!leftover.isEmpty()) {   // don't break paragraphs, tables and quote levels
      if(thisLine.isEmpty() || (thisPrefix!=lastPrefix) || thisLine.contains("  ") || thisLine.contains('\t'))
        appendTextWPrefix(quoted, leftover, wrapAt, lastPrefix);
      else
        thisLine.prepend(leftover+" ");
      leftover=TQString();
    }

    if((int)(thisPrefix.length()+thisLine.length()) > wrapAt) {
      breakPos=findBreakPos(thisLine,wrapAt-thisPrefix.length());
      if(breakPos < (int)(thisLine.length())) {
        leftover=thisLine.right(thisLine.length()-breakPos-1);
        thisLine.truncate(breakPos);
      }
    }

    quoted+=thisPrefix+thisLine+"\n";
    lastPrefix=thisPrefix;
  }

  if (!leftover.isEmpty())
    appendTextWPrefix(quoted, leftover, wrapAt, lastPrefix);

  return quoted;
}

// **** misc. message-boxes **********************************************************

void KNHelper::displayInternalFileError(TQWidget *w)
{
  KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to load/save configuration.\nWrong permissions on home folder?\nYou should close KNode now to avoid data loss."));
}


void KNHelper::displayExternalFileError(TQWidget *w)
{
  KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to load/save file."));
}


void KNHelper::displayRemoteFileError(TQWidget *w)
{
  KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to save remote file."));
}


void KNHelper::displayTempFileError(TQWidget *w)
{
  KMessageBox::error((w!=0)? w : knGlobals.topWidget, i18n("Unable to create temporary file."));
}
