/* This file is part of the KDE libraries
   Copyright (C) 1999 Ian Zepp (icszepp@islc.net)

   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

/* AIX needs strings.h for str*casecmp(), and our config.h loads it on AIX
   So we don't need to include strings.h explicitly */

//US #include "config.h"

#include "kstringhandler.h"
/*US
QString KStringHandler::word( const QString &text , uint pos )
{
    QStringList list = QStringList::split( " ", text , true );

    if ( pos < list.count() )
        return list[ pos ];

    return "";
}

QString KStringHandler::word( const QString &text , const char *range )
{
    // Format in: START:END
    // Note index starts a 0 (zero)
    //
    // 0:        first word to end
    // 1:3        second to fourth words
    QStringList list = QStringList::split( " ", text , true );
    QString tmp = "";
    QString r = range;

    if ( text.isEmpty() )
        return tmp;

    // do stuff here
    QRegExp reg;

    int at = 0;
    int pos = 0;
    int cnt = 0;

    if ( r.find(QRegExp("[0-9]+:[0-9]+")) != -1 )
    {
        at  = r.find(":");
        pos = atoi( r.left(at).ascii() );
        cnt = atoi( r.remove(0,at+1).ascii() );
    }
    else if ( r.find(QRegExp(":+[0-9]+")) != -1 )
    {
        at  = r.find(":");
        pos = 0;
        cnt = atoi( r.remove(0,at+1).ascii() );
    }
    else if ( r.find(QRegExp("[0-9]+:+")) != -1 )
    {
        at  = r.find(":");
        pos = atoi( r.left(at).ascii() );
        cnt = list.count(); // zero index
    }
    else if ( r.find(QRegExp("[0-9]+")) != -1 )
    {
        pos = atoi( r.ascii() );
        cnt = pos;
    }
    else
    {
        return tmp; // not found/implemented
    }

    //
    // Extract words
    //
    int wordsToExtract = cnt-pos+1;
    QStringList::Iterator it = list.at( pos);

    while ( (it != list.end()) && (wordsToExtract-- > 0))
    {
       tmp += *it;
       tmp += " ";
       it++;
    }

    return tmp.stripWhiteSpace();
}

//
// Insertion and removal routines
//
QString KStringHandler::insword( const QString &text , const QString &word , uint pos )
{
    if ( text.isEmpty() )
        return word;

    if ( word.isEmpty() )
        return text;

    // Split words and add into list
    QStringList list = QStringList::split( " ", text, true );

    if ( pos >= list.count() )
        list.append( word );
    else
        list.insert( list.at(pos) , word );

    // Rejoin
    return list.join( " " );
}

QString KStringHandler::setword( const QString &text , const QString &word , uint pos )
{
    if ( text.isEmpty() )
        return word;

    if ( word.isEmpty() )
        return text;

    // Split words and add into list
    QStringList list = QStringList::split( " ", text, true );

    if ( pos >= list.count() )
        list.append( word );
    else
    {
        list.insert( list.remove( list.at(pos) ) , word );
    }

    // Rejoin
    return list.join( " " );
}

QString KStringHandler::remrange( const QString &text , const char *range )
{
    // Format in: START:END
    // Note index starts a 0 (zero)
    //
    // 0:        first word to end
    // 1:3        second to fourth words
    QStringList list = QStringList::split( " ", text , true );
    QString tmp = "";
    QString r = range;

    if ( text.isEmpty() )
        return tmp;

    // do stuff here
    QRegExp reg;

    int at = 0;
    int pos = 0;
    int cnt = 0;

    if ( r.find(QRegExp("[0-9]+:[0-9]+")) != -1 )
    {
        at  = r.find(':');
        pos = atoi( r.left(at).ascii() );
        cnt = atoi( r.remove(0,at+1).ascii() );
    }
    else if ( r.find(QRegExp(":+[0-9]+")) != -1 )
    {
        at  = r.find(':');
        pos = 0;
        cnt = atoi( r.remove(0,at+1).ascii() );
    }
    else if ( r.find(QRegExp("[0-9]+:+")) != -1 )
    {
        at  = r.find(':');
        pos = atoi( r.left(at).ascii() );
        cnt = list.count(); // zero index
    }
    else if ( r.find(QRegExp("[0-9]+")) != -1 )
    {
        pos = atoi( r.ascii() );
        cnt = pos;
    }
    else
    {
        return text; // not found/implemented
    }

    //
    // Remove that range of words
    //
    int wordsToDelete = cnt-pos+1;
    QStringList::Iterator it = list.at( pos);

    while ( (it != list.end()) && (wordsToDelete-- > 0))
       it = list.remove( it );

    return list.join( " " );
}

QString KStringHandler::remword( const QString &text , uint pos )
{
    QString tmp = "";

    if ( text.isEmpty() )
        return tmp;

    // Split words and add into list
    QStringList list = QStringList::split( " ", text, true );

    if ( pos < list.count() )
        list.remove( list.at( pos ) );

    // Rejoin
    return list.join( " " );
}

QString KStringHandler::remword( const QString &text , const QString &word )
{
    QString tmp = "";

    if ( text.isEmpty() )
        return tmp;

    if ( word.isEmpty() )
        return text;

    // Split words and add into list
    QStringList list = QStringList::split( " ", text, true );

    QStringList::Iterator it = list.find(word);

    if (it != list.end())
       list.remove( it );

    // Rejoin
    return list.join( " " );
}

//
// Capitalization routines
//
QString KStringHandler::capwords( const QString &text )
{
    QString tmp = "";

    if ( text.isEmpty() )
        return tmp;

    QStringList list = QStringList::split( " ", text, true );

    return capwords( QStringList::split( " ", text, true )).join( " " );
}

QStringList KStringHandler::capwords( const QStringList &list )
{
    QStringList tmp;
    QString word;

    if ( list.count() == 0 )
        return tmp;

    for ( QStringList::ConstIterator it= list.begin();
          it != list.end();
          it++)
    {
        word = *it;
        word = word.left(1).upper() + word.remove(0,1);

        tmp.append( word ); // blank list to start with
    }

    return tmp;
}

//
// Reverse routines
//
QString KStringHandler::reverse( const QString &text )
{
    QString tmp;

    if ( text.isEmpty() )
        return tmp;

    QStringList list;
    list = QStringList::split( " ", text, true );
    list = reverse( list );

    return list.join( " " );
}

QStringList KStringHandler::reverse( const QStringList &list )
{
    QStringList tmp;

    if ( list.count() == 0 )
        return tmp;

    for ( QStringList::ConstIterator it= list.begin();
          it != list.end();
          it++)
        tmp.prepend( *it );

    return tmp;
}

//
// Left, Right, Center justification
//
QString KStringHandler::ljust( const QString &text , uint width )
{
    QString tmp = text;
    tmp = tmp.stripWhiteSpace(); // remove leading/trailing spaces

    if ( tmp.length() >= width )
        return tmp;

    for ( uint pos = tmp.length() ; pos < width ; pos++ )
        tmp.append(" ");

    return tmp;
}

QString KStringHandler::rjust( const QString &text , uint width )
{
    QString tmp = text;
    tmp = tmp.stripWhiteSpace(); // remove leading/trailing spaces

    if ( tmp.length() >= width )
        return tmp;

    for ( uint pos = tmp.length() ; pos < width ; pos++ )
        tmp.prepend(" ");

    return tmp;
}

QString KStringHandler::center( const QString &text , uint width )
{
    // Center is slightly different, in that it will add
    // spaces to the RIGHT side (left-justified) before
    // it adds a space to the LEFT side.

    QString tmp = text;
    tmp = tmp.stripWhiteSpace(); // remove leading/trailing spaces

    if ( tmp.length() >= width )
        return tmp;

    bool left = false; // start at right side.

    for ( uint pos = tmp.length() ; pos < width ; pos++ )
    {
        if ( left )
            tmp.prepend(" ");
        else
            tmp.append(" ");

        // Reverse bool
        left = !left;
    }

    return tmp;
}

QString KStringHandler::lsqueeze( const QString & str, uint maxlen )
{
  if (str.length() > maxlen) {
    int part = maxlen-3;
    return QString("..." + str.right(part));
  }
  else return str;
}

QString KStringHandler::csqueeze( const QString & str, uint maxlen )
{
  if (str.length() > maxlen && maxlen > 3) {
    int part = (maxlen-3)/2;
    return QString(str.left(part) + "..." + str.right(part));
  }
  else return str;
}

QString KStringHandler::rsqueeze( const QString & str, uint maxlen )
{
  if (str.length() > maxlen) {
    int part = maxlen-3;
    return QString(str.left(part) + "...");
  }
  else return str;
}

QString KStringHandler::lEmSqueeze(const QString &name, const QFontMetrics& fontMetrics, uint maxlen)
{
  return lPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen);
}

static inline int emSqueezeLimit(int delta, int min, int max)
{
  if (delta < min) return min;
  if (delta > max) return max;
  return delta;
}

QString KStringHandler::lPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels)
{
  uint nameWidth = fontMetrics.width(name);

  if (maxPixels < nameWidth)
  {
    QString tmp = name;
    const uint em = fontMetrics.maxWidth();
    maxPixels -= fontMetrics.width("...");

    while (maxPixels < nameWidth && !tmp.isEmpty())
    {
      int delta = (nameWidth - maxPixels) / em;
      delta = emSqueezeLimit(delta, 1, delta); // no max

      tmp.remove(0, delta);
      nameWidth = fontMetrics.width(tmp);
    }

    return ("..." + tmp);
  }

  return name;
}

QString KStringHandler::cEmSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxlen)
{
  return cPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen);
}

QString KStringHandler::cPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels)
{
  uint nameWidth = fontMetrics.width(name);

  if (maxPixels < nameWidth)
  {
    QString tmp = name;
    const uint em = fontMetrics.maxWidth();
    maxPixels -= fontMetrics.width("...");

    while (maxPixels < nameWidth && !tmp.isEmpty())
    {
      int length = tmp.length();
      int delta = (nameWidth - maxPixels) / em;
      delta = emSqueezeLimit(delta, 1, length) ;

      tmp.remove((length / 2) - (delta / 2), delta);
      nameWidth = fontMetrics.width(tmp);
    }

    return tmp.insert((tmp.length() + 1) / 2, "...");
  }

  return name;
}

QString KStringHandler::rEmSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxlen)
{
  return rPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen);
}

QString KStringHandler::rPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels)
{
  uint nameWidth = fontMetrics.width(name);

  if (maxPixels < nameWidth)
  {
    QString tmp = name;
    const uint em = fontMetrics.maxWidth();
    maxPixels -= fontMetrics.width("...");

    while (maxPixels < nameWidth && !tmp.isEmpty())
    {
      int length = tmp.length();
      int delta = (nameWidth - maxPixels) / em;
      delta = emSqueezeLimit(delta, 1, length) ;

      tmp.remove(length - delta, delta);
      nameWidth = fontMetrics.width(tmp);
    }

    return (tmp + "...");
  }

  return name;
}

///// File name patterns (like *.txt)

bool KStringHandler::matchFileName( const QString& filename, const QString& pattern  )
{
   int len = filename.length();
   int pattern_len = pattern.length();

   if (!pattern_len)
      return false;

   // Patterns like "Makefile*"
   if ( pattern[ pattern_len - 1 ] == '*' && len + 1 >= pattern_len ) {
      const QChar *c1 = pattern.unicode();
      const QChar *c2 = filename.unicode();
      int cnt = 1;
      while ( cnt < pattern_len && *c1++ == *c2++ )
         ++cnt;
      return cnt == pattern_len;
   }

   // Patterns like "*~", "*.extension"
   if ( pattern[ 0 ] == '*' && len + 1 >= pattern_len )
   {
     const QChar *c1 = pattern.unicode() + pattern_len - 1;
     const QChar *c2 = filename.unicode() + len - 1;
     int cnt = 1;
     while ( cnt < pattern_len && *c1-- == *c2-- )
        ++cnt;
     return cnt == pattern_len;
  }

   // Patterns like "Makefile"
   return ( filename == pattern );
}

  QStringList
KStringHandler::perlSplit(const QString & sep, const QString & s, uint max)
{
  bool ignoreMax = 0 == max;

  QStringList l;

  int searchStart = 0;

  int tokenStart = s.find(sep, searchStart);

  while (-1 != tokenStart && (ignoreMax || l.count() < max - 1))
  {
    if (!s.mid(searchStart, tokenStart - searchStart).isEmpty())
      l << s.mid(searchStart, tokenStart - searchStart);

    searchStart = tokenStart + sep.length();
    tokenStart = s.find(sep, searchStart);
  }

  if (!s.mid(searchStart, s.length() - searchStart).isEmpty())
    l << s.mid(searchStart, s.length() - searchStart);

  return l;
}

  QStringList
KStringHandler::perlSplit(const QChar & sep, const QString & s, uint max)
{
  bool ignoreMax = 0 == max;

  QStringList l;

  int searchStart = 0;

  int tokenStart = s.find(sep, searchStart);

  while (-1 != tokenStart && (ignoreMax || l.count() < max - 1))
  {
    if (!s.mid(searchStart, tokenStart - searchStart).isEmpty())
      l << s.mid(searchStart, tokenStart - searchStart);

    searchStart = tokenStart + 1;
    tokenStart = s.find(sep, searchStart);
  }

  if (!s.mid(searchStart, s.length() - searchStart).isEmpty())
    l << s.mid(searchStart, s.length() - searchStart);

  return l;
}

  QStringList
KStringHandler::perlSplit(const QRegExp & sep, const QString & s, uint max)
{
  bool ignoreMax = 0 == max;

  QStringList l;

  int searchStart = 0;
  int tokenStart = sep.search(s, searchStart);
  int len = sep.matchedLength();

  while (-1 != tokenStart && (ignoreMax || l.count() < max - 1))
  {
    if (!s.mid(searchStart, tokenStart - searchStart).isEmpty())
      l << s.mid(searchStart, tokenStart - searchStart);

    searchStart = tokenStart + len;
    tokenStart = sep.search(s, searchStart);
    len = sep.matchedLength();
  }

  if (!s.mid(searchStart, s.length() - searchStart).isEmpty())
    l << s.mid(searchStart, s.length() - searchStart);

  return l;
}
US end */

/*US 
 QString
KStringHandler::tagURLs( const QString& text )
{
    QRegExp urlEx("(www\\.(?!\\.)|(f|ht)tp(|s)://)[\\d\\w\\./,:_~\\?=&;#@\\-\\+\\%]+[\\d\\w/]");

    QString richText( text );
    int urlPos = 0, urlLen;
    while ((urlPos = urlEx.search(richText, urlPos)) >= 0)
    {
        urlLen = urlEx.matchedLength();
        QString href = richText.mid( urlPos, urlLen );
        // Qt doesn't support (?<=pattern) so we do it here
        if((urlPos > 0) && richText[urlPos-1].isLetterOrNumber()){
            urlPos++;
            continue;
        }
        // Don't use QString::arg since %01, %20, etc could be in the string
        QString anchor = "<a href=\"" + href + "\">" + href + "</a>";
        richText.replace( urlPos, urlLen, anchor );


        urlPos += anchor.length();
    }
    return richText;
}
*/
QString KStringHandler::obscure( const QString &str )
{
  QString result;
  for ( uint i = 0; i < str.length(); ++i )
    result += ( str.at( i ).unicode() < 0x20 ) ? str.at( i ) :
        QChar( 0x1001F - str.at( i ).unicode() );

  return result;
}