/* Copyright (C) 2002 Philippe Fremy <pfremy@kde.org>

   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.

   This program 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
    General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/


#ifndef VIMWIDGET_H
#define VIMWIDGET_H

#include <qxembed.h>
#include <qtimer.h>
#include <qstringlist.h>

#include <kprocess.h>

class QMultiLineEdit;

class VimWidget: public QXEmbed
{
    Q_OBJECT

public:
    /** You can enable the menu bar and the tool bar inside the embedded vim,
      * but it looks ugly (in my opinion) */
    VimWidget( QWidget *parent=0, const char *name=0, WFlags f = 0 );
    ~VimWidget();


    /** Open a local file. There is no way to tell if the opening was
      * successful but if the file is accessible and readable, there is
	  * no reason why it could fail. The current buffer is saved and 
	  * removed. */
    void openFile( QString filename );

    /** Save the current file. 
      * There is no way to tell if the operation was successful */
    void saveFile();

    /** Set vim in read-only or read-write mode. */
    void setReadWrite( bool rw );

    /** Fetch readwrite value from vim.*/
    bool isReadWrite();

    /** Send a command to vim, puttting vim in normal mode first (whatever the
      * current mode was). */
    void sendNormalCmd( const QString & cmd );

    /** send a command to vim, puttting vim in insert mode first (whatever the
      * current mode was). Leave vim in insert mode if leaveInInsertMode is
	  * true */
    void sendInsertCmd( const QString & cmd, bool leaveInInsertMode = true );

    /** send a command to vim, puttting vim in command-line mode first
      * (whatever the current mode was). Command-line is when you press ':'
      * in normal mode. The funcion automatically prepends with ':' and
      * appends the newline necessary to make vim process the command line. 
      * Ex : sendCommandLine("% s/hop/bof/g") to make vim replace all strings
      * "hop" by "bof".*/
    void sendCmdLineCmd( const QString & cmd );

    /** Convenience function. */
    void setVimVariable(QString var, QString value = "") 
        { sendCmdLineCmd("set " + var + " " + value ); }

    /** send a command to vim, without changing the mode. The result could
      * differ whether you are in normal mode or in insert mode. Therefore,
	  * you should prever the previous functions. 
	  */
    void sendRawCmd( const QString & cmd );


    /** Evaluate an expression in vim. Do ":help expr" in vim for more 
      * details about expressions.
      * If the returned string is empty, vim has not understood your
      * expression (vim is not very verbose).
      *
      * Remember that :
      * - vim options must be preceded by a '&'
      * - strings must be inside single or double quotes
      * - vim internal variable must be preceded by 'b:', 'w:', 'g:', 'l:'
      * , 'a:' * or 'v:'. See help about "internal-variable" for more 
      * details.
      * - environment must be preceded by '$'
      * - register must by preceded by '@'
      *
      * Examples: 
      *     evalExpr( "&tabstop" ) 		-> width of a tab
      *     evalExpr( "v:lang" )   		-> current vim language
      *     evalExpr( "mode()" )   		-> current editing mode
      */
    QString evalExpr( QString expr );

	/** returns text at line n, or an empty string if there is no line n*/
	QString textline( int n ) { return evalExpr( QString("getline(%1)").arg(n) ); }

	/** Returns the current buffer's content */
	QString text();

	/** Set the current buffer's content */
	void setText( QString s );

	void insertText( uint line, uint col, QString text );

	/** Cursor column */
	int cursorCol() { return evalExpr( "col('.')" ).toInt(); }
	/** Cursor raw */
	int cursorRaw() { return evalExpr( "line('.')" ).toInt(); }

	/** Set cursor position */
	void setCursorPos( int raw, int col );

public slots:
    // Just for fun
    void someTests();

protected:
   void processCmd(QString newCmd = QString::null ); 
   void sendCmd(QString cmd);
   void startVim( QString serverName, QString fileName );
   bool setExecutable();

protected slots:
   
    void embedVimWid( int wid );
    void vimRealised();

	void testProcessExited(KProcess *);
	void testTimerFinished();

    void vimProcessStdout( KProcess * proc, char * buf, int buflen );
    void vimProcessStderr( KProcess * proc, char * buf, int buflen );
    void vimProcessExited( KProcess * proc);

    void cmdProcessOutput( KProcess * proc, char * buf, int buflen );
    void cmdProcessExited( KProcess * proc);

    void exprProcessStdout( KProcess * proc, char * buf, int buflen );
    void exprProcessStderr( KProcess * proc, char * buf, int buflen );
    void exprProcessExited( KProcess * proc);

signals:

    void vimReady();

    // internal use only
    void embeddedId( int wid );


protected:
    /** Wait until vim is closed */
    void closeVim();
    virtual bool close( bool alsoDelete );
	void fallbackMode();

	bool		_beingClosed;
    bool        _cmdRunning;
	bool		_fallbackMode;
    QString     _cmdProcOutput;
    QString     _exprProcOutput;
    QString     _exprProcErr;
    QStringList _pendingCmd;
    QString     _fileName;
    QString     _serverName;
	QString		_vimExecutable;
    bool        _vimReady;
    bool        _readWrite;
    KProcess *  _vimProcess;
	QMultiLineEdit * _mle;
	int			counter;
};

#endif // VIMWIDGET_H
