/* libsswf_errormanager.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2008 */

/*

Copyright (c) 2002-2008 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

/** \file
 *
 * \brief The implementation of the error manager class
 *
 * This file declares the body of the functions which are not
 * inline. It is part of the SSWF library.
 */


#include	"sswf/libsswf.h"

using namespace sswf;





/////////////////////////////////////////// ErrorManager

/** \class sswf::ErrorManager
 *
 * \brief This class helps the user to know what errors occur in the library
 *
 * This class was introduced in version 1.8.0.
 *
 * The ErrorManager class is used to let the library call one of your
 * function to handle the error (i.e. so you can print out the message,
 * exit your program, etc.)
 *
 * The callback happens at the time the error occurs.
 *
 * At this time, only the TagHeader derives from this object. All the
 * tags and the Actions call the sswf::ErrorManager::OnError()
 * function.
 *
 * \sa sswf::TagHeader
 */



/** \class sswf::ErrorManager::InternalErrorException
 *
 * \brief Class defined to throw exceptions in the library.
 *
 * Some errors are simply unrecoverable. These errors generate an
 * exception. At this time, the library will only generate this
 * InternalErrorException. Later I may add a hiearchy and information
 * in the exception.
 *
 * In general, at this time you should not try to catch these
 * exceptions. Use them to crash in the library with your debugger
 * and see what went wrong.
 *
 * \sa sswf::ErrorManager
 * \sa sswf::ErrorManager::ErrorHandler
 */

/** \class sswf::ErrorManager::ErrorHandler
 *
 * \brief A sub-class to derive from to receive errors
 *
 * This class is used to catch the errors generated by the library
 * through the error manager. You can setup one error handler
 * pointer in the library. It is called each time there is an
 * error letting you application deal with how to display the
 * error to the end user. By default, the library prints the
 * error on stderr.
 *
 * \sa sswf::ErrorManager::ErrorHandler::OnError(error_code_t errcode, const char *msg) = 0
 */

/** \brief A destructor since the class has a virtual function.
 *
 * This destructor ensures that all the destructors from classes
 * derived from this class are called as expected.
 */
ErrorManager::ErrorHandler::~ErrorHandler()
{
}


/** \fn sswf::ErrorManager::ErrorHandler::OnError(error_code_t errcode, const char *msg) = 0
 *
 * \brief Function to overload to catch errors.
 *
 * To catch the errors, derive from the ErrorHandler sub-class and
 * implement this function. It receives the error code and a
 * message one can print on the screen.
 *
 * \param[in] errcode The code of the error being generated
 * \param[in] msg The message corresponding to the specified errcode
 *
 * \return The function is expected to return errcode, but if you want
 * the library to ignore the error, you can instead return
 * ERROR_CODE_NONE. In this case, some functions will still fail
 * and ignore that return code.
 */





/** \enum sswf::ErrorManager::error_code_t
 *
 * \brief Enumeration of all the SSWF errors.
 *
 * This enumeration is a list of the different errors that can
 * occur while using the SSWF library. Most errors are considered
 * bad and should not happen.
 *
 * To catch the errors, derive from the ErrorHandler sub-class and
 * implement the
 * ErrorManager::ErrorHandler::OnError(error_code_t errcode, const char *message, va_list args)
 * function.
 *
 * If your function generates multiple errors or call sub-functions
 * which themselves can generate multiple errors, then you can
 * use the KeepFirst(error_code_t a, error_code_t b) function to
 * make sure that you keep the first error.
 *
 * \code
 *	ec = ErrorManager::ERROR_CODE_NONE;
 *	...
 *	ec = ErrorManager::KeepFirst(ec, SaveMore(obj[idx]));
 *	...
 *	return ec;
 * \endcode
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_NONE
 *
 * \brief No error occured.
 *
 * This value should never be passed via the OnError() function. However,
 * some functions return an error and this value can be used when the
 * function does not generate an error.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_ACTION_OVERFLOW
 *
 * \brief Trying to add too many actions in the same buffer
 *
 * Action buffers are limited in the number of actions they can record.
 * In general this is about 64Kb of action data including constant
 * strings, dictionaries, etc. In some circumstances, only 255 bytes
 * are available.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_BUTTON_MISSING_STATE
 *
 * \brief Buttons do not work without at least one state.
 *
 * This errors tells you that you are trying to save a button which
 * has no states whatsoever.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_CHILDREN_NOT_SUPPORTED
 *
 * \brief Tag passed as parent cannot accept children.
 *
 * Whenever a tag is created, you need to specify a parent tag.
 * However, most tags do not accept children. These tags will
 * generate this error when used as a parent of another tag.
 *
 * At this time, only an sswf::TagHeader and sswf::TagSprite can
 * be used as a parent.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_ENDED_ACTION_SCRIPT
 *
 * \brief The END action was found before the end.
 *
 * This error occurs when the list of actions includes an END
 * action within the list instead of being at the end.
 *
 * There is usually no need for you to include the END
 * action so just don't do so and let the system close your
 * list of actions as expected.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_INTERNAL_ERROR
 *
 * \brief An error which should never occur!
 *
 * This indicates a problem in logic in the library.
 *
 * Please report such errors if you can produce them!
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_LABEL_NOT_FOUND
 *
 * \brief No label corresponding to a branch instruction was found.
 *
 * Branch instructions specify the name of a label to which they
 * jump. No label with that name could be found in the label block
 * of action at the same level.
 *
 * This happens when the label was not added or when the label
 * is in a sub-block or a parent block (i.e. you cannot branch
 * from within a \e With action block to outside of that block.)
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_LABEL_OVERFLOW
 *
 * \brief A label is too far from a corresponding branch.
 *
 * The distance between a label and a corresponding branch is limited by the
 * offset one can use with the branch instructions. If the label is too far
 * (i.e. more than 32Kb before or after), the branch cannot be used and the
 * library generates this error.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_MISSING_FRAME_NAME
 *
 * \brief A required frame name is missing.
 *
 * Some objects such as the WaitForFrame action need to be given the name of
 * a frame to properly be defined. This error occurs if the name was not
 * defined or set to the empty string.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_OBJECT_NOT_FOUND
 *
 * \brief An object or a tag could not be found.
 *
 * Different functions use references to other objects or tags.
 * This error occurs if a named object or tag cannot be found
 * in the list of tags. Verify the names and try again.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_REGISTER_OVERFLOW
 *
 * \brief Too large a register number was specified.
 *
 * Registers are limited to 0, 1, 2 and 3 in older versions of SWF.
 *
 * In version 7 and over, registers 0 to 255 can be used (255 seems to
 * be special in some ways though!) within a function.
 *
 * If a register number larger than what is allowed is used, the library
 * generates this error.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_START_SOUND_NO_INFO
 *
 * \brief Cannot create a StartSound tag without information about the sound.
 *
 * This error occurs when a StartSound tag is saved without some sound
 * information (i.e. what sound needs to be started?!)
 *
 * \sa sswf::ErrorManager::ERROR_CODE_COMPRESSED_SOUND_8BITS
 * \sa sswf::ErrorManager::ERROR_CODE_UNSUPPORTED_SOUND_FORMAT
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_COMPRESSED_SOUND_8BITS
 *
 * \brief Library cannot compress 8 bits sound data.
 *
 * Only 16 bits sound data can be compressed at this time.
 *
 * The library may later be capable of converting the 8 bits sound data in
 * a 16 bits buffer ready for compression.
 *
 * \sa sswf::ErrorManager::ERROR_CODE_START_SOUND_NO_INFO
 * \sa sswf::ErrorManager::ERROR_CODE_UNSUPPORTED_SOUND_FORMAT
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_INCOMPATIBLE_CHILD
 *
 * \brief Parent refused that child (by type)
 *
 * Whenever a tag is created, you need to specify a parent tag;
 * except for the sswf::TagHeader object which represents the root
 * tag.
 *
 * The parent tag checks whether it can accept the child depending on its
 * type. If not, an error is generated and the new tag will have no parent.
 *
 * The only two tags which currently accept children are the sswf::TagHeader
 * and sswf::TagSprite. The sswf::TagSprite has rather limited number of
 * tags which can be added as a child.
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_UNSUPPORTED_SOUND_FORMAT
 *
 * \brief The library does not currently support this sound format.
 *
 * Thought the specified sound format is a valid SWF format, it
 * is not currently supported by the library.
 *
 * \sa sswf::ErrorManager::ERROR_CODE_COMPRESSED_SOUND_8BITS
 * \sa sswf::ErrorManager::ERROR_CODE_START_SOUND_NO_INFO
 *
 * <hr>
 */


/** \var sswf::ErrorManager::error_code_t sswf::ErrorManager::ERROR_CODE_max
 *
 * \brief The last error + 1
 *
 * This value represents the last error + 1.
 */








/** \brief Initializes an error manager
 *
 * The constructor sets the number of errors to zero and the error
 * handler pointer to null.
 */
ErrorManager::ErrorManager(void)
{
	f_error_count = 0;
	f_error_handler = 0;
}




/** \brief Defines a pointer to a user error handler
 *
 * This function is called to define the pointer to use as the
 * error handler pointer.
 *
 * The function returns the current error handler. You may either
 * call it or ignore it from within your function. By calling it
 * you in effect create a chain and all error handlers can be called
 * in this way.
 *
 * \note
 * You should save the returned pointer and restore it when you
 * are done with your pointer. However, this is not a 100% viable
 * scheme since another function could add another pointer after
 * you put yours. Then restoring would in effect prevent the newer
 * function from being called.
 *
 * My point of view is that it won't be necessary to put more than
 * one error handler in the error manager. If need be, create a
 * class which handles one to many a bit like the following code:
 *
 * \code
 *	class MyStuff
 *	{
 *	public:
 *		void AddHandler(ErrorHandler *handler)
 *		{
 *			f_handlers += handler;
 *		}
 *		std::vector<ErrorHandler*>	f_handlers;
 *		error_code_t OnError(error_code_t errcode, ...)
 *		{
 *			for(i = 0; i < f_handlers.size(); ++i) {
 *				f_handlers[i].OnError(errcode, ...);
 *			}
 *			return errcode;
 *		}
 *	};
 * \endcode
 *
 * \warning
 * This function is not visible in the C library. Instead, you need
 * to use the specialized function:
 *
 * \code
 * void sswf_error_manager_set_callback(struct SSWF_ErrorManager *error_manager, sswf_error_manager_on_error_callback callback);
 * \endcode
 *
 * The error_manager pointer is taken by casting your tag header
 * as required.
 *
 * \param[in] error_handler The handler to attach to this error manager
 *
 * \return The previous error handler
 */
ErrorManager::ErrorHandler *ErrorManager::SetErrorHandler(ErrorManager::ErrorHandler *error_handler)
{
	ErrorHandler	*old_handler = f_error_handler;

	f_error_handler = error_handler;

	return old_handler;
}



/** \brief Called whenever an error occurs
 *
 * This function is called by the library whenever an error occurs
 * (you can also call it yourself).
 *
 * The message and following arguments are defined as for vprintf(3C).
 *
 * The error code is defined as one of the ErrorManager error code.
 *
 * Each time this function is called, the number of errors is increased.
 * You can use the sswf::ErrorManager::Count(void) const function to
 * retrieve the value of the current counter.
 *
 * There is no level for errors. All are considered as errors, some are
 * fatal meaning that the process in progress will end immediately.
 *
 * \note
 * The error code returned by this function is usually the input
 * error code. The user handler may, however, modify that value
 * and return any other error code including ERROR_CODE_NONE to
 * (try to) cancel the error.
 *
 * \param[in] errcode On of the ERROR_CODE_... values
 * \param[in] message The message, can include formatting characters
 * \param[in] args The arguments for the message formatting characters
 *
 * \return An error code; usually the input error code
 *
 * \sa sswf::ErrorManager::Count(void) const
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, ...) const
 */
ErrorManager::error_code_t ErrorManager::OnError(error_code_t errcode, const char *message, va_list args) const
{
	++f_error_count;

	char buf[1024];
	vsnprintf(buf, sizeof(buf), message, args);
	buf[sizeof(buf) - 1] = '\0';

	if(f_error_handler != 0) {
		// the user handler can modify the returned error code
		return f_error_handler->OnError(errcode, buf);
	}

	fprintf(stderr, "sswf: error: %d: %s\n", errcode, buf);

	return errcode;
}


/** \brief Called whenever an error occurs
 *
 * This function is called by the library whenever an error occurs
 * (you can also call it yourself).
 *
 * The message and following arguments are defined as for printf(3C).
 *
 * This function simple calls the
 * sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list args) const
 *
 * \note
 * This function is not visible in the C library.
 *
 * \param[in] errcode On of the ERROR_CODE_... values
 * \param[in] message The message, can include formatting characters
 * \param[in] ... The arguments for the message formatting characters
 *
 * \return An error code; usually the input error code
 *
 * \sa sswf::ErrorManager::Count(void) const
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list args) const
 */
ErrorManager::error_code_t ErrorManager::OnError(error_code_t errcode, const char *message, ...) const
{
	va_list args;
	va_start(args, message);
	errcode = OnError(errcode, message, args);
	va_end(args);
	return errcode;
}


/** \brief Reset the error counter
 *
 * This function resets the error counter. The counter is set to
 * zero on creation of an ErrorManager object. It is increased each
 * time the OnError() function is called and can be retrieved using
 * the Count() function.
 *
 * \sa sswf::ErrorManager::ErrorManager(void)
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list args) const
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, ...) const
 * \sa sswf::ErrorManager::Count(void) const
 */
void ErrorManager::Reset(void)
{
	f_error_count = 0;
}


/** \brief Retrieve the value of the current error counter
 *
 * This function can be used to retrieve the current error
 * counter. This is set to zero on creation of an error
 * manager and whenever the Reset() function is called.
 *
 * You can use this counter to print out the total number of
 * errors which occured once a function you called returns.
 *
 * \return The number of errors so far or zero when no errors occured
 *
 * \sa sswf::ErrorManager::ErrorManager(void)
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list args) const
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *message, ...) const
 * \sa sswf::ErrorManager::Reset(void)
 */
int ErrorManager::Count(void) const
{
	return f_error_count;
}



/** \brief Keep the first error if not NONE, otherwise keep the second
 *
 * This function returns the first error code (a) if it is not
 * ErrorManager::ERROR_CODE_NONE, otherwise it returns the second
 * error code (b).
 *
 * This is used in many functions where an error does not stop the
 * function processing. Instead the very first error generated is
 * kept and returned to the caller.
 *
 * This is especially useful to generate as many errors as possible
 * without requiring the user to re-run the Save() process 100 times
 * if there are 100 errors.
 *
 * \sa sswf::ErrorManager::error_code_t
 * \sa sswf::ErrorManager::OnError(error_code_t errcode, const char *msg)
 */
ErrorManager::error_code_t ErrorManager::KeepFirst(error_code_t a, error_code_t b)
{
	if(a == ErrorManager::ERROR_CODE_NONE) {
		return b;
	}

	return a;
}


/* The following options fold the documentation; use 'zi' to turn on and off
 *
 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
 */
