/* 
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>

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

#include <kgenericfactory.h>
#include <kmessagebox.h>
#include <qlayout.h>

#include "SpellPlugin.h"
#include "SpellPlugin.moc"

K_EXPORT_COMPONENT_FACTORY( katespellplugin, KGenericFactory<SpellPlugin>( "katespell" ) );
     
  class PluginView : public KXMLGUIClient
{             
  friend class SpellPlugin;

  public:
    Kate::MainWindow *win;
};

SpellConfigPage::SpellConfigPage( QWidget* parent, KSpellConfig* config )
	: Kate::PluginConfigPage( parent)
{
	QVBoxLayout* l = new QVBoxLayout( this );
	l->addWidget( new KSpellConfig( this, 0L, config, false ) );
}

SpellPlugin::SpellPlugin( QObject* parent, const char* name, const QStringList& )
	: Kate::Plugin ( (Kate::Application *)parent, name )
	, m_currentDoc( 0L )
	, m_kspell( 0L )
	, m_kspellConfig( new KSpellConfig() )
	, m_mispellCount( 0 )
	, m_replaceCount( 0 )
{
}

SpellPlugin::~SpellPlugin()
{
	if( m_kspell ) {
		m_kspell->setAutoDelete(true);
		m_kspell->cleanUp(); // need a way to wait for this to complete
	}
	
	delete m_kspellConfig;
}

void SpellPlugin::addView(Kate::MainWindow *win)
{
    // TODO: doesn't this have to be deleted?
    PluginView *view = new PluginView ();
                        	KStdAction::spelling( this, SLOT(spellcheck()), view->actionCollection() );
	
    view->setInstance (new KInstance("kate"));
    view->setXMLFile("plugins/katespell/ui.rc");
    win->guiFactory()->addClient (view);
    view->win = win; 
    
   m_views.append (view);
}   

void SpellPlugin::removeView(Kate::MainWindow *win)
{
  for (uint z=0; z < m_views.count(); z++)
    if (m_views.at(z)->win == win)
    {
      PluginView *view = m_views.at(z);
      m_views.remove (view);
      win->guiFactory()->removeClient (view);
      delete view;
    }  
}

//  Spellchecking methods

void SpellPlugin::spellcheck()
{
	m_currentDoc = application()->documentManager()->activeDocument();
	
	if( !m_currentDoc->isReadWrite() )
		return;
	
	m_kspell = new KSpell( 0, i18n("Spellcheck"),
	                       this, SLOT(ready()), m_kspellConfig );
	
	connect( m_kspell, SIGNAL(death()),
	         this, SLOT(spellCleanDone()) );
	
	connect( m_kspell, SIGNAL(misspelling(const QString&, const QStringList&, unsigned int)),
	         this, SLOT(misspelling(const QString&, const QStringList&, unsigned int)) );
	connect( m_kspell, SIGNAL(corrected(const QString&, const QString&, unsigned int)),
	         this, SLOT(corrected(const QString&, const QString&, unsigned int)) );
	connect( m_kspell, SIGNAL(done(const QString&)),
	         this, SLOT(spellResult(const QString&)) );
}

void SpellPlugin::ready()
{
	m_currentDoc->setReadWrite( false );
	
	m_mispellCount = 0;
	m_replaceCount = 0;
	
	m_kspell->setProgressResolution( 1 );
	
	m_kspell->check( m_currentDoc->text() );
}

void SpellPlugin::locatePosition( uint pos, uint& line, uint& col )
{
	uint cnt = 0;
	
	line = col = 0;
	
	// Find pos  -- CHANGEME: store the last found pos's cursor
	//   and do these searched relative to that to
	//   (significantly) increase the speed of the spellcheck
	for( ; line < m_currentDoc->numLines() && cnt <= pos; line++ )
		cnt += m_currentDoc->lineLength(line) + 1;
	
	line--;
	col = pos - (cnt - m_currentDoc->lineLength(line)) + 1;
}

void SpellPlugin::misspelling( const QString& origword, const QStringList&, uint pos )
{
	m_mispellCount++;
	
	uint line, col;
	
	locatePosition( pos, line, col );
	
	m_currentDoc->setSelection( line, col, line, col + origword.length() );
}

void SpellPlugin::corrected( const QString& originalword, const QString& newword, uint pos )
{
	m_replaceCount++;
	
	uint line, col;
	
	locatePosition( pos, line, col );
	
	m_currentDoc->removeText( line, col, line, col + originalword.length() );
	m_currentDoc->insertText( line, col, newword );
}

void SpellPlugin::spellResult( const QString& )
{
	m_currentDoc->clearSelection();
	m_currentDoc->setReadWrite( true );
	m_kspell->cleanUp();
}

void SpellPlugin::spellCleanDone()
{
	KSpell::spellStatus status = m_kspell->status();
	
	if( status == KSpell::Error ) {
		KMessageBox::sorry( 0,
		  i18n("ISpell could not be started. "
		       "Please make sure you have ISpell "
		       "properly configured and in your PATH."));
	} else if( status == KSpell::Crashed ) {
		m_currentDoc->setReadWrite( true );
		KMessageBox::sorry( 0,
		  i18n("ISpell seems to have crashed."));
	}
	
	delete m_kspell;
	m_kspell = 0;
}
