#ifndef _melder_error_h_
#define _melder_error_h_
/* melder_error.h
 *
 * Copyright (C) 1992-2021,2025 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 <mutex>

class MelderError { };   // empty exception class

extern std::mutex theMelder_error_mutex;
extern std::atomic <integer> theMelder_error_threadId;

bool Melder_hasError ();
bool Melder_hasError (conststring32 partialError);
	/* Returns true if there is an error message in store, otherwise false. */
bool Melder_hasCrash ();
	/* Returns true if there is an error message in store, and that error message is a crash message. */

void Melder_clearError ();
	/* Cancel all stored error messages. */

conststring32 Melder_getError ();
	/* Returns the error string. Mainly used with str32str. */

void Melder_appendError_noLine (const MelderArg& arg1);

/**
	The usual error reporting function is Melder_throw. However,
	you may sometimes want to prepend other messages before Melder_throw,
	without jumping away from the error-generating location.
	In such a special case you can use Melder_appendError.

	Melder_appendError() has to be followed by one of these:
	- Melder_throw() (or just `throw`) to prepend the error to a normal exception;
	- Melder_flushError() to show the error in the GUI
	  (this is where a trail of Melder_throw will usually end up as well);
	- Melder_clearError() to ignore the error.

	If you don't do this, the error will linger in your error buffer until
	the next, probably unrelated, error is generated,
	and your prepended error text will be shown to the user out of context,
	which is wrong.
*/

void MelderError__appendOneString (conststring32 message);

template <typename... Arg>
void Melder_appendError (const Arg... arg) {
	std::lock_guard lock (theMelder_error_mutex);
	if (Melder_hasError ()) {
		if (Melder_thisThread_getUniqueID () != theMelder_error_threadId)
			return;
	} else {
		theMelder_error_threadId = Melder_thisThread_getUniqueID ();
	}
	(// fold
		MelderError__appendOneString (MelderArg { arg }. _arg)
				, ...
	);
	MelderError__appendOneString (U"\n");
}

#define Melder_throw(...)  do { Melder_appendError (__VA_ARGS__); throw MelderError (); } while (false)

void Melder_flushError ();

template <typename... Arg>
void Melder_flushError (const Arg... arg) {
	Melder_appendError (arg...);
	Melder_flushError ();
}
	/* Send all deferred error messages to stderr (batch) or to an "Error" dialog, */
	/* including, if there are arguments, the error message generated by this function. */

void Melder_setErrorProc (void (*p_errorProc) (conststring32));

void _private_Melder_crash (const MelderArg&,
	const MelderArg& = U"", const MelderArg& = U"", const MelderArg& = U"",
	const MelderArg& = U"", const MelderArg& = U"", const MelderArg& = U"",
	const MelderArg& = U"", const MelderArg& = U"", const MelderArg& = U"",
	const MelderArg& = U"", const MelderArg& = U"", const MelderArg& = U""
);

#define Melder_crash(...)  do { _private_Melder_crash (__VA_ARGS__); abort (); } while (0)

void Melder_setCrashProc (void (*p_crashProc) (conststring32));

/* End of file melder_error.h */
#endif
