/* UiPause.cpp
 *
 * Copyright (C) 2009-2020,2022-2026 Paul Boersma
 *
 * This code 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 3 of the License, or (at
 * your option) any later version.
 *
 * This code 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 work. If not, see <http://www.gnu.org/licenses/>.
 */

#include "UiPause.h"
#include "praatP.h"

static autoUiForm thePauseForm;
static int thePauseForm_clicked = 0;
static int theCancelContinueButton = 0;
static bool thePauseForm_wasBackgrounding = false;
static Interpreter thePauseForm_interpreterReference;
static bool thePauseForm_secondPass = false;
static structMelderFolder thePauseForm_savedFolder;
static Editor thePauseForm_savedEditorReference;

static void thePauseFormOkCallback (UiForm /* sendingForm */, integer /* narg */, Stackel /* args */,
	conststring32 /* sendingString */, Interpreter /* interpreter */,
	conststring32 /* invokingButtonTitle */, bool /* modified */, void *closure, Editor optionalEditor)
{
	if (! thePauseForm)   // BUG: perhaps there was a mistake in the script
		return;
	Melder_assert (thePauseForm_interpreterReference);
	Melder_assert (Thing_isa (thePauseForm_interpreterReference, classInterpreter));
	if (thePauseForm_interpreterReference -> optionalDynamicEnvironmentEditor() != thePauseForm_savedEditorReference) {
		Melder_assert (thePauseForm_savedEditorReference);
		Melder_assert (! thePauseForm_interpreterReference -> optionalDynamicEnvironmentEditor());
		Melder_assert (thePauseForm_interpreterReference -> optionalDynamicEditorEnvironmentClassName());
				// testing the assumption that the environment can be lost but never added during pause
		Melder_assert (thePauseForm);
		if (thePauseForm -> d_dialogForm) {
			GuiThing_hide (thePauseForm -> d_dialogForm);   // BUG: memory leak
			thePauseForm_secondPass = false;
		}
		thePauseForm. releaseToUser();   // undangle (will be autodestroyed when unmanaged)
		Melder_assert (! thePauseForm);
		const integer lineNumber = thePauseForm_interpreterReference -> lineNumber;
		Interpreter_stop (thePauseForm_interpreterReference);
		Melder_flushError (U"Cannot continue after pause, because the ", thePauseForm_interpreterReference -> optionalDynamicEditorEnvironmentClassName(), U" has been closed.",
				U"\nScript interrupted at line ", lineNumber, U".");   // a top-level error message
		return;
	}
	/*
		Get the data from the pause form.
	*/
	Melder_assert (thePauseForm);
	thePauseForm_clicked = UiForm_getClickedContinueButton (thePauseForm.get());
	if (thePauseForm_clicked != theCancelContinueButton)
		UiForm_Interpreter_addVariables (thePauseForm.get(), (Interpreter) closure);   // 'closure', not 'interpreter' or 'theInterpreter'!
	Melder_assert (thePauseForm_clicked >= 1 && thePauseForm_clicked <= 10);
	conststring32 clickedText = thePauseForm -> continueTexts [thePauseForm_clicked].get();
	if (thePauseForm -> d_dialogForm)
		GuiThing_hide (thePauseForm -> d_dialogForm);   // BUG: memory leak
	thePauseForm. releaseToUser();   // undangle (will be autodestroyed when unmanaged)
	Melder_assert (! thePauseForm);
	/*
		Resume the interpreter.
	*/
	thePauseForm_interpreterReference -> lineNumber -= 1;   // in order to make sure we'll get a second pass
	thePauseForm_secondPass = true;
	thePauseForm_interpreterReference -> pausedByPauseWindow = false;
	if (thePauseForm_wasBackgrounding)
		praat_background ();
	try {
		Interpreter_resume (thePauseForm_interpreterReference);
	} catch (MelderError) {
		if (thePauseForm_clicked == theCancelContinueButton)
			Melder_flushError (U"This happened after you cancelled the pause form.");
		else
			Melder_flushError (U"This happened after you clicked “", clickedText, U"” in the pause form.");
	}
}
static void thePauseFormCancelCallback (UiForm /* dia */, void * /* closure */) {
	if (! thePauseForm)   // BUG: perhaps there was a mistake in the script
		return;
	Melder_assert (thePauseForm_interpreterReference);
	/*
		We arrive here if the user clicked the close box of the dialog window, or if they clicked Stop.
	*/
	Melder_assert (thePauseForm);
	if (thePauseForm -> d_dialogForm) {
		GuiThing_hide (thePauseForm -> d_dialogForm);   // BUG: memory leak
		thePauseForm_secondPass = false;
	}
	thePauseForm. releaseToUser();   // undangle (will be autodestroyed when unmanaged)
	Melder_assert (! thePauseForm);
	if (theCancelContinueButton != 0) {
		/*
			The pause window apparently contains a "Cancel"-like button, so it doesn’t contain a Stop button.
			Hence, if we arrive here, the user must have clicked the close box of the dialog window.
			We divert this click to the "Cancel"-like button,
			and it will be the script’s responsibility to handle it.
		*/
		thePauseForm_clicked = theCancelContinueButton;   // there was
		if (thePauseForm_interpreterReference -> optionalDynamicEnvironmentEditor() != thePauseForm_savedEditorReference) {
			Melder_assert (thePauseForm_savedEditorReference);
			Melder_assert (! thePauseForm_interpreterReference -> optionalDynamicEnvironmentEditor());
			Melder_assert (thePauseForm_interpreterReference -> optionalDynamicEditorEnvironmentClassName());
					// testing the assumption that the environment can be lost but never added during pause
			const integer lineNumber = thePauseForm_interpreterReference -> lineNumber;
			Interpreter_stop (thePauseForm_interpreterReference);
			Melder_flushError (U"Cannot continue after pause, because the ", thePauseForm_interpreterReference -> optionalDynamicEditorEnvironmentClassName(), U" has been closed.",
					U"\nScript interrupted at line ", lineNumber, U".");   // a top-level error message
		}
		/*
			Resume the interpreter.
		*/
		thePauseForm_interpreterReference -> lineNumber -= 1;   // in order to make sure we'll get a second pass
		thePauseForm_secondPass = true;
		thePauseForm_interpreterReference -> pausedByPauseWindow = false;
		if (thePauseForm_wasBackgrounding)
			praat_background ();
		try {
			Interpreter_resume (thePauseForm_interpreterReference);
		} catch (MelderError) {
			Melder_flushError (U"This happened after you stopped the pause form.");
		}
	} else {
		/*
			The pause window apparently contains no "Cancel"-like button, so it must contain a Stop button.
			Perform the normal action for the Stop button, which is to post a message about interruption.
			A click in the close box of the dialog window performs the same action as the Stop button.
		*/
		const integer lineNumber = thePauseForm_interpreterReference -> lineNumber;
		Interpreter_stop (thePauseForm_interpreterReference);
		Melder_flushError (U"You interrupted the script at line ", lineNumber, U".");   // a top-level error message
	}
}
void UiPause_begin (GuiWindow topShell, Editor optionalPauseWindowOwningEditor, conststring32 title, Interpreter interpreter) {
	if (thePauseForm_secondPass)
		return;   // this can happen in `pauseScript()` and in `pause`.
	if (thePauseForm)
		Melder_throw (Melder_upperCaseAppName(), U" cannot have more than one pause form at a time.");
	thePauseForm = UiForm_create (topShell, optionalPauseWindowOwningEditor, Melder_cat (U"Pause: ", title),
		thePauseFormOkCallback, interpreter,   // pass interpreter as closure!
		nullptr, nullptr
	);
}
void UiPause_real (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “real” should be between a “beginPause” and an “endPause”.");
	UiForm_addReal (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_positive (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “positive” should be between a “beginPause” and an “endPause”.");
	UiForm_addPositive (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_integer (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “integer” should be between a “beginPause” and an “endPause”.");
	UiForm_addInteger (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_natural (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “natural” should be between a “beginPause” and an “endPause”.");
	UiForm_addNatural (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_word (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “word” should be between a “beginPause” and an “endPause”.");
	UiForm_addWord (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_sentence (conststring32 label, conststring32 defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “sentence” should be between a “beginPause” and an “endPause”.");
	UiForm_addSentence (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_text (conststring32 label, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “text” should be between a “beginPause” and an “endPause”.");
	UiForm_addText (thePauseForm.get(), nullptr, nullptr, label, defaultValue, numberOfLines);
}
void UiPause_boolean (conststring32 label, bool defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “boolean” should be between a “beginPause” and an “endPause”.");
	UiForm_addBoolean (thePauseForm.get(), nullptr, nullptr, label, defaultValue);
}
void UiPause_infile (conststring32 label, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “infile” should be between a “beginPause” and an “endPause”.");
	UiForm_addInfile (thePauseForm.get(), nullptr, nullptr, label, defaultValue, numberOfLines);
}
void UiPause_outfile (conststring32 label, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “outfile” should be between a “beginPause” and an “endPause”.");
	UiForm_addOutfile (thePauseForm.get(), nullptr, nullptr, label, defaultValue, numberOfLines);
}
void UiPause_folder (conststring32 label, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “folder” should be between a “beginPause” and an “endPause”.");
	UiForm_addFolder (thePauseForm.get(), nullptr, nullptr, label, defaultValue, numberOfLines);
}
void UiPause_realvector (conststring32 label, kUi_realVectorFormat defaultFormat, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “realvector” should be between a “beginPause” and an “endPause”.");
	UiForm_addRealVector (thePauseForm.get(), nullptr, nullptr, label, defaultFormat, defaultValue, numberOfLines);
}
void UiPause_positivevector (conststring32 label, kUi_realVectorFormat defaultFormat, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “positivevector” should be between a “beginPause” and an “endPause”.");
	UiForm_addRealVector (thePauseForm.get(), nullptr, nullptr, label, defaultFormat, defaultValue, numberOfLines);
}
void UiPause_integervector (conststring32 label, kUi_integerVectorFormat defaultFormat, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “integervector” should be between a “beginPause” and an “endPause”.");
	UiForm_addIntegerVector (thePauseForm.get(), nullptr, nullptr, label, defaultFormat, defaultValue, numberOfLines);
}
void UiPause_naturalvector (conststring32 label, kUi_integerVectorFormat defaultFormat, conststring32 defaultValue, integer numberOfLines) {
	if (! thePauseForm)
		Melder_throw (U"The function “naturalvector” should be between a “beginPause” and an “endPause”.");
	UiForm_addIntegerVector (thePauseForm.get(), nullptr, nullptr, label, defaultFormat, defaultValue, numberOfLines);
}
void UiPause_choice (conststring32 label, int defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “choice” should be between a “beginPause” and an “endPause”.");
	UiForm_addChoice (thePauseForm.get(), nullptr, nullptr, nullptr, label, defaultValue, 1);
}
void UiPause_optionmenu (conststring32 label, int defaultValue) {
	if (! thePauseForm)
		Melder_throw (U"The function “optionmenu” should be between a “beginPause” and an “endPause”.");
	UiForm_addOptionMenu (thePauseForm.get(), nullptr, nullptr, nullptr, label, defaultValue, 1);
}
void UiPause_option (conststring32 optionText) {
	if (! thePauseForm)
		Melder_throw (U"The function “option” should be between a “beginPause” and an “endPause”.");
	UiOption option = UiForm_addOption (thePauseForm.get(), optionText);
	if (! option) {
		thePauseForm. reset();
		Melder_throw (U"Found the function “option” without a preceding “choice” or “optionmenu”.");
	}
}
void UiPause_heading (conststring32 label) {
	if (! thePauseForm)
		Melder_throw (U"The function “comment” should be between a “beginPause” and an “endPause”.");
	UiForm_addHeading (thePauseForm.get(), nullptr, label);
}
void UiPause_comment (conststring32 label) {
	if (thePauseForm_secondPass)
		return;   // this can happen in `pauseScript()` and in `pause`.
	if (! thePauseForm)
		Melder_throw (U"The function “comment” should be between a “beginPause” and an “endPause”.");
	UiForm_addComment (thePauseForm.get(), nullptr, label);
}
void UiPause_caption (conststring32 label) {
	if (! thePauseForm)
		Melder_throw (U"The function “caption” should be between a “beginPause” and an “endPause”.");
	UiForm_addCaption (thePauseForm.get(), nullptr, label);
}

int UiPause_end (int numberOfContinueButtons, int defaultContinueButton, int cancelContinueButton,
	conststring32 continueText1, conststring32 continueText2, conststring32 continueText3,
	conststring32 continueText4, conststring32 continueText5, conststring32 continueText6,
	conststring32 continueText7, conststring32 continueText8, conststring32 continueText9,
	conststring32 continueText10, Interpreter interpreter)
{
	if (! thePauseForm_secondPass) {
		if (! thePauseForm)
			Melder_throw (U"Found the function “endPause” without a preceding “beginPause”.");
		Melder_assert (interpreter);
		thePauseForm_interpreterReference = interpreter;
		thePauseForm_savedEditorReference = interpreter -> optionalDynamicEnvironmentEditor();
		UiForm_setPauseForm (thePauseForm.get(), numberOfContinueButtons, defaultContinueButton, cancelContinueButton,
			continueText1, continueText2, continueText3, continueText4, continueText5,
			continueText6, continueText7, continueText8, continueText9, continueText10,
			thePauseFormCancelCallback
		);
		theCancelContinueButton = cancelContinueButton;
		UiForm_finish (thePauseForm.get());
		thePauseForm_wasBackgrounding = Melder_backgrounding;
		//if (theCurrentPraatApplication -> batch) goto end;
		if (thePauseForm_wasBackgrounding)
			praat_foreground ();
		Melder_getCurrentFolder (& thePauseForm_savedFolder);
		thePauseForm_clicked = 0;
		thePauseForm_interpreterReference -> pausedByPauseWindow = true;
		/*
			Put the pause form on the screen.
		*/
		UiForm_destroyWhenUnmanaged (thePauseForm.get());
		UiForm_do (thePauseForm.get(), false);
		return 0;
	} else {
		Melder_assert (! thePauseForm);
		thePauseForm_secondPass = false;
		return thePauseForm_clicked;
	}
}

void UiPause_cleanUp () {
	thePauseForm. reset();
	thePauseForm_clicked = 0;
	theCancelContinueButton = 0;
	thePauseForm_wasBackgrounding = false;
	thePauseForm_interpreterReference = nullptr;
	thePauseForm_secondPass = false;
	MelderFolder_setToNull (& thePauseForm_savedFolder);
	thePauseForm_savedEditorReference = nullptr;
}

/* End of file UiPause.cpp */
