/* libsswf_tag_header.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 sswf::TagHeader 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;



/////////////////////////////////////////// TagHeader

/** \class sswf::TagHeader
 *
 * \brief The header or root of the SWF movie.
 *
 * The TagHeader class is used as the root of an SWF movie. In order to
 * create a movie you must first create a TagHeader. This is the parent
 * tag of most of the other tags (the exception, at this time, are the
 * objects put in a Sprite; but Sprites are defined in a TagHeader)
 *
 * Use the SetCompress(), SetUseNetwork(), SetVersion(), SetMaximumVersion()
 * SetAutoOrder(), SetFrame(), SetOutputEncoding(), SetRate() to initialize
 * the movie.
 *
 * Call Save() once all the tags have been saved in the movie.
 *
 * \sa <a href="../SWFalexref.html#file_header">SWF Alexis' Reference&mdash;The File Header</a>
 */



/** \brief Initialize the TagHeader object
 *
 * The TagHeader create a movie with a frame set to 0, 100, 0, 100 by default.
 *
 * The rate is set to 30.0 frames a second.
 *
 * There is no minimum, maximum or specific version required.
 *
 * By default, the use of the network by a movie played locally is turned off.
 * Yet, this is not fully enforced since it does not force the library into
 * creating a version 8 movie. Use SetVersion() to force a version 8 movie.
 */
TagHeader::TagHeader(void)
	: TagBase("header", 0)
{
	f_version = 0;
	f_min_version = 0;
	f_minimum_version = 0;
	f_maximum_version = 0;
	f_frame.Set(0, 100 * 20, 0, 100 * 20);		// width x height = 100 x 100 (in pixels)
	f_rate = 30.0;
	f_compress = false;
	f_auto_order = false;
	f_use_network = false;
	f_has_metadata = false;
	f_has_jpegtables = false;
	f_next_id = 0;
	f_output_encoding = 0;
	f_iconvertor_open = false;
	f_iconvertor = reinterpret_cast<iconv_t>(-1);	// if f_iconvertor_open is false then this won't be used

#if DEBUG
	// this shouldn't be used until the Save() function is called
	f_saving = false;
#endif

}


/** \brief Destroy a TagHeader object
 *
 * In particular, the destructor ensures that if an iconv(3) handle
 * has been created, it gets closed.
 *
 * The TagHeader derives from the TagBase which itself derives from
 * the MemoryManager. This one is used to manage all the memory allocation
 * and the destructor will release any buffer the TagHeader has allocated.
 *
 * \sa TagBase::~TagBase(), MemoryManager::~MemoryManager()
 */
TagHeader::~TagHeader()
{
	if(f_iconvertor_open) {
		iconv_close(f_iconvertor);
	}
}


/** \brief Check that the child is not a TagHeader
 *
 * A sswf::TagHeader cannot be a child of any other tag. Any other
 * tag can be a child of a TagHeader so far.
 *
 * This function returns an error whenever the child is a tag header.
 *
 * \sa sswf::TagBase::OnNewChild(const char *child)
 * \sa sswf::TagSprite::OnNewChild(const char *child)
 */
ErrorManager::error_code_t TagHeader::OnNewChild(const char *child_name) const
{
	if(strcmp(child_name, "header") == 0) {
		// headers cannot be children
		return ErrorManager::ERROR_CODE_INCOMPATIBLE_CHILD;
	}

	return ErrorManager::ERROR_CODE_NONE;
}









/** \brief Get the next unique identifier
 *
 * All the definition tags require an identifier. In order to generate
 * only unique identifier, one can use this function.
 *
 * Notice that there is a maximum of 32766 identifiers. Values larger
 * than 32767 have special meanings and 0 cannot legally be used as
 * an object identifier.
 *
 * \return A unique identifier.
 *
 * \sa RemoveID(sswf_id_t id)
 */
unsigned short TagHeader::NextID(void)
{
	f_next_id++;
	return f_next_id;
}


/** \brief Release an identifier previously allocated with NextID
 *
 * At times, an object foresees the necessity to have its own identifier.
 * Then later it may either be merged with another object or jointly
 * used with another object. In that case, it is possible to release
 * an identifier.
 *
 * At this time, if you release the very last identifier which was
 * allocated, it is made available again, otherwise the function does
 * nothing.
 *
 * \param id The identifier which is not needed anymore.
 *
 * \sa NextID(void)
 */
void TagHeader::RemoveID(sswf_id_t id)
{
	// we restore this ID only if possible, otherwise forget
	// about it altogether (we could create a list of "removed"
	// IDs so we could reuse them later... anyway!)
	if(f_next_id == id) {
		--f_next_id;
	}
}


/** \brief Defines the size of the output
 *
 * This function defines the size of the output frame.
 *
 * By default, the TagHeader sets the frame to (0, 100, 0, 100).
 *
 * The frame must have x and y positions set to (0, 0).
 *
 * \param rect Rhe rectangle used to define the frame sizes.
 *
 * \sa Frame(void)
 */
void TagHeader::SetFrame(const SRectangle& rect)
{
	// the frame rectangle must have the minimum X and Y positions
	// set to zero so we use the total width and height of the
	// user rectangle instead of the real positions
	f_frame.SetReorder(0, labs(rect.XMin() - rect.XMax()), 0, labs(rect.YMin() - rect.YMax()));
}


/** \brief Retrieve the current frame sizes
 *
 * This function returns a constant reference to the current
 * frame object defined in the header.
 *
 * This can, for instance, be used to create the necessary
 * HTML code to play back the movie.
 *
 * \return A rectangle with the frame sizes.
 *
 * \sa SetFrame(const SRectangle& rect)
 */
const SRectangle& TagHeader::Frame(void) const
{
	return f_frame;
}


/** \brief Defines the encoding of the entire movie
 *
 * Whenever you are using a version 6 or better movie, you endup
 * using UTF-8 strings.
 *
 * When creating an older version, you may need to convert your
 * input in a specific encoding so it works for your viewers.
 * This function can be used to set the encoding used in the
 * movie.
 *
 * \param encoding The name of the encoding as support by your iconv
 */
void TagHeader::SetOutputEncoding(const char *encoding)
{
	MemFree(f_output_encoding);
	if(encoding == 0 || *encoding == '\0') {
		f_output_encoding = 0;
	}
	else {
		f_output_encoding = StrDup(encoding);
	}
}


/** \brief Set the speed at which the frames are to be played
 *
 * This function sets the speed at which the frames in the
 * movie needs to be played back.
 *
 * By default, the frame rate is set to 30.0 (i.e. close to
 * the NTSC refresh rate).
 *
 * A smaller value makes the movie play slower. A larger value
 * makes the movie play faster.
 *
 * PAL uses 25 frames a second. Older movies can have as few as
 * 15 images a second.
 *
 * \param rate The frame rate (saved in a short float in the movie,
 * the range is 1.0 to nearly 256.0, thought over 60 it tends
 * to be way too fast)
 *
 * \sa Rate(void)
 */
void TagHeader::SetRate(float rate)
{
	f_rate = rate;
}


/** \brief Set the speed at which the frames are to be played
 *
 * This function returns the rate you set with SetRate().
 *
 * \return The frame rate in a floating point.
 *
 * \sa SetRate(float rate)
 */
float TagHeader::Rate(void) const
{
	return f_rate;
}


/** \brief Set whether the movie should be compressed or not
 *
 * Since version 6, it is possible to create SWF files which are
 * compressed with zlib. This flag is false by default. Setting
 * the flag to true forces the library to save at least a version
 * 6 movie.
 *
 * \param compress Set to whether true or false
 */
void TagHeader::SetCompress(bool compress)
{
	f_compress = compress;
}


/** \brief Let the library re-order the objects
 *
 * <i>Note: this is currently ignored</i>
 *
 * The ultimate would be to have the library sort out the definitions
 * of objects so they happen only when necessary. This is very
 * problematic now that you can write complex ActionScripts which
 * can reference an object (and the library could not really
 * know... unless it is told.)
 *
 * So at this time I did not work on this functionality.
 *
 * \param auto_order Whether you want the library to re-order the
 * definition tags for you.
 */
void TagHeader::SetAutoOrder(bool auto_order)
{
	f_auto_order = auto_order;
}


/** \brief Whether a locally played movie can use the network
 *
 * When you use a local player to run an SWF animation, this
 * flag determines whether that movie can make use of the
 * network or not.
 *
 * Movies played via a browser connected to the Internet
 * ignore this flag.
 *
 * Note that only movies of version 8 or more can use this
 * flag.
 */
void TagHeader::SetUseNetwork(bool use_network)
{
	f_use_network = use_network;
}





/** \brief Prepare the children objects to be saved
 *
 * This function calls all the children PreSave() function.
 * It is used for the children to have a chance to prepare
 * themselves before being saved. This is necessary for
 * the few tags which need forward look ups. Also some
 * special structures need preparation. For the fonts,
 * it is also necessary to have a PreSave2ndPass()
 *
 * The TagHeader object overwrites this TagBase function
 * in order to search for a TagMetadata and TagJPEGTables.
 * It ensures unicity of these tags, and in a version 8
 * animation, it needs to set a flag in the FileAttributes
 * tags when there is a TagMetadata in the file.
 *
 * \return 0 when it worked; some other non-zero value when it fails
 */
ErrorManager::error_code_t TagHeader::PreSave(void)
{
	TagBase		*p;
	const char	*name;

	/*
	 * The Header now checks for a TagMetadata and TagJPEGTables
	 */
	f_has_metadata = false;
	f_has_jpegtables = false;
	p = Children();
	while(p != 0) {
		name = p->Name();
		if(strcmp(name, "metadata") == 0) {
			if(f_has_metadata) {
				return ErrorManager::ERROR_CODE_TWO_OR_MORE_METADATA;
			}
			f_has_metadata = true;
		}
		if(strcmp(name, "jpegtables") == 0) {
			if(f_has_jpegtables) {
				return ErrorManager::ERROR_CODE_TWO_OR_MORE_JPEGTABLES;
			}
			f_has_jpegtables = true;
		}
		p = p->Next();
	}

	// Now pre-save all the children
	return TagBase::PreSave();
}



/** \brief Define the minimum SWF version required for a successful Save()
 *
 * This function is used to determine the minimum SWF version required
 * to save the current movie. It is automatically called by the Save()
 * function before the movie is created in a Data buffer to know whether
 * it will be possible to create that movie.
 *
 * This funtion goes through all the TagHeader childrens recursively and
 * each one determine what minimum SWF version the it requires to
 * properly be saved in a movie. Note that the TagDoAction, and other
 * tags supporting actions, checks every single action.
 *
 * By default, the minimum version is set to 1 unless you requested a
 * compressed movie in which case it starts at 6. You can start with a
 * different minimum version by calling the SetMinimumVersion() function.
 * Note however that if you set the minimum version to 5 and request a
 * compressed movie, the result will be that the minimum starts at 6
 * anyway.
 *
 * If you called SetVersion(), which fixes the version of the output
 * movie, the minimum is set to that version directly. If any
 * tag cannot be saved with that specific version, then the Save()
 * function fails and no movie is created.
 *
 * If you want to make sure that a movie is not created with <i>too large</i>
 * a version, then use the SetMaximumVersion() function. The Save()
 * will fail if the minimum required is larger than the maximum you defined.
 *
 * \param[out] min_version The minimum version
 *
 * \return An error code number
 *
 * \sa Save(), SetVersion(), SetMinimumVersion(), SetMaximumVersion()
 */
ErrorManager::error_code_t TagHeader::DefineMinimumVersion(int& min_version)
{
	unsigned char			v;
	ErrorManager::error_code_t	ec;

#if DEBUG
	// in pre-save mode we can change the minimum version
	f_saving = false;
#endif

	// the minimum version is dynamically determined
	// we start with a version of 1 if possible
	// and then each tag can say whether it needs
	// a higher version than the current minimum
	if(f_version == 0) {
		f_min_version = f_compress ? 6 : 1;
		// if the user requested a specific minimum
		// version, then use that version instead
		if(f_minimum_version != 0) {
			if(f_min_version < f_minimum_version) {
				f_min_version = f_minimum_version;
			}
		}
	}
	else {
		if(f_compress && f_version < 6) {
			// can't save a compressed movie in a
			// version earlier than v6.x
			return OnError(ERROR_CODE_VERSION_UNSATISFIED, "cannot generate a compress movie with a version smaller than 6; version requested is %d", f_version);
		}
		f_min_version = f_version;
	}

	// ensure everything is properly setup in all the
	// object by a call to the PreSave()
	ec = PreSave();
	if(ec != ErrorManager::ERROR_CODE_NONE) {
		return ec;
	}

	v = f_min_version;
	ec = PreSave2ndPass();
	if(ec != ErrorManager::ERROR_CODE_NONE) {
		return ec;
	}

	// we can't safely switch between any versions in
	// the 2nd pass -- the fonts wouldn't be correct
	// otherwise, so we have to call the pre-save functions
	// all over again!
	if(v <= 5 && f_min_version >= 6) {
		// this time we start with f_min_version >= 6!
		ec = PreSave();
		if(ec != 0) {
			return ec;
		}

		ec = PreSave2ndPass();
		if(ec != 0) {
			return ec;
		}
	}

#if DEBUG
	// in save mode we cannot change the minimum version
	f_saving = true;
#endif

	min_version = static_cast<int>(f_min_version);

	return ErrorManager::ERROR_CODE_NONE;
}


/** \brief Save the movie in a Data buffer
 *
 * The Save() function first calls the DefineMinimumVersion()
 * function. Even if you do not specify a minimum, maximum or
 * specific version, it is necessary to know the version before
 * starting the save process since
 * certain tags can either be optimized or are required to be
 * used instead of others depending on the version (for instance,
 * a version 5 can make use of SWF_TAG_PROTECT_DEBUG, but other
 * versions will use SWF_TAG_PROTECT or SWF_TAG_PROTECT_DEBUG2).
 *
 * Once it determined it could save the movie, it recursively
 * saves all the objects in the <i>data</i> parameter.
 *
 * If everything works as expected, it returns zero meaning
 * that the <i>data</i> buffer holds the resulting movie.
 *
 * \param data The buffer where the movie is to be saved
 *
 * \return 0 when the Save() succeeded; a non-zero value otherwise
 */
ErrorManager::error_code_t TagHeader::Save(Data& data)
{
	TagBase				*child, *last;
	Data				result, header;
	int				min_version;
	ErrorManager::error_code_t	ec;
	size_t				length, children_size;
	uLongf				l;
	Bytef				*d;
	void				*ptr;

	// defining the minimum version calls the PreSave[2ndPass]() functions
	// as required before a save
	ec = DefineMinimumVersion(min_version);
	if(ec != ErrorManager::ERROR_CODE_NONE) {
		return ec;
	}

	// reset the counter in case the user saves multiple times
	ResetFrames();

	// save all the children first
	last = 0;
	child = Children();
	while(child != 0) {
		ec = ErrorManager::KeepFirst(ec, child->Save(result));
		last = child;
		child = child->Next();
	}
	// if any child generated an error, return now
	if(ec != ErrorManager::ERROR_CODE_NONE) {
		return ec;
	}

	if(f_version != 0) {
		// if f_version is set, we will use it to save but it has to
		// (1) be small than max; and (2) min cannot be larger
		if(f_min_version > f_version) {
			return ErrorManager::OnError(ErrorManager::ERROR_CODE_VERSION_UNSATISFIED, "the minimum version required is %d; requested version %d", f_min_version, f_version);
		}
		if(f_version > f_maximum_version && f_maximum_version != 0) {
			return ErrorManager::OnError(ErrorManager::ERROR_CODE_VERSION_UNSATISFIED, "the maximum version imposed is %d; requested version %d", f_maximum_version, f_version);
		}
	}
	else {
		// when f_version is not defined, the version used is f_min_version
		if(f_min_version > f_maximum_version && f_maximum_version != 0) {
			return ErrorManager::OnError(ErrorManager::ERROR_CODE_VERSION_UNSATISFIED, "the minimum version required is %d; the maximum version imposed is %d", f_min_version, f_maximum_version);
		}
	}

	if(last == 0) {
		// there is nothing in there?!!?
		return ErrorManager::OnError(ErrorManager::ERROR_CODE_VERSION_UNSATISFIED, "there are no tags to save; stopping the Save() process");
	}

	// add the SHOW FRAME & END tags so it works properly with any player
	// NOTE: if the list ends with "end" and that "end" isn't preceeded
	//       by show_frame, then there won't be a show frame.
	if(strcmp(last->Name(), "end") != 0) {
		if(strcmp(last->Name(), "showframe") != 0) {
			ShowFrame();
			SaveTag(result, SWF_TAG_SHOW_FRAME, 0);
		}
		result.PutShort(SWF_TAG_END);
	}
	result.Align();

	// in result we have got all the info now
	// create the header itself

	// save the movie sizes
	f_frame.Save(header);
	header.Align();
	header.PutShort((unsigned short) floor(f_rate * 256.0));
	header.PutShort(FrameCount());

	// Since version 8, SWF movies require a very first tag named
	// FileAttributes. Because it has to be first and since it is
	// very much just like a header extension and thus the
	// TagHeader takes care of it.
	if(f_version >= 8) {
		SaveTag(header, SWF_TAG_FILE_ATTRIBUTES, 4);
		header.WriteBits(0, 3);
		header.WriteBits(f_has_metadata, 1);
		header.WriteBits(0, 3);
		header.WriteBits(f_use_network, 1);
		header.PutByte(0);
		header.PutByte(0);
		header.PutByte(0);
	}

	children_size = result.ByteSize();
	length = children_size + header.ByteSize() + 8;	/* 8 bytes is the size of the header without the rectangle which is already included in the header */

	data.PutByte(f_compress ? 'C' : 'F');
	data.PutByte('W');
	data.PutByte('S');
	data.PutByte(f_version == 0 ? f_min_version : f_version);
	data.PutLong(length);

	if(f_compress) {
		// v6.x+ only
		// in this case we need to merge the header + result
		// and compress it before to append to the data buffer
		header.Append(result);		// TODO: this is a bit ulgy I think...

		header.Read(ptr, length);
		l = (length * 11) / 10 + 256;		// buffer + 10% + 256 bytes
		d = (Bytef *) MemAlloc(l, "compressed movie buffer");
		compress(d, &l, (const Bytef *) ptr, length);
		data.Write(d, l);
		MemFree(d);
	}
	else {
		// not compressed, append everything as is
		data.Append(header);
		data.Append(result);
	}

	return ErrorManager::ERROR_CODE_NONE;
}



/** \brief Save a string in a movie with the proper encoding
 *
 * All the strings giving to any tag in an SWF movie must
 * be UTF-8. This function will ensure that the encoding
 * of the string in the resulting SWF movie is set according
 * to the user specification (when the version is 5 or less)
 * or left alone (when the version is 6 or more.)
 *
 * This function is used by the tags to save strings in the
 * <i>data</i> buffer. It should not otherwise be called
 * directly.
 *
 * For movies with version 5 or less, you should use the
 * SetOutputEncoding() to ensure that it is saved with the
 * correct caracters. By default, ISO-8859-1 is used.
 *
 * \param data The buffer where the string is to be saved
 * \param string The string to save in the buffer
 *
 * \return The number of bytes saved in the buffer
 *
 * \sa TagBase::PutString(const char *string)
 */
ErrorManager::error_code_t TagHeader::SaveEncodedString(Data& data, const char *string)
{
	char		buf[256], *s, *p;
	const char	*encoding;
	size_t		out, in;
	int		r;

#if DEBUG
	// here we must be in the save process
	assert(f_saving, "cannot call SaveEncodedString(data, \"%s\") without being within a Save() function", string);
#endif

	if(string == 0 || *string == '\0') {
		// nothing to encode in an empty string
		data.PutByte(0);
		return ErrorManager::ERROR_CODE_NONE;
	}

	if((f_version == 0 ? f_min_version : f_version) >= 6) {
		// version 6.x takes UTF-8 strings which is exactly what
		// we have by default
		data.PutString(string);
		return ErrorManager::ERROR_CODE_NONE;
	}

	// we need the iconv(3) call to encode the string as the
	// user specified before to save it in the Data buffer
	if(!f_iconvertor_open) {
		// convertor not opened yet!
		encoding = f_output_encoding == 0 ? "iso_8859-1" : f_output_encoding;
		f_iconvertor = iconv_open(encoding, "UTF-8");
		if(f_iconvertor == (iconv_t) -1) {
			return ErrorManager::OnError(ErrorManager::ERROR_CODE_ICONV_ENCODER_NOT_AVAILABLE, "cannot open encoder to convert characters from \"UTF-8\" to \"%s\".", encoding);
		}
		f_iconvertor_open = true;
	}
	in = strlen(string);
	out = in * 16;
	if(out >= sizeof(buf)) {
		// use an intermediate string we have to allocate
		s = (char *) MemAlloc(out, "SaveEncodedString(): intermediate string buffer");
	}
	else {
		s = buf;
	}
	p = s;

#if 0
{
printf("Before:");
const unsigned char *t = (const unsigned char *) string;
while(*t != '\0') {
	printf(" %02X", *t);
	t++;
}
}
#endif

	r = (int) iconv(f_iconvertor, ICONV_INPUT_CAST &string, &in, &p, &out);
	if(r < 0) {
		if(s != buf) {
			MemFree(s);
		}
		return ErrorManager::OnError(ErrorManager::ERROR_CODE_ICONV_FAILED, "cannot convert string \"%s\".\n", string);
	}
	*p = '\0';

#if 0
{
printf(", after:");
const unsigned char *t = (const unsigned char *) s;
while(*t != '\0') {
	printf(" %02X", *t);
	t++;
}
}
#endif

	data.PutString(s);
	if(s != buf) {
		MemFree(s);
	}

	return ErrorManager::ERROR_CODE_NONE;
}




/** \brief Defines what the minimum version shall be to save this movie
 *
 * Whenever you create a movie, you may want to use a minimum version to
 * make sure that certain options are properly taken in account.
 *
 * For instance, if you want to make use of the <i>Use Network</i> flag,
 * you need a minimum version of 8. If you want to be sure that your
 * movie works all over the world, in regard to text, then you need to
 * use a version of 6 as a minimum.
 *
 * The version defined with this function is the one used to start
 * defining the minimum version necessary by calling the
 * DefineMinimumVersion() function.
 *
 * \param version The minimum version.
 */
void TagHeader::MinimumVersion(unsigned char version)
{
	if(this == 0) {
		return;
	}

#if DEBUG
	// this is to make sure we don't call this function while
	// saving since changing the version may actually invalidate
	// the previous tags in the movie
	assert(!f_saving, "TagHeader::MinimumVersion() called while saving movie -- this is illegal");
#endif

	if(version > f_min_version) {
		f_min_version = version;
	}
}


/** \brief Read the current output movie version
 *
 * This function retrieves the version in which the movie will
 * be saved.
 *
 * Unless you called the SetVersion() function, this is the
 * minimum version as defined by the DefineMinimumVersion()
 * function.
 *
 * This function can be overloaded in order to control what
 * it shall return. For instance, you may always prevent a
 * version of 1 or 2 to appear. Then your Version() function
 * would look like this:
 *
 * \code
 * 	unsigned char MyObject::Version(void) const
 * 	{
 * 		unsigned char version;
 *
 * 		version = TagHeader::Version();
 *
 * 		if(version < 3) {
 * 			version = 3;
 * 		}
 *
 * 		return version;
 * 	}
 * \endcode
 *
 * \return The version used to save the movie.
 *
 * \warning the version returned by this function will be valid
 * only after you called the DefineMinimumVersion() function.
 * 
 * \sa SetVersion(unsigned char version), DefineMinimumVersion(void)
 */
unsigned char TagHeader::Version(void) const
{
	return f_version == 0 ? f_min_version : f_version;
}



/** \brief Set the version of the output movie
 *
 * This function can be used to force the output movie to use a
 * specific version.
 *
 * It is not recommanded to use this function.
 *
 * If any tag cannot be represented in the specified version,
 * then the Save() function will fail.
 *
 * This is used by the DefineMinimumVersion() function
 * and the Save() function.
 *
 * \param version The exact version the output movie should use
 *
 * \sa DefineMinimumVersion(), Save(), SetMinimumVersion(), SetMaximumVersion()
 */
void TagHeader::SetVersion(unsigned char version)
{
	f_version = version;
}


/** \brief Set the minimum version for the output movie
 *
 * This function is used to ensure that the output movie
 * will be at least of that version. Lower versions will
 * not be considered.
 *
 * This is useful if you want to create a version 6 movie
 * but you don't want to be prevented from creating a
 * version 7 or 8.
 *
 * This is used by the DefineMinimumVersion() function.
 *
 * \param version The minimum version the output movie should use
 *
 * \sa DefineMinimumVersion(), Save(), SetVersion(), SetMaximumVersion()
 */
void TagHeader::SetMinimumVersion(unsigned char version)
{
	f_minimum_version = version;
}


/** \brief Set the maximum version for the output movie
 *
 * This function is used to ensure that the output movie
 * will not be more than that version. If a tag requires
 * a higher versions then it won't be possible to save the
 * movie.
 *
 * This is useful if you want to create a movie for Linux
 * which at this time (Oct 2006) still does not support
 * version 8 of the Macromedia player.
 *
 * This is used by the Save() function to verify that the
 * movie can indeed be saved before to proceed.
 *
 * \param version The maximum version the output movie should use
 *
 * \sa DefineMinimumVersion(), Save(), SetVersion(), SetMinimumVersion()
 */
void TagHeader::SetMaximumVersion(unsigned char version)
{
	f_maximum_version = version;
}


/** \brief Return the type flags as defined in TagBase
 *
 * This function returns the flags representing the TagHeader.
 *
 * At this time it includes: HEADER, UNIQUE and START.
 */
TagBase::swf_type_t TagHeader::TypeFlags(void) const
{
	return SWF_TYPE_HEADER | SWF_TYPE_UNIQUE | SWF_TYPE_START;
}




/** \brief Call the error manager OnError() function
 *
 * This function calls the
 * sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list ap) const.
 *
 * \param errcode	The code of this error
 * \param message	The message for this error
 * \param ap		Arguments for the message
 *
 * \return Usually the input error code; the user may change it
 */
ErrorManager::error_code_t TagHeader::OnError(ErrorManager::error_code_t errcode, const char *message, va_list ap) const
{
	return ErrorManager::OnError(errcode, message, ap);
}


/** \brief Call the error manager OnError() function
 *
 * This function calls the
 * sswf::ErrorManager::OnError(error_code_t errcode, const char *message, va_list ap) const.
 *
 * \param errcode	The code of this error
 * \param message	The message for this error
 * \param ...		Arguments for the message
 *
 * \return Usually the input error code; the user may change it
 */
ErrorManager::error_code_t TagHeader::OnError(ErrorManager::error_code_t errcode, const char *message, ...) const
{
	va_list ap;
	va_start(ap, message);
	ErrorManager::error_code_t ec = ErrorManager::OnError(errcode, message, ap);
	va_end(ap);
	return ec;
}



/* 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
 */
