/* This file is part of the KDE project

   Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
   Copyright 2002-2004 Ariya Hidayat <ariya@kde.org>
   Copyright 2002-2003 Norbert Andres <nandres@web.de>
   Copyright 2002 John Dailey <dailey@vt.edu>
   Copyright 2001-2002 Philipp Mueller <philipp.mueller@gmx.de>
   Copyright 2000-2002 Laurent Montel <montel@kde.org>
   Copyright 2000-2001 Werner Trobin <trobin@kde.org>
   Copyright 1999-2001 David Faure <faure@kde.org>
   Copyright 1998-2000 Torben Weis <weis@kde.org>
   Copyright 1998-1999 Stephan Kulow <coolo@kde.org>
   Copyright 1998 Reginald Stadlbauer <reggie@kde.org>

   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 <math.h>

#include <tqregexp.h>

#include <tdeconfig.h>
#include <kdebug.h>

#include "kspread_doc.h"
#include "kspread_locale.h"
#include "kspread_sheet.h"
#include "kspread_undo.h"
#include "kspread_value.h"
#include "valueconverter.h"
#include "kspread_autofill.h"

using namespace KSpread;

TQStringList *AutoFillSequenceItem::month = 0L;
TQStringList *AutoFillSequenceItem::shortMonth = 0L;
TQStringList *AutoFillSequenceItem::day = 0L;
TQStringList *AutoFillSequenceItem::shortDay = 0L;
TQStringList *AutoFillSequenceItem::other = 0L;
/**********************************************************************************
 *
 * AutoFillDeltaSequence
 *
 **********************************************************************************/

AutoFillDeltaSequence::AutoFillDeltaSequence( AutoFillSequence *_first, AutoFillSequence *_next )
  : m_ok(true),
    m_sequence(0L)
{
  if ( _first->count() != _next->count() )
  {
    m_ok = false;
    return;
  }

  m_sequence = new TQMemArray<double> ( _first->count() );

  AutoFillSequenceItem *item = _first->getFirst();
  AutoFillSequenceItem *item2 = _next->getFirst();
  int i = 0;
  // for( item = _first->getFirst(); item != 0L && item2 != 0L; item = _first->getNext() );
  for ( i = 0; i < _first->count(); i++ )
  {
    double d;
    if ( !item->getDelta( item2, d ) )
      {
        m_ok = false;
        return;
      }
    m_sequence->at( i++ ) = d;
    item2 = _next->getNext();
    item = _first->getNext();
  }
}

AutoFillDeltaSequence::~AutoFillDeltaSequence()
{
    delete m_sequence;
}

bool AutoFillDeltaSequence::equals( AutoFillDeltaSequence *_delta )
{
  if ( m_sequence == 0L )
    return false;
  if ( _delta->getSequence() == 0L )
    return false;
  if ( m_sequence->size() != _delta->getSequence()->size() )
    return false;

  for ( unsigned int i = 0; i < m_sequence->size(); i++ )
  {
    if ( m_sequence->at( i ) != _delta->getSequence()->at( i ) )
      return false;
  }

  return true;
}

double AutoFillDeltaSequence::getItemDelta( int _pos )
{
  if ( m_sequence == 0L )
    return 0.0;

  return m_sequence->at( _pos );
}

/**********************************************************************************
 *
 * AutoFillSequenceItem
 *
 **********************************************************************************/

AutoFillSequenceItem::AutoFillSequenceItem( int _i )
{
    m_IValue = _i;
    m_Type = INTEGER;
}

AutoFillSequenceItem::AutoFillSequenceItem( double _d )
{
    m_DValue = _d;
    m_Type = FLOAT;
}

AutoFillSequenceItem::AutoFillSequenceItem( const TQString &_str )
{
    m_String = _str;
    m_Type = STRING;

    if ( month == 0L )
    {
        month = new TQStringList();
        month->append( i18n("January") );
        month->append( i18n("February") );
        month->append( i18n("March") );
        month->append( i18n("April") );
        month->append( i18n("May") );
        month->append( i18n("June") );
        month->append( i18n("July") );
        month->append( i18n("August") );
        month->append( i18n("September") );
        month->append( i18n("October") );
        month->append( i18n("November") );
        month->append( i18n("December") );
    }

    if ( shortMonth == 0L )
    {
        shortMonth = new TQStringList();
        shortMonth->append( i18n("Jan") );
        shortMonth->append( i18n("Feb") );
        shortMonth->append( i18n("Mar") );
        shortMonth->append( i18n("Apr") );
        shortMonth->append( i18n("May short", "May") );
        shortMonth->append( i18n("Jun") );
        shortMonth->append( i18n("Jul") );
        shortMonth->append( i18n("Aug") );
        shortMonth->append( i18n("Sep") );
        shortMonth->append( i18n("Oct") );
        shortMonth->append( i18n("Nov") );
        shortMonth->append( i18n("Dec") );
    }

    if ( day == 0L )
    {
        day = new TQStringList();
        day->append( i18n("Monday") );
        day->append( i18n("Tuesday") );
        day->append( i18n("Wednesday") );
        day->append( i18n("Thursday") );
        day->append( i18n("Friday") );
        day->append( i18n("Saturday") );
        day->append( i18n("Sunday") );
    }

    if ( shortDay == 0L )
    {
        shortDay = new TQStringList();
        shortDay->append( i18n("Mon") );
        shortDay->append( i18n("Tue") );
        shortDay->append( i18n("Wed") );
        shortDay->append( i18n("Thu") );
        shortDay->append( i18n("Fri") );
        shortDay->append( i18n("Sat") );
        shortDay->append( i18n("Sun") );
    }

    if( other==0L)
      {
	//	other=new TQStringList();
	TDEConfig *config = Factory::global()->config();
	config->setGroup( "Parameters" );
	other=new TQStringList(config->readListEntry("Other list"));
      }

    if ( month->find( _str ) != month->end() )
    {
        m_Type = MONTH;
        return;
    }

    if ( shortMonth->find( _str ) != shortMonth->end() )
    {
        m_Type = SHORTMONTH;
        return;
    }

    if ( day->find( _str ) != day->end() )
    {
      m_Type = DAY;
      return;
    }

    if ( shortDay->find( _str ) != shortDay->end() )
    {
      m_Type = SHORTDAY;
      return;
    }

    if( other->find(_str)!=other->end())
      {
	m_Type = OTHER;
	m_OtherBegin=0;
	m_OtherEnd=other->count();
	int index= other->findIndex(_str);
	//find end and begin of qstringlist of other.
	for ( TQStringList::Iterator it = other->find(_str); it != other->end();++it )
	  {
	    if((*it)=="\\")
	      {
	      m_OtherEnd=index;
	      break;
	      }
	    index++;
	  }
	index= other->findIndex(_str);
	for ( TQStringList::Iterator it = other->find(_str); it != other->begin();--it )
	  {
	    if((*it)=="\\")
	      {
	      m_OtherBegin=index;
	      break;
	      }
	    index--;
	  }
	return;
      }

    if ( m_String[0] == '=' )
        m_Type = FORMULA;
}

bool AutoFillSequenceItem::getDelta( AutoFillSequenceItem *seq, double &_delta )
{
    if ( seq->getType() != m_Type )
        return false;

    switch( m_Type )
    {
    case INTEGER:
        _delta = (double)( seq->getIValue() - m_IValue );
        return true;
    case FLOAT:
        _delta = seq->getDValue() - m_DValue;
        return true;
    case FORMULA:
    case STRING:
        if ( m_String == seq->getString() )
        {
            _delta = 0.0;
            return true;
        }
        return false;
    case MONTH:
        {
            int i = month->findIndex( m_String );
            int j = month->findIndex( seq->getString() );
            int k = j;

            if ( j + 1 == i )
                _delta = -1.0;
            else
                _delta = ( double )( k - i );
            return true;
        }

    case SHORTMONTH:
        {
            int i = shortMonth->findIndex( m_String );
            int j = shortMonth->findIndex( seq->getString() );
            int k = j;

            if ( j + 1 == i )
                _delta = -1.0;
            else
                _delta = ( double )( k - i );
            return true;
        }

    case DAY:
        {
            int i = day->findIndex( m_String );
            int j = day->findIndex( seq->getString() );
            int k = j;

            if ( j + 1 == i )
                _delta = -1.0;
            else
                _delta = ( double )( k - i );
            kdDebug() << m_String << " i: " << i << " j: " << j << " k: " << k << " delta: " << _delta << endl;
            return true;
        }

    case SHORTDAY:
        {
            int i = shortDay->findIndex( m_String );
            int j = shortDay->findIndex( seq->getString() );
            int k = j;

            if ( j + 1 == i )
                _delta = -1.0;
            else
                _delta = ( double )( k - i );
            return true;
        }
    case OTHER:
      {
	if( m_OtherEnd!= seq->getIOtherEnd() || m_OtherBegin!= seq->getIOtherBegin())
	  return false;
	int i = other->findIndex( m_String );
	int j = other->findIndex( seq->getString() );
	int k = j;
	if ( j < i )
	  k += (m_OtherEnd - m_OtherBegin - 1);
	/*if ( j + 1 == i )
	  _delta = -1.0;
	  else*/
	  _delta = ( double )( k - i );
	return true;
      }
     default:
      return false;
    }
}

TQString AutoFillSequenceItem::getSuccessor( int _no, double _delta )
{
    TQString erg;
    switch( m_Type )
    {
    case INTEGER:
        erg.sprintf("%i", m_IValue + _no * (int)_delta );
        break;
    case FLOAT:
        erg.sprintf("%f", m_DValue + (double)_no * _delta );
        break;
    case FORMULA:
    case STRING:
        erg = m_String;
        break;
    case MONTH:
        {
            int i = month->findIndex( m_String );
            int j = i + _no * (int) _delta;
            while (j < 0)
              j += month->count();
            int k = j % month->count();
            erg = (*month->at( k ));
        }
        break;
    case SHORTMONTH:
        {
            int i = shortMonth->findIndex( m_String );
            int j = i + _no * (int) _delta;
            while (j < 0)
              j += shortMonth->count();
            int k = j % shortMonth->count();
            erg = (*shortMonth->at( k ));
        }
        break;
    case DAY:
        {
            int i = day->findIndex( m_String );
            int j = i + _no * (int) _delta;
            while (j < 0)
              j += day->count();
            int k = j % day->count();
            erg = (*day->at( k ));
        }
	break;
    case SHORTDAY:
        {
            int i = shortDay->findIndex( m_String );
            int j = i + _no * (int) _delta;
            while (j < 0)
              j += shortDay->count();
            int k = j % shortDay->count();
            erg = (*shortDay->at( k ));
        }
        break;
    case OTHER:
      {
	 int i = other->findIndex( m_String )-(m_OtherBegin+1);
	 int j = i + _no * (int) _delta;
	 int k = j % (m_OtherEnd - m_OtherBegin-1);
	 erg = (*other->at( (k+m_OtherBegin+1) ));
      }
     case TIME:
     case DATE:
      // gets never called but fixes a warning while compiling
      break;
    }

    return TQString( erg );
}

TQString AutoFillSequenceItem::getPredecessor( int _no, double _delta )
{
  TQString erg;
  switch( m_Type )
  {
   case INTEGER:
    erg.sprintf("%i", m_IValue - _no * (int)_delta );
    break;
   case FLOAT:
    erg.sprintf("%f", m_DValue - (double)_no * _delta );
    break;
   case FORMULA:
   case STRING:
    erg = m_String;
    break;
   case MONTH:
    {
      int i = month->findIndex( m_String );
      int j = i - _no * (int) _delta;
      while ( j < 0 )
        j += month->count();
      int k = j % month->count();
      erg = (*month->at( k ));
    }
    break;
   case SHORTMONTH:
    {
      int i = shortMonth->findIndex( m_String );
      int j = i - _no * (int) _delta;
      while ( j < 0 )
        j += shortMonth->count();
      int k = j % shortMonth->count();
      erg = (*shortMonth->at( k ));
    }
    break;
   case DAY:
    {
      int i = day->findIndex( m_String );
      int j = i - _no * (int) _delta;
      while ( j < 0 )
        j += day->count();
      int k = j % day->count();
      erg = (*day->at( k ));
    }
    break;
   case SHORTDAY:
    {
      int i = shortDay->findIndex( m_String );
      int j = i - _no * (int) _delta;
      while ( j < 0 )
        j += shortDay->count();
      int k = j % shortDay->count();
      erg = (*shortDay->at( k ));
    }
    break;
   case OTHER:
    {
      int i = other->findIndex( m_String ) - (m_OtherBegin + 1);
      int j = i - _no * (int) _delta;
      while ( j < 0 )
        j += (m_OtherEnd - m_OtherBegin - 1);
      int k = j % (m_OtherEnd - m_OtherBegin - 1);
      erg = (*other->at( (k + m_OtherBegin + 1) ));
    }
   case TIME:
   case DATE:
    // gets never called but fixes a warning while compiling
    break;
  }

  return TQString( erg );
}

/**********************************************************************************
 *
 * AutoFillSequence
 *
 **********************************************************************************/

AutoFillSequence::AutoFillSequence( Cell *_cell )
{
    sequence.setAutoDelete( true );

    if ( _cell->isFormula() )
    {
        TQString d = _cell->encodeFormula();
        sequence.append( new AutoFillSequenceItem( d ) );
    }
    else if ( _cell->value().isNumber() )
    {
        if ( floor( _cell->value().asFloat() ) == _cell->value().asFloat() )
        {
            sequence.append( new AutoFillSequenceItem( (int)_cell->value().asFloat()) );
        }
        else
            sequence.append( new AutoFillSequenceItem(_cell->value().asFloat() ) );
    }
    else if ( !_cell->text().isEmpty() )
        sequence.append( new AutoFillSequenceItem( _cell->text() ) );
}

bool AutoFillSequence::matches( AutoFillSequence* _seq, AutoFillDeltaSequence *_delta )
{
    AutoFillDeltaSequence delta( this, _seq );
    if ( !delta.isOk() )
        return false;

    if ( delta.equals( _delta ) )
         return true;

    return false;
}

void AutoFillSequence::fillCell( Cell *src, Cell *dest, AutoFillDeltaSequence *delta, int _block, bool down )
{
    TQString erg = "";

    // Special handling for formulas
    if ( sequence.first() != 0L && sequence.first()->getType() == AutoFillSequenceItem::FORMULA )
    {
        TQString f = dest->decodeFormula( sequence.first()->getString() );
        dest->setCellText( f );
        dest->copyFormat( src );
        return;
    }

    AutoFillSequenceItem *item;
    int i = 0;
    if (down)
    {
      for ( item = sequence.first(); item != 0L; item = sequence.next() )
        erg += item->getSuccessor( _block, delta->getItemDelta( i++ ) );
    }
    else
    {
      for ( item = sequence.first(); item != 0L; item = sequence.next() )
        erg += item->getPredecessor( _block, delta->getItemDelta( i++ ) );
    }

    dest->setCellText( erg );
    dest->copyFormat( src );
}

/**********************************************************************************
 *
 * Sheet
 *
 **********************************************************************************/

void Sheet::autofill( TQRect &src, TQRect &dest )
{
    if (src == dest)
    {
        return;
    }

    setRegionPaintDirty( dest );

    doc()->emitBeginOperation();

    if ( !doc()->undoLocked() )
    {
      UndoAutofill *undo = new UndoAutofill( doc(), this, dest );
      doc()->addCommand( undo );
    }

    // disable the update of the max sroll range on each cell insertion
    // Bug 124806: creating series takes extremely long time
    enableScrollBarUpdates(false);

    // Fill from left to right
    if ( src.left() == dest.left() && src.right() < dest.right() )
    {
        for ( int y = src.top(); y <= src.bottom(); y++ )
        {
            int x;
            TQPtrList<Cell> destList;
            for ( x = src.right() + 1; x <= dest.right(); x++ )
                destList.append( nonDefaultCell( x, y ) );
            TQPtrList<Cell> srcList;
            for ( x = src.left(); x <= src.right(); x++ )
                srcList.append( cellAt( x, y ) );
            TQPtrList<AutoFillSequence> seqList;
            seqList.setAutoDelete( true );
            for ( x = src.left(); x <= src.right(); x++ )
                seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
            fillSequence( srcList, destList, seqList );
        }
    }

    // Fill from top to bottom
    if ( src.top() == dest.top() && src.bottom() < dest.bottom() )
    {
        for ( int x = src.left(); x <= dest.right(); x++ )
        {
            int y;
            TQPtrList<Cell> destList;
            for ( y = src.bottom() + 1; y <= dest.bottom(); y++ )
                destList.append( nonDefaultCell( x, y ) );
            TQPtrList<Cell> srcList;
            for ( y = src.top(); y <= src.bottom(); y++ )
            {
                srcList.append( cellAt( x, y ) );
            }
            TQPtrList<AutoFillSequence> seqList;
            seqList.setAutoDelete( true );
            for ( y = src.top(); y <= src.bottom(); y++ )
                seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
            fillSequence( srcList, destList, seqList );
        }
    }

    // Fill from right to left
    if ( ( src.left() == dest.right() || src.left() == dest.right() - 1) && src.right() >= dest.right() )
    {
        if ( src.left() != dest.right() )
            dest.setRight( dest.right() - 1 );

        for ( int y = dest.top(); y <= dest.bottom(); y++ )
        {
            int x;
            TQPtrList<Cell> destList;

            for ( x = dest.left(); x < src.left(); x++ )
            {
                destList.append( nonDefaultCell( x, y ) );
            }
            TQPtrList<Cell> srcList;
            for ( x = src.left(); x <= src.right(); x++ )
            {
                srcList.append( cellAt( x, y ) );
            }
            TQPtrList<AutoFillSequence> seqList;
            seqList.setAutoDelete( true );
            for ( x = src.left(); x <= src.right(); x++ )
                seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
            fillSequence( srcList, destList, seqList, false );
        }
    }

    // Fill from bottom to top
    if ( (src.top() == dest.bottom() || src.top() == (dest.bottom() - 1) ) && src.bottom() >= dest.bottom() )
    {
        if (src.top() != dest.bottom() )
            dest.setBottom(dest.bottom() - 1);
        int startVal = TQMIN( dest.left(), src.left());
        int endVal = TQMAX(src.right(), dest.right());
        for ( int x = startVal; x <= endVal; x++ )
        {
            int y;
            TQPtrList<Cell> destList;
            for ( y = dest.top(); y < src.top(); y++ )
                destList.append( nonDefaultCell( x, y ) );
            TQPtrList<Cell> srcList;
            for ( y = src.top(); y <= src.bottom(); ++y )
            {
                srcList.append( cellAt( x, y ) );
            }
            TQPtrList<AutoFillSequence> seqList;
            seqList.setAutoDelete( true );
            for ( y = src.top(); y <= src.bottom(); y++ )
                seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
            fillSequence( srcList, destList, seqList, false );
        }
    }

    // update the max sroll range ONCE here
    enableScrollBarUpdates(true);
    checkRangeHBorder(dest.right());
    checkRangeVBorder(dest.bottom());

    emit sig_updateView( this );
    // doc()->emitEndOperation();
}


void Sheet::fillSequence( TQPtrList<Cell>& _srcList,
				 TQPtrList<Cell>& _destList,
                                 TQPtrList<AutoFillSequence>& _seqList,
                                 bool down)
{
    doc()->emitBeginOperation(true);

    /* try finding an interval to use to fill the sequence */
    if (!FillSequenceWithInterval(_srcList, _destList, _seqList, down))
    {
      /* if no interval was found, just copy down through */
      FillSequenceWithCopy(_srcList, _destList, down);
    }

    doc()->emitEndOperation();

}

TQVariant getDiff( const Value& value1, const Value& value2  , AutoFillSequenceItem::Type type  )
{
  if ( type == AutoFillSequenceItem::FLOAT )
	  return TQVariant( value2.asFloat() - value1.asFloat() );
  if ( type == AutoFillSequenceItem::TIME || type == AutoFillSequenceItem::DATE )
	  return TQVariant( (int)( value2.asInteger() - value1.asInteger() ) );

  return TQVariant( (int)0 );
  // note: date and time difference can be calculated as
  // the difference of the serial number
 /* if( (type == AutoFillSequenceItem::FLOAT) ||
      (type == AutoFillSequenceItem::DATE) ||
      (type == AutoFillSequenceItem::TIME) )
    return ( value2.asFloat() - value1.asFloat() );
  else
    return 0.0;*/
}

bool Sheet::FillSequenceWithInterval(TQPtrList<Cell>& _srcList,
                                            TQPtrList<Cell>& _destList,
                                            TQPtrList<AutoFillSequence>& _seqList,
                                            bool down)
{
  if (_srcList.first()->isFormula())
    return false;

  TQPtrList<AutoFillDeltaSequence> deltaList;
  deltaList.setAutoDelete( true );
  bool ok = false;

  if ( _srcList.first()->value().isNumber() || _srcList.first()->isDate() || _srcList.first()->isTime() )
  {
    AutoFillSequenceItem::Type type;

    TQValueVector< TQVariant > tmp( _seqList.count() );  /*= new TQValueList< TQVariant > ( _seqList.count() )*/;
    TQValueVector< TQVariant > diff( _seqList.count() ); /*= new TQValueList< TQVariant > ( _seqList.count() )*/;
    int p = -1;
    int count = 0;
    int tmpcount = 0;

    Cell * cell = _srcList.first();
    Cell * cell2 = _srcList.next();

    bool singleCellOnly = (cell2 == 0);

    if ( cell->isDate() )
      type = AutoFillSequenceItem::DATE;
    else if ( cell->isTime() )
      type = AutoFillSequenceItem::TIME;
    else if ( cell->value().isNumber() )
      type = AutoFillSequenceItem::FLOAT;
    else
      return false; // Cannot happen due to if condition

    while ( cell && (cell2 || singleCellOnly) )
    {

      Value cellValue = cell->value();
      Value cell2Value;

      //If we only have a single cell, the interval will depend upon the data type.
      //- For numeric values, set the interval to 0 as we don't know what might be useful as a sequence
      //- For time values, set the interval to one hour, as this will probably be the most useful setting
      //- For date values, set the interval to one day, as this will probably be the most useful setting
      //
      //Note that the above options were chosen for consistency with Excel.  Gnumeric (1.59) sets
      //the interval to 0 for all types, OpenOffice.org (2.00) uses increments of 1.00, 1 hour and 1 day
      //respectively
      if (singleCellOnly)
      {
      	if (type == AutoFillSequenceItem::FLOAT)
		cell2Value = cellValue;
	else if ( type == AutoFillSequenceItem::TIME)
		cell2Value = Value( cellValue.asTime().addSecs( 60*60 ) );
	else if ( type == AutoFillSequenceItem::DATE)
		cell2Value = Value ( cellValue.asDate().addDays( 1 ) );
      }
      else
      {
	cell2Value = cell2->value();

      	// check if both cells contain the same type
      	if ( ( !cellValue.isNumber() )
           || ( cell2->isDate() && type != AutoFillSequenceItem::DATE )
           || ( cell2->isTime() && type != AutoFillSequenceItem::TIME ) )
      	{
        	count = 0;
        	ok = false;
       		break;
      	}
      }

      TQVariant delta = getDiff(cellValue , cell2Value , type );

      if (count < 1)
      {
        p = count;
        diff[ count++ ] = delta;
      }
      else
      {
        // same value again?
        if (diff[ p ] == delta)
        {
          // store it somewhere else for the case we need it later
          ++p;
          tmp[ tmpcount++ ] = delta;
        }
        else
        {
          // if we have saved values in another buffer we have to insert them first
          if ( tmpcount > 0 )
          {
            for ( int i = 0; i < tmpcount; ++i )
            {
              diff[ count++ ] = tmp.at( i );
            }

            tmpcount = 0;
          }

          // insert the value
          p = 0;
          diff[ count++ ] = delta;
        }
      }

      // check next cell pair
      cell  = cell2;
      cell2 = _srcList.next();
    }

    // we have found something:
    if (count > 0 && (tmpcount > 0 || count == 1))
    {
      TQVariant cellValue( (int) 0 );

      Cell * dest;
      Cell * src;

      int i = tmpcount;
      if (down)
      {
        dest = _destList.first();
        src  = _srcList.last();
      }
      else
      {
        dest = _destList.last();
        src  = _srcList.first();

        i   *= -1;
      }

      if ( type == AutoFillSequenceItem::FLOAT )
	      cellValue = src->value().asFloat();
      else
	      cellValue = (int)src->value().asInteger();

      TQString res;
      // copy all the data
      while (dest)
      {
        if (down)
        {
          while ( i >= count )
            i -= count;
        }
        else
        {
          while ( i < 0)
            i += count;
        }

	TQVariant currentDiff = diff.at( i );

	if (cellValue.type() == TQVariant::Double)
        	if (down)
            		cellValue = cellValue.asDouble() + currentDiff.asDouble();
          	else
            		cellValue = cellValue.asDouble() -  currentDiff.asDouble();
	else
		if (down)
			cellValue = cellValue.asInt() + currentDiff.asInt();
		else
			cellValue = cellValue.asInt() - currentDiff.asInt();

	if ( type == AutoFillSequenceItem::TIME)
	{
		Value timeValue = doc()->converter()->asTime( Value(cellValue.asInt()) );
		Value stringValue = doc()->converter()->asString( timeValue );
		dest->setCellText( stringValue.asString() );
	}
	else if ( type == AutoFillSequenceItem::DATE)
	{
		Value dateValue = doc()->converter()->asDate( Value(cellValue.asInt()) );
		Value stringValue = doc()->converter()->asString( dateValue );
		dest->setCellText( stringValue.asString() );
	}
	else
        	dest->setCellText( cellValue.asString() );

	dest->copyFormat( src );

        if (down)
        {
          ++i;
          dest = _destList.next();
          src = _srcList.next();
        }
        else
        {
          --i;
          dest = _destList.prev();
          src = _srcList.prev();
        }

        if (!src)
          src = _srcList.last();
      }

      ok = true;
    }
    else
    {
      ok = false;
    }

   // delete tmp;
   // delete diff;

    return ok;
  }

  // What is the interval (block)? If your sheet looks like this:
  // 1 3 5 7 9
  // then the interval has the length 1 and the delta list is [2].
  // 2 200 3 300 4 400
  // Here the interval has length 2 and the delta list is [1,100]


  // How big is the interval. It is in the range from [2...n/2].
  // The case of an interval of length n is handled below.
  //
  // We try to find the shortest interval.
  for ( unsigned int step = 1; step <= _seqList.count() / 2; step++ )
  {
    kdDebug() << "Looking for interval: " << step << " seqList count: " << _seqList.count() << endl;
    // If the interval is of length 'step' then the _seqList size must
    // be a multiple of 'step'
    if ( _seqList.count() % step == 0 )
    {
      // Be optimistic
      ok = true;

      deltaList.clear();

      // Guess the delta by looking at cells 0...2*step-1
      //
      // Since the interval may be of length 'step' we calculate the delta
      // between cells 0 and step, 1 and step+1, ...., step-1 and 2*step-1
      for ( unsigned int t = 0; t < step; t++ )
      {
	deltaList.append( new AutoFillDeltaSequence( _seqList.at(t),
						     _seqList.at(t+step) ) );
	ok = deltaList.getLast()->isOk();
      }

      /* Verify the delta by looking at cells step..._seqList.count()
	 We only looked at the cells 0 ... '2*step-1'.
	 Now test wether the cells from "(tst-1) * step + s" share the same delta
	 with the cell "tst * step + s" for all test=1..._seqList.count()/step
	 and for all s=0...step-1.
      */
      for ( unsigned int tst = 1; ok && ( tst * step < _seqList.count() );
	    tst++ )
      {
	for ( unsigned int s = 0; ok && ( s < step ); s++ )
	{
	  if ( !_seqList.at( (tst-1) * step + s )->
	       matches( _seqList.at( tst * step + s ), deltaList.at( s ) ) )
	    ok = false;
	}
      }
      // Did we find a valid interval ?
      if ( ok )
      {
	unsigned int s = 0;
	// Amount of intervals (blocks)
	int block = _seqList.count() / step;

	// Start iterating with the first cell
	Cell * cell;
        if (down)
          cell = _destList.first();
        else
        {
          cell = _destList.last();
          block -= (_seqList.count() - 1);
        }


	// Loop over all destination cells
	while ( cell )
	{
          kdDebug() << "Valid interval, cell: " << cell->row() << " block: " << block << endl;

	  // End of block? -> start again from beginning
          if (down)
          {
            if ( s == step )
            {
              ++block;
              s = 0;
            }
          }
          else
          {
            if ( s >= step )
            {
              s = step - 1;
              ++block;
            }
          }

          kdDebug() << "Step: " << step << " S: " << s << " Block " << block
                    << " SeqList: " << _seqList.count()
                    << " SrcList: " << _srcList.count() << " DeltaList: " << deltaList.count()
                    << endl;

	  // Set the value of 'cell' by adding 'block' times the delta tp the
	  // value of cell 's'.
	  _seqList.at( s )->fillCell( _srcList.at( s ), cell,
				      deltaList.at( s ), block, down );

          if (down)
          {
            // Next cell
            cell = _destList.next();
            ++s;
          }
          else
          {
            // Previous cell
            cell = _destList.prev();
            --s;
          }
	}
      }
    }
  }
  return ok;
}

void Sheet::FillSequenceWithCopy(TQPtrList<Cell>& _srcList,
                                        TQPtrList<Cell>& _destList,
                                        bool down)
{
  // We did not find any valid interval. So just copy over the marked
  // area.
  Cell * cell;

  if (down)
    cell = _destList.first();
  else
    cell = _destList.last();
  int incr = 1;
  unsigned int s = 0;
  double factor = 1;

  if (!down)
    s = _srcList.count() - 1;

  if ( _srcList.at( s )->value().isNumber() &&
       !(_srcList.at( s )->isDate() || _srcList.at( s )->isTime() ) )
    factor = _srcList.at( s )->value().asFloat();

  while ( cell )
  {
    if (down)
    {
      if ( s == _srcList.count() )
        s = 0;
    }
    else
    {
      if ( s >= _srcList.count() )
        s = _srcList.count() - 1;
    }

    if ( !_srcList.at( s )->text().isEmpty() )
    {
      if ( _srcList.at( s )->isFormula() )
      {
        TQString d = _srcList.at( s )->encodeFormula();
        cell->setCellText( cell->decodeFormula( d ) );
      }
      else if(_srcList.at( s )->value().isNumber() && _srcList.count()==1)
      {
        double val;
        int format_type = _srcList.at( s )->formatType();
        if ( format_type == Percentage_format )
        {
          factor = 0.01;  // one percent
        }
        else if ( _srcList.at( s )->isTime() )
        {
          // FIXME this is a workaround to avoid those nasty one minute off
          //       "dragging down" time is inaccurate overa large lists!
          //       This is the best approximation I could find (raphael)
          if (down)
          {
//             factor = 1.000002/24.  + 0.000000001;
            factor = 0.041666751;
          }
          else
          {  //when dragging "up" the error must of course be the other way round
            factor = 0.0416665;
          }
        }

        if (!down)
          val = (_srcList.at( s )->value().asFloat() - (incr * factor));
        else
          val = (_srcList.at( s )->value().asFloat() + (incr * factor));

        TQString tmp;
        tmp = tmp.setNum(val);
        cell->setCellText( tmp );
        ++incr;
      }
      else if((AutoFillSequenceItem::month != 0L)
	      && AutoFillSequenceItem::month->find( _srcList.at( s )->text()) != 0L
	      && AutoFillSequenceItem::month->find( _srcList.at( s )->text()) != AutoFillSequenceItem::month->end()
	      && _srcList.count() == 1)
      {
	TQString strMonth=_srcList.at( s )->text();
	int i = AutoFillSequenceItem::month->findIndex( strMonth )+incr;
	int k = (i) % AutoFillSequenceItem::month->count();
	cell->setCellText((*AutoFillSequenceItem::month->at( k )));
        incr++;
      }
      else if(AutoFillSequenceItem::day != 0L
	      && AutoFillSequenceItem::day->find( _srcList.at( s )->text()) != 0L
	      && AutoFillSequenceItem::day->find( _srcList.at( s )->text())
	         != AutoFillSequenceItem::day->end()
	      && _srcList.count()==1)
      {
	TQString strDay=_srcList.at( s )->text();
	int i = AutoFillSequenceItem::day->findIndex( strDay )+incr;
	int k = (i) % AutoFillSequenceItem::day->count();
	cell->setCellText((*AutoFillSequenceItem::day->at( k )));
        incr++;
      }
      else
      {
	TQRegExp number("(\\d+)");
	int pos =number.search(_srcList.at( s )->text());
	if( pos!=-1 )
	{
	  TQString tmp=number.cap(1);
	  int num=tmp.toInt()+incr;
	  cell->setCellText(_srcList.at( s )->text().replace(number,TQString::number(num)));
          ++incr;
	}
	else if ( !_srcList.at( s )->link().isEmpty() )
        {
	  cell->setCellText( _srcList.at( s )->text() );
	  cell->setLink( _srcList.at( s )->link() );
        }
        else
        {
	  cell->setCellText( _srcList.at( s )->text() );
        }
      }
    }
    else
      cell->setCellText( "" );

    cell->copyFormat( _srcList.at( s ) );

    if (down)
    {
      cell = _destList.next();
      ++s;
    }
    else
    {
      cell = _destList.prev();
      --s;
    }
  }
  return;
}
