/******************************************************************************
 JXSpellChecker.cc

	<Description>

	BASE CLASS = <NONE>

	Copyright © 1997 by Glenn W. Bach.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

#include <JXSpellChecker.h>
#include <JXSpellCheckerDialog.h>

#include <JXWindow.h>
#include <JXWindowDirector.h>

#include <JXTEBase.h>
#include <JProcess.h>
#include <JInPipeStream.h>
#include <JOutPipeStream.h>
#include <JString.h>
#include <JUserNotification.h>

#include <jTypes.h>
#include <jStreamUtil.h>
#include <jProcessUtil.h>
#include <jStrStreamUtil.h>
#include <jDirUtil.h>

#include <jXGlobals.h>

#include <jStreamUtil.h>
#include <jStrStreamUtil.h>

#include <strstream.h>
#include <iostream.h>
#include <jAssert.h>

const JCharacter* JXSpellChecker::kSpellCheckFinished = "JXSpellChecker::kSpellCheckFinished";

const JCharacter EOT = '\4';

JString JXSpellChecker::itsPrefsString;

const JFileVersion kCurrentPrefsFileVersion 	= 1; 

/******************************************************************************
 Constructor

 *****************************************************************************/

JXSpellChecker::JXSpellChecker
	(
	JXTEBase* editor
	)
	:
	JBroadcaster()
{
	itsEditor 		= editor;
	itsCurrentIndex	= 1;
	itsDialog 		= NULL;
	itsWords 		= NULL;
	itsFoundErrors	= kFalse;

	int toFD, fromFD;

	JString cwd = JGetCurrentDirectory();
	JString home;
	if (JGetHomeDirectory(&home))
		{
		JChangeDirectory(home);
		}

	JError err = JProcess::Create(&itsProcess, "ispell -a",
									kJCreatePipe, &toFD,
									kJCreatePipe, &fromFD,
									kJTossOutput, NULL);
	assert (err.OK());

	itsIn = new JInPipeStream(fromFD, kTrue);
	assert(itsIn != NULL);
	itsOut = new JOutPipeStream(toFD, kTrue);
	assert(itsOut != NULL);

	char s[255];
	itsIn->getline(s, 255);
	JChangeDirectory(cwd);

	itsDialog = new JXSpellCheckerDialog(JXGetApplication());
	assert(itsDialog != NULL);

	if (!itsPrefsString.IsEmpty())
		{
		jistrstream(is, itsPrefsString.GetCString(), itsPrefsString.GetLength());
		itsDialog->GetWindow()->ReadGeometry(is);
		}
	else
		{
		itsDialog->GetWindow()->PlaceAsDialogWindow();
		}

	itsEditor->GetWindow()->GetDirector()->Suspend();
	itsEditor->TEActivate();
}

/******************************************************************************
 Destructor

 *****************************************************************************/

JXSpellChecker::~JXSpellChecker()
{
	*itsOut << '#';
	*itsOut << endl;
	delete itsOut;
	delete itsIn;
	delete itsProcess;
}

/******************************************************************************
 Receive

 *****************************************************************************/

void
JXSpellChecker::Receive
	(
	JBroadcaster* 					sender,
	const JBroadcaster::Message& 	message
	)
{
	if (sender == itsDialog && message.Is(JXSpellCheckerDialog::kSpellButtonPressed))
		{
		const JXSpellCheckerDialog::SpellButtonPressed* info =
			dynamic_cast(const JXSpellCheckerDialog::SpellButtonPressed*, &message);
		assert( info != NULL );

		JXSpellCheckerDialog::SpellAction action = 
			itsDialog->GetSpellAction();
		if (action == JXSpellCheckerDialog::kChange)
			{
			itsEditor->Paste(itsDialog->GetCorrection());
			itsCurrentIndex = itsEditor->GetWordEnd(itsEditor->GetInsertionIndex()) + 1;
			}
		else if (action == JXSpellCheckerDialog::kLearn)
			{
			JString orig(itsDialog->GetCheckWord());
			*itsOut << '&';
			orig.Print(*itsOut);
			*itsOut << endl;
			*itsOut << '#';
			*itsOut << endl;
			}
		else if (action == JXSpellCheckerDialog::kLearnCaps)
			{
			JString orig(itsDialog->GetCheckWord());
			*itsOut << '*';
			orig.Print(*itsOut);
			*itsOut << endl;
			*itsOut << '#';
			*itsOut << endl;
			}
		else if (action == JXSpellCheckerDialog::kIgnoreAll)
			{
			JString orig(itsDialog->GetCheckWord());
			*itsOut << '@';
			orig.Print(*itsOut);
			*itsOut << endl;
			}
		else if (action == JXSpellCheckerDialog::kChangeAll)
			{
			itsEditor->Paste(itsDialog->GetCorrection());
			itsCurrentIndex = itsEditor->GetWordEnd(itsEditor->GetInsertionIndex()) + 1;
			JString orig(itsDialog->GetCheckWord());
			JBoolean toss;
			while (itsEditor->SearchForward(orig, kTrue, kTrue, kFalse, &toss))
				{
				itsEditor->Paste(itsDialog->GetCorrection());
				}
			}
		else if (action == JXSpellCheckerDialog::kClose)
			{
			CleanUpOnFinished();
			return;
			}
		CheckWords();
		}
}

/******************************************************************************
 CheckWords

 *****************************************************************************/

void
JXSpellChecker::CheckWords()
{
	JBoolean ok = kTrue;
	while (ok)
		{
		JIndex end = itsEditor->GetWordEnd(itsCurrentIndex);
		JIndex start = itsEditor->GetWordStart(end);
		if ((end < start) || (start < itsCurrentIndex))
			{
			ok = kFalse;
			CleanUpOnFinished();
			}
		else
			{
			itsEditor->SetSelection(start, end);
//			itsEditor->TEActivate();
			JString text;
			JBoolean selected = itsEditor->GetSelection(&text);
			assert(selected);
			ok = CheckWord(text);
			itsCurrentIndex = end + 1;
			}
		}
}

/******************************************************************************
 CheckWord

 *****************************************************************************/

JBoolean
JXSpellChecker::CheckWord
	(
	const JString& word
	)
{
	if (word.IsInteger() || word.IsFloat())
		{
		return kTrue;
		}
	if (itsWords != NULL)
		{
		itsWords->DeleteAll();
		}
	word.Print(*itsOut);
	*itsOut << endl;
	JString test = JReadLine(*itsIn);
	if (test.IsEmpty())
		{
		test = JReadLine(*itsIn);
		}
	if (test.IsEmpty())
		{
		return kTrue;
		}
	JCharacter c = test.GetFirstCharacter();
	if ((c == '*') || (c == '+') || (c == '-'))
		{
		return kTrue;
		}
	itsEditor->TEScrollToSelection(kFalse);
	
	JIndex findex;
	if (test.LocateSubstring(":", &findex))
		{
		test.RemoveSubstring(1, findex);
		test.TrimWhitespace();
		if (itsWords == NULL)
			{
			itsWords = new JPtrArray<JString>;
			assert(itsWords != NULL);
			}
		while (test.LocateSubstring(",", &findex))
			{
			JString* str = new JString(test.GetSubstring(1, findex - 1));
			assert(str != NULL);
			str->TrimWhitespace();
			itsWords->Append(str);
			test.RemoveSubstring(1, findex);
			}
		JString* str = new JString(test);
		assert(str != NULL);
		str->TrimWhitespace();
		itsWords->Append(str);
		assert(itsWords->GetElementCount() > 0);
		if (c == '&')
			{
			itsDialog->SetFirstGuess(*(itsWords->NthElement(1)));
			}
		itsDialog->SetSuggestions(itsWords);
		}
	itsDialog->SetCheckWord(word);
	ListenTo(itsDialog);
	itsDialog->Activate();
	itsFoundErrors = kTrue;
	return kFalse;
}

/******************************************************************************
 ReadSetup (static)

 ******************************************************************************/

void
JXSpellChecker::ReadSetup
	(
	istream& is
	)
{
	JFileVersion version;
	is >> version;
	if (version == 1)
		{
		JReadAll(is, &itsPrefsString);
		}	
}

/******************************************************************************
 WriteSetup (static)

 ******************************************************************************/

void
JXSpellChecker::WriteSetup
	(
	ostream& os
	)
{
	os << kCurrentPrefsFileVersion << ' ';
	itsPrefsString.Print(os);
}

/******************************************************************************
 CleanUpOnFinished (private)

 ******************************************************************************/

void
JXSpellChecker::CleanUpOnFinished()
{
	itsEditor->SetCaretLocation(1);
	ostrstream os;
	itsDialog->GetWindow()->WriteGeometry(os);
	os << ends;
	itsPrefsString = os.str();
	JUnfreeze(os);
	itsDialog->Deactivate();
	itsEditor->TEDeactivate();
	itsEditor->GetWindow()->GetDirector()->Resume();
	Broadcast(SpellCheckFinished(itsFoundErrors));
}
