/*
 *  Copyright (c) 2002-2003 Jesper K. Pedersen <blackie@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 version 2 as published by the Free Software Foundation.
 *
 *  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.
 **/

#ifdef TQT_ONLY
  #include "compat.h"
#else
  #include <tdelocale.h>
  #include <kiconloader.h>
  #include <tdestandarddirs.h>
  #include <tdemessagebox.h>
  #include "kregexpeditorprivate.moc"
#endif

#include <tqlineedit.h>
#include <tqtooltip.h>
#include <tqtoolbutton.h>
#include "kregexpeditorprivate.h"
#include "scrollededitorwindow.h"
#include "regexpbuttons.h"
//#include <unistd.h> // What do I need this for?
#include <stdio.h>
#include "infopage.h"
#include <tqsplitter.h>
#include <tqdockarea.h>
#include "userdefinedregexps.h"
#include "auxbuttons.h"
#include <tqaccel.h>
#include <tqtimer.h>
#include "verifier.h"
#include <tqfile.h>
#include "verifybuttons.h"
#include <tqwhatsthis.h>

KRegExpEditorPrivate::KRegExpEditorPrivate(TQWidget *parent, const char *name)
    : TQWidget(parent, name), _updating( false ), _autoVerify( true )
{
  setMinimumSize(730,300);
  TQDockArea* area = new TQDockArea(TQt::Horizontal, TQDockArea::Normal, this );
  area->setMinimumSize(2,2);
  TQDockArea* verArea1 = new TQDockArea(TQt::Vertical, TQDockArea::Normal, this );
  verArea1->setMinimumSize(2,2);
  TQDockArea* verArea2 = new TQDockArea(TQt::Vertical, TQDockArea::Reverse, this );
  verArea2->setMinimumSize(2,2);

  // The DockWindows.
  _regExpButtons = new RegExpButtons( area, "KRegExpEditorPrivate::regExpButton" );
  _verifyButtons = new VerifyButtons( area, "KRegExpEditorPrivate::VerifyButtons" );
  _auxButtons = new AuxButtons( area, "KRegExpEditorPrivate::AuxButtons" );
  _userRegExps = new UserDefinedRegExps( verArea1, "KRegExpEditorPrivate::userRegExps" );
  _userRegExps->setResizeEnabled( true );
  TQWhatsThis::add( _userRegExps, i18n( "In this window you will find predefined regular expressions. Both regular expressions "
                                       "you have developed and saved, and regular expressions shipped with the system." ));

  // Editor window
  _editor = new TQSplitter(TQt::Vertical, this, "KRegExpEditorPrivate::_editor" );

  _scrolledEditorWindow =
    new RegExpScrolledEditorWindow( _editor, "KRegExpEditorPrivate::_scrolledEditorWindow" );
  TQWhatsThis::add( _scrolledEditorWindow, i18n( "In this window you will develop your regular expressions. "
                                               "Select one of the actions from the action buttons above, and click the mouse in this "
                                               "window to insert the given action."));

  _info = new InfoPage( this, "_info" );
  _verifier = new Verifier( _editor, "KRegExpEditorPrivate::_verifier" );
  connect( _verifier, TQ_SIGNAL( textChanged() ), this, TQ_SLOT( maybeVerify() ) );
  TQWhatsThis::add( _verifier, i18n("Type in some text in this window, and see what the regular expression you have developed matches.<p>"
                                   "Each second match will be colored in red and each other match will be colored blue, simply so you "
                                   "can distinguish them from each other.<p>"
                                   "If you select part of the regular expression in the editor window, then this part will be "
                                   "highlighted - This allows you to <i>debug</i> your regular expressions") );

  _editor->hide();
  _editor->setSizes( TQValueList<int>() << _editor->height()/2 << _editor->height()/2 );

  TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, 6, "KRegExpEditorPrivate::topLayout" );
  topLayout->addWidget( area );
  TQHBoxLayout* rows = new TQHBoxLayout; // I need to cal addLayout explicit to get stretching right.
  topLayout->addLayout( rows, 1 );

  rows->addWidget( verArea1 );
  rows->addWidget( _editor, 1 );
  rows->addWidget( _info, 1 );
  rows->addWidget( verArea2 );

  // Connect the buttons
  connect( _regExpButtons, TQ_SIGNAL( clicked( int ) ),   _scrolledEditorWindow, TQ_SLOT( slotInsertRegExp( int ) ) );
  connect( _regExpButtons, TQ_SIGNAL( doSelect() ), _scrolledEditorWindow, TQ_SLOT( slotDoSelect() ) );
  connect( _userRegExps, TQ_SIGNAL( load( RegExp* ) ),    _scrolledEditorWindow, TQ_SLOT( slotInsertRegExp( RegExp*  ) ) );

  connect( _regExpButtons, TQ_SIGNAL( clicked( int ) ), _userRegExps,   TQ_SLOT( slotUnSelect() ) );
  connect( _regExpButtons, TQ_SIGNAL( doSelect() ),     _userRegExps,   TQ_SLOT( slotUnSelect() ) );
  connect( _userRegExps, TQ_SIGNAL( load( RegExp* ) ),  _regExpButtons, TQ_SLOT( slotUnSelect() ) );

  connect( _scrolledEditorWindow, TQ_SIGNAL( doneEditing() ), _regExpButtons, TQ_SLOT( slotSelectNewAction() ) );
  connect( _scrolledEditorWindow, TQ_SIGNAL( doneEditing() ), _userRegExps, TQ_SLOT( slotSelectNewAction() ) );

  connect( _regExpButtons, TQ_SIGNAL( clicked( int ) ), this, TQ_SLOT( slotShowEditor() ) );
  connect( _userRegExps, TQ_SIGNAL( load( RegExp* ) ), this, TQ_SLOT( slotShowEditor() ) );
  connect( _regExpButtons, TQ_SIGNAL( doSelect() ), this, TQ_SLOT( slotShowEditor() ) );

  connect( _scrolledEditorWindow, TQ_SIGNAL( savedRegexp() ), _userRegExps, TQ_SLOT( slotPopulateUserRegexps() ) );

  connect( _auxButtons, TQ_SIGNAL( undo() ), this, TQ_SLOT( slotUndo() ) );
  connect( _auxButtons, TQ_SIGNAL( redo() ), this, TQ_SLOT( slotRedo() ) );
  connect( _auxButtons, TQ_SIGNAL( cut() ), _scrolledEditorWindow, TQ_SLOT( slotCut() ) );
  connect( _auxButtons, TQ_SIGNAL( copy() ), _scrolledEditorWindow, TQ_SLOT( slotCopy() ) );
  connect( _auxButtons, TQ_SIGNAL( paste() ), _scrolledEditorWindow, TQ_SLOT( slotPaste() ) );
  connect( _auxButtons, TQ_SIGNAL( save() ), _scrolledEditorWindow, TQ_SLOT( slotSave() ) );
  connect( _verifyButtons, TQ_SIGNAL( autoVerify( bool ) ), this, TQ_SLOT( setAutoVerify( bool ) ) );
  connect( _verifyButtons, TQ_SIGNAL( verify() ), this, TQ_SLOT( doVerify() ) );
  connect( _verifyButtons, TQ_SIGNAL( changeSyntax( const TQString& ) ), this, TQ_SLOT( setSyntax( const TQString& ) ) );

  connect( this, TQ_SIGNAL( canUndo( bool ) ), _auxButtons, TQ_SLOT( slotCanUndo( bool ) ) );
  connect( this, TQ_SIGNAL( canRedo( bool ) ), _auxButtons, TQ_SLOT( slotCanRedo( bool ) ) );
  connect( _scrolledEditorWindow, TQ_SIGNAL( anythingSelected( bool ) ), _auxButtons, TQ_SLOT( slotCanCut( bool ) ) );
  connect( _scrolledEditorWindow, TQ_SIGNAL( anythingSelected( bool ) ), _auxButtons, TQ_SLOT( slotCanCopy( bool ) ) );
  connect( _scrolledEditorWindow, TQ_SIGNAL( anythingOnClipboard( bool ) ), _auxButtons, TQ_SLOT( slotCanPaste( bool ) ) );
  connect( _scrolledEditorWindow, TQ_SIGNAL( canSave( bool ) ), _auxButtons, TQ_SLOT( slotCanSave( bool ) ) );

  connect( _scrolledEditorWindow, TQ_SIGNAL( verifyRegExp() ), this, TQ_SLOT( maybeVerify() ) );

  connect( _verifyButtons, TQ_SIGNAL( loadVerifyText( const TQString& ) ), this, TQ_SLOT( setVerifyText( const TQString& ) ) );

  // connect( _verifier, TQ_SIGNAL( countChanged( int ) ), _verifyButtons, TQ_SLOT( setMatchCount( int ) ) );

  // TQt anchors do not work for <pre>...</pre>, thefore scrolling to next/prev match
  // do not work. Enable this when they work.
  // connect( _verifyButtons, TQ_SIGNAL( gotoFirst() ), _verifier, TQ_SLOT( gotoFirst() ) );
  // connect( _verifyButtons, TQ_SIGNAL( gotoPrev() ), _verifier, TQ_SLOT( gotoPrev() ) );
  // connect( _verifyButtons, TQ_SIGNAL( gotoNext() ), _verifier, TQ_SLOT( gotoNext() ) );
  // connect( _verifyButtons, TQ_SIGNAL( gotoLast() ), _verifier, TQ_SLOT( gotoLast() ) );
  // connect( _verifier, TQ_SIGNAL( goForwardPossible( bool ) ), _verifyButtons, TQ_SLOT( enableForwardButtons( bool ) ) );
  // connect( _verifier, TQ_SIGNAL( goBackwardPossible( bool ) ), _verifyButtons, TQ_SLOT( enableBackwardButtons( bool ) ) );

  _auxButtons->slotCanPaste( false );
  _auxButtons->slotCanCut( false );
  _auxButtons->slotCanCopy( false );
  _auxButtons->slotCanSave( false );


  // Line Edit
  TQHBoxLayout* layout = new TQHBoxLayout( topLayout, 6 );
  TQLabel* label = new TQLabel( i18n("ASCII syntax:"), this );
  layout->addWidget( label );
  clearButton = new TQToolButton( this );
  const TQString icon( TQString::fromLatin1( TQApplication::reverseLayout() ? "clear_left" : "locationbar_erase" ) );
  TQIconSet clearIcon = SmallIconSet( icon );
  clearButton->setIconSet( clearIcon );
  layout->addWidget( clearButton );
  TQToolTip::add( clearButton, i18n("Clear expression") );
  _regexpEdit = new TQLineEdit( this );
  layout->addWidget( _regexpEdit );
  TQWhatsThis::add( _regexpEdit, i18n( "This is the regular expression in ASCII syntax. You are likely only "
				      "to be interested in this if you are a programmer, and need to "
				      "develop a regular expression using TQRegExp.<p>"
                                      "You may develop your regular expression both by using the graphical "
				      "editor, and by typing the regular expression in this line edit.") );

#ifdef TQT_ONLY
  TQPixmap pix( "icons/error.png" );
#else
  TQPixmap pix = TDEGlobal::iconLoader()->loadIcon(locate("data", TQString::fromLatin1("kregexpeditor/pics/error.png") ), TDEIcon::Toolbar );
#endif
  _error = new TQLabel( this );
  _error->setPixmap( pix );
  layout->addWidget( _error );
  _error->hide();

  _timer = new TQTimer(this);

  connect( _scrolledEditorWindow, TQ_SIGNAL( change() ), this, TQ_SLOT( slotUpdateLineEdit() ) );
  connect( _regexpEdit, TQ_SIGNAL(textChanged( const TQString& ) ), this, TQ_SLOT( slotTriggerUpdate() ) );
  connect( _timer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( slotTimeout() ) );
  connect( clearButton, TQ_SIGNAL( clicked() ), _regexpEdit, TQ_SLOT( clear() ) );

  // Push an initial empty element on the stack.
  _undoStack.push( _scrolledEditorWindow->regExp() );
  _redoStack.setAutoDelete( true );

  TQAccel* accel = new TQAccel( this );
  accel->connectItem( accel->insertItem( CTRL+Key_Z ), this, TQ_SLOT( slotUndo() ) );
  accel->connectItem( accel->insertItem( CTRL+Key_R ), this, TQ_SLOT( slotRedo() ) );

  setSyntax( TQString::fromLatin1( "TQt" ) );
}

TQString KRegExpEditorPrivate::regexp()
{
  RegExp* regexp = _scrolledEditorWindow->regExp();
  TQString res = RegExpConverter::current()->toStr( regexp, false );
  delete regexp;
  return res;
}

void KRegExpEditorPrivate::slotUpdateEditor( const TQString & txt)
{
  _updating = true;
  bool ok;
  if ( !RegExpConverter::current()->canParse() ) {
      // This can happend if the application set a text through the API.
  }
  else {
      RegExp* result = RegExpConverter::current()->parse( txt, &ok );
      if ( ok ) {
          TQPtrList<CompoundRegExp> list = _userRegExps->regExps();
          for ( TQPtrListIterator<CompoundRegExp> it( list ); *it; ++it ) {
              result->replacePart( *it );
          }

          _scrolledEditorWindow->slotSetRegExp( result );
          _error->hide();
          maybeVerify( );
          recordUndoInfo();
          result->check( _errorMap );
      }
      else {
          _error->show();
          if ( _autoVerify )
              _verifier->clearRegexp();
      }
      delete result;
  }
  _updating = false;
}

void KRegExpEditorPrivate::slotUpdateLineEdit()
{
  if ( _updating )
    return;
  _updating = true;

  RegExp* regexp = _scrolledEditorWindow->regExp();
  regexp->check( _errorMap );

  TQString str = RegExpConverter::current()->toStr( regexp, false );
  _regexpEdit->setText( str );
  delete regexp;

  recordUndoInfo();

  _updating = false;
}

void KRegExpEditorPrivate::recordUndoInfo()
{
  Q_ASSERT( _updating );

  // Update undo/redo stacks
  RegExp* regexp = _scrolledEditorWindow->regExp();
  if ( regexp->toXmlString() != _undoStack.top()->toXmlString() ) {
    _undoStack.push( regexp );
    _redoStack = TQPtrStack<RegExp>();
    emitUndoRedoSignals();
  }
}

void KRegExpEditorPrivate::slotRedo()
{
  if ( _redoStack.count() != 0 ) {
    _undoStack.push(_redoStack.pop());
    _scrolledEditorWindow->slotSetRegExp( _undoStack.top() );
    slotUpdateLineEdit();
    emitUndoRedoSignals();
    maybeVerify();
  }
}

void KRegExpEditorPrivate::slotUndo()
{
  if ( _undoStack.count() > 1 ) {
    _redoStack.push(_undoStack.pop());
    _scrolledEditorWindow->slotSetRegExp( _undoStack.top() );
    slotUpdateLineEdit();
    emitUndoRedoSignals();
    maybeVerify();
  }
}

void KRegExpEditorPrivate::slotShowEditor()
{
    _info->hide();
    _editor->show();
}

void KRegExpEditorPrivate::emitUndoRedoSignals()
{
  emit canUndo( _undoStack.count() > 1 );
  emit changes( _undoStack.count() > 1 );
  emit canRedo( _redoStack.count() > 0 );
}

void KRegExpEditorPrivate::slotSetRegexp( TQString regexp )
{
  _regexpEdit->setText( regexp );
}

void KRegExpEditorPrivate::slotTriggerUpdate()
{
  /* ### Guess this timeout value should be configurable somewhere, or (even
   * better: do some kind of benchmark each time the editor view gets updated
   * to measure how long it takes on the client system to render the editor
   * with the current complexity. That way we'd get good response times for
   * simple regexps, and flicker-free display for complex regexps.
   * - Frerich
   */
  if ( !_updating ) {
    _timer->start( 300, true );
    slotShowEditor();
  }
}

void KRegExpEditorPrivate::slotTimeout()
{
  slotUpdateEditor( _regexpEdit->text() );
}

void KRegExpEditorPrivate::setMatchText( const TQString& text )
{
    bool autoVerify = _autoVerify;
    _autoVerify = false;
    _verifier->setText( text );
    _autoVerify = autoVerify;
}

void KRegExpEditorPrivate::maybeVerify()
{
    if ( _autoVerify )
        doVerify();
    else
        _verifyButtons->setMatchCount(-1);
}

void KRegExpEditorPrivate::doVerify()
{
    bool autoVerify = _autoVerify; // prevent loop due to verify emit changed, which calls maybeVerify
    _autoVerify = false;
    RegExp* regexp = _scrolledEditorWindow->regExp();

    _verifier->verify( RegExpConverter::current()->toStr( regexp, true ) );
    delete regexp;
    _autoVerify = autoVerify;
}

void KRegExpEditorPrivate::setAutoVerify( bool on )
{
    _autoVerify = on;
    if ( !_autoVerify )
        _verifier->clearRegexp();
    else
        doVerify();
}

void KRegExpEditorPrivate::setVerifyText( const TQString& fileName )
{
    bool autoVerify = _autoVerify;
    _autoVerify = false;
    TQFile file( fileName );
    if ( !file.open( IO_ReadOnly ) ) {
        KMessageBox::sorry(0, i18n("Could not open file '%1' for reading").arg( fileName ) );
    }
    else {
        TQTextStream s( &file );
        TQString txt = s.read();
        file.close();
        RegExp* regexp = _scrolledEditorWindow->regExp();
        _verifier->setText( txt );
        _verifier->verify( RegExpConverter::current()->toStr( regexp, true ) );
        delete regexp;
    }
    _autoVerify = autoVerify;
}

void KRegExpEditorPrivate::setCaseSensitive( bool b )
{
    _verifier->setCaseSensitive( b );
}

void KRegExpEditorPrivate::setMinimal( bool b )
{
    _verifier->setMinimal( b );
}

void KRegExpEditorPrivate::setSyntax( const TQString& syntax )
{
    RegExpConverter* converter = _verifyButtons->setSyntax( syntax );
    RegExpConverter::setCurrent( converter );
    if ( converter->canParse() ) {
        _regexpEdit->setReadOnly( false );
        _regexpEdit->setBackgroundMode( TQt::PaletteBase );
    }
    else {
        _regexpEdit->setReadOnly( true );
        _regexpEdit->setBackgroundMode( TQt::PaletteBackground );
    }
    _regExpButtons->setFeatures( converter->features() );
    _verifier->setHighlighter( converter->highlighter(_verifier) );
    slotUpdateLineEdit();
}

void KRegExpEditorPrivate::showHelp()
{
    _info->show();
    _editor->hide();
}

void KRegExpEditorPrivate::setAllowNonTQtSyntax( bool b )
{
    _verifyButtons->setAllowNonTQtSyntax( b );
}
