/* libsswf_tag_metadata.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::TagMetadata 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;



/////////////////////////////////////////// TagMetadata

/** \class sswf::TagMetadata
 *
 * \brief The description of the SWF animation in humain language
 *
 * This class handles the metadata tag which encapsulate
 * a full description of your SWF animation.
 *
 * The description needs to be saved in a valid XML format as
 * described in my reference:
 * <a href="http://sswf.m2osw.com/SWFalexref.html#tag_metadata">Alexis' SWF Reference&mdash;Metadata</a>
 * You can also look at this .c++ file which can generate a
 * simplified XML description file.
 *
 * This tag should appear very early on in the SWF file so
 * search engines which will use this information do not have
 * to upload the entire file to find the metadata tag.
 *
 * \warning
 * Notice that the TagMetadata requires one group of data to be
 * defined to work properly. The groups are as follow:
 *
 *	\li void sswf::TagMetadata::SetFilename(const char *filename);
 *
 *	\par
 *	Set the filename of an XML file including valid content for
 *	an TagMetadata XML buffer. All the other calls are ignored
 *	when this one call is made with a non-empty string.
 *
 *	\li void sswf::TagMetadata::SetMetadata(const char *xml);
 *
 *	\par
 *	Set the XML buffer directly. All the other calls, except
 *	SetFilename(), are ignored when this SetMetadata() function
 *	is called.
 *
 *	\li void sswf::TagMetadata::SetTitle(const char *title);
 *	\li void sswf::TagMetadata::SetDescription(const char *description);
 *	\li void sswf::TagMetadata::SetAuthor(const char *description);
 *	\li void sswf::TagMetadata::SetPublisher(const char *publisher);
 *	\li void sswf::TagMetadata::SetCopyright(const char *copyright);
 *	\li void sswf::TagMetadata::SetURL(const char *url);
 *
 *	\par
 *	Any one of these functions will set a specific entry in the
 *	XML output. SSWF will take care of generating the XML code. The
 *	string of these functions should be plain text without any XML
 *	tags.
 *
 *	\li <i>none of these calls</i>
 *
 *	\par
 *	If nothing is defined, then the default provided by SSWF
 *	is saved as the SWF animation description.
 *
 * \sa <a href="http://sswf.m2osw.com/SWFalexref.html#tag_metadata">Alexis' SWF Reference&mdash;Metadata</a>
 * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference&mdash;swf_tag</a>
 */

/** \brief Initializes the TagMetadata object
 *
 * The constructor initializes a TagMetadata with no user description
 * by default. If no description is added, then a default SSWF
 * description is saved in the resulting SWF movie.
 *
 * \param parent The sswf::TagHeader where this sswf::TagMetadata is inserted
 */
TagMetadata::TagMetadata(TagBase *parent)
	: TagBase("metadata", parent)
{
	f_xml = 0;
	f_filename = 0;
	f_title = 0;
	f_description = 0;
	f_author = 0;
	f_publisher = 0;
	f_copyright = 0;
	f_url = 0;
}



/** \brief Return the type of the metadata tag
 *
 * This function returns the type flags of the TagMetadata.
 * The TagMetadata is a control, needs to be unique and should
 * be close to the start of the movie.
 *
 * \return The set of flags representing an sswf::TagMetadata
 */
TagBase::swf_type_t TagMetadata::TypeFlags(void) const
{
	return SWF_TYPE_CONTROL | SWF_TYPE_UNIQUE | SWF_TYPE_START;
}


/** \brief Set a string which represents the whole XML metadata
 *
 * This function defines the entire XML metadata in one string.
 * SSWF will directly save this string. At this time, SSWF does
 * not check for the validity of the string.
 *
 * \param xml The direct XML as the description of the SWF animation
 *
 * \sa sswf::TagMetadata::SetFilename
 */
void TagMetadata::SetMetadata(const char *xml)
{
	MemFree(f_xml);
	f_xml = StrDup(xml);
}


/** \brief Define the name of an XML file with the description
 *
 * This function can be used to define the name of a file holding
 * the XML to be saved in the TagMetadata. The SSWF library will
 * read the file at the time the tag is being saved.
 *
 * The library does not check the content validity. You can use
 * xmllint for that purpose.
 *
 * \param filename The name of an XML file to read for the metadata
 *
 * \sa sswf::TagMetadata::SetMetadata
 */
void TagMetadata::SetFilename(const char *filename)
{
	MemFree(f_filename);
	f_filename = StrDup(filename);
}



/** \brief Defines the title
 *
 * Define the SWF animation title.
 *
 * \param title The name or title of the SWF animation
 *
 * \sa sswf::TagMetadata::SetAuthor
 * \sa sswf::TagMetadata::SetCopyright
 * \sa sswf::TagMetadata::SetDescription
 * \sa sswf::TagMetadata::SetPublisher
 * \sa sswf::TagMetadata::SetURL
 */
void TagMetadata::SetTitle(const char *title)
{
	MemFree(f_title);
	f_title = StrDup(title);
}



/** \brief Defines the description of the SWF animation
 *
 * Define the SWF animation description. This can be a very long
 * string. There is no limit imposed.
 *
 * \param description The description of the SWF animation
 *
 * \sa sswf::TagMetadata::SetAuthor
 * \sa sswf::TagMetadata::SetCopyright
 * \sa sswf::TagMetadata::SetPublisher
 * \sa sswf::TagMetadata::SetTitle
 * \sa sswf::TagMetadata::SetURL
 */
void TagMetadata::SetDescription(const char *description)
{
	MemFree(f_description);
	f_description = StrDup(description);
}



/** \brief Defines the name of the SWF animation (main) author
 *
 * Define the name of the SWF animation author. Unfortunately,
 * this function does no currently support more than one author
 * name.
 *
 * \param author The name of the author; at this time, you can add
 *		multiple authors by adding semi-colon between names
 *
 * \sa sswf::TagMetadata::SetCopyright
 * \sa sswf::TagMetadata::SetDescription
 * \sa sswf::TagMetadata::SetPublisher
 * \sa sswf::TagMetadata::SetTitle
 * \sa sswf::TagMetadata::SetURL
 */
void TagMetadata::SetAuthor(const char *author)
{
	MemFree(f_author);
	f_author = StrDup(author);
}



/** \brief Defines the name of the SWF animation publisher
 *
 * Define the name of the SWF animation publisher.
 * This is often the name of the company the author is working
 * with. If you create a movie for yourself, you can put your
 * own name.
 *
 * \param publisher The name (and at times address, phone number, etc.) of the publisher
 *
 * \sa sswf::TagMetadata::SetAuthor
 * \sa sswf::TagMetadata::SetCopyright
 * \sa sswf::TagMetadata::SetDescription
 * \sa sswf::TagMetadata::SetTitle
 * \sa sswf::TagMetadata::SetURL
 */
void TagMetadata::SetPublisher(const char *publisher)
{
	MemFree(f_publisher);
	f_publisher = StrDup(publisher);
}



/** \brief Defines the legal information in regard to this SWF animation
 *
 * Define legal information in link with the SWF animation. This
 * is often a copyright statement including the year of publication.
 *
 * This string can also defines the rights of the users in regard to
 * this movie (i.e. free content, proprietary under patent, etc.)
 *
 * \param copyright The copyright in some humain language
 *
 * \sa sswf::TagMetadata::SetAuthor
 * \sa sswf::TagMetadata::SetDescription
 * \sa sswf::TagMetadata::SetPublisher
 * \sa sswf::TagMetadata::SetTitle
 * \sa sswf::TagMetadata::SetURL
 */
void TagMetadata::SetCopyright(const char *copyright)
{
	MemFree(f_copyright);
	f_copyright = StrDup(copyright);
}



/** \brief Defines a link to the source of the movie
 *
 * Movies can easily be copied. This enables the search engines
 * to know of the real source of the animation (until a hacker
 * changes the URL, of course).
 *
 * This should be your company or personal URL possibly directly
 * to the page where the animation appears.
 *
 * \param url The standard URL link to a webpage
 *
 * \sa sswf::TagMetadata::SetAuthor
 * \sa sswf::TagMetadata::SetCopyright
 * \sa sswf::TagMetadata::SetDescription
 * \sa sswf::TagMetadata::SetPublisher
 * \sa sswf::TagMetadata::SetTitle
 */
void TagMetadata::SetURL(const char *url)
{
	MemFree(f_url);
	f_url = StrDup(url);
}




/** \brief Inform the system that version 8 is required
 *
 * This function is used by the TagMetadata to tell the
 * Save() command that to save this tag a version 8 movie
 * is required.
 *
 * \return ErrorManager::ERROR_CODE_NONE when no error occurs,
 * 		and some other ErrorManager::error_code_t otherwise
 */
ErrorManager::error_code_t TagMetadata::PreSave(void)
{
	MinimumVersion(8);

	return ErrorManager::ERROR_CODE_NONE;
}



/** \brief Is the next character a space?
 *
 * Check whether the specified input string pointer points to a space character.
 * The function recognizes the no-break space character (i.e. 0x00A0 which in
 * UTF-8 is represented as the code byte sequence 0xC2 0xA0.)
 *
 * The function increments the input string pointer by the number of bytes the
 * space represents minus 1. (i.e. the input string pointer will be incremented
 * whtn the 0x00A0 character is found.)
 *
 * \param s The string to check for a space character
 *
 * \return 'true' when the input string points to a space; 'false' otherwise
 */
bool is_space(char *& s)
{
	char		c;

	// also test the &nbsp; -- in UTF-8 it is saved on two bytes
	c = *s;
	if((unsigned char) c == 0xC2 && (unsigned char) s[1] == 0xA0) {
		s++;
		return true;
	}
	return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
}



/** \brief Save the TagMetadata tag
 *
 * This function saves the TagMetadata in the specified Data buffer.
 *
 * The function creates the XML data for the TagMetadata tag and save
 * the result in the data parameter.
 *
 * The function tries to optimize the XML as much as possible.
 *
 * \todo
 * The loading and optimization code should probably be in a separate
 * set of functions so one could check out what the XML is at anything.
 *
 * \return ErrorManager::ERROR_CODE_NONE when no error occurs,
 * 		and some other ErrorManager::error_code_t otherwise
 */
ErrorManager::error_code_t TagMetadata::Save(Data& data)
{
	int		l;
	char		*xml, *s, *d, quote;
	FILE		*f;
	bool		closing;

	if(f_filename != 0) {
		// TODO: should we have "r" instead? (since XML is supposed to
		// be text even on "special" systems such as Microsoft Windows...)
		f = fopen(f_filename, "rb");
		if(f == 0) {
			// try adding the .xml extension
			l = strlen(f_filename);
			if(l < 5 || strcmp(f_filename + l - 5, ".xml") != 0) {
				s = StrDup(f_filename);
				d = StrCat(s, ".xml");
				MemFree(s);
				f = fopen(d, "rb");
				MemFree(d);
			}
			if(f == 0) {
				return OnError(ErrorManager::ERROR_CODE_FILE_NOT_FOUND, "cannot open file \"%s\" for the Metadata tag.", f_filename);
			}
		}
		fseek(f, 0, SEEK_END);
		l = ftell(f);
		fseek(f, 0, SEEK_SET);
		xml = (char *) MemAlloc(l + 1, "Metadata from File");
		if(fread(xml, l, 1, f) != 1) {
			MemFree(xml);
			fclose(f);
			return OnError(ErrorManager::ERROR_CODE_IO, "error while reading file \"%s\" for the Metadata tag.", f_filename);
		}
		fclose(f);
		xml[l] = '\0';
	}
	else if(f_xml != 0) {
		xml = StrDup(f_xml);
	}
	else if(f_title != 0 || f_description != 0 || f_author != 0 || f_publisher != 0 || f_copyright != 0 || f_url != 0) {
		s = StrDup("<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>");
		if(f_url == 0) {
			// put the SSWF default URL
			d = StrCat(s, "<rdf:Description rdf:about='http://sswf.m2osw.com' xmlns:dc='http://purl.org/dc/elements/1.1'>");
			MemFree(s);
			s = d;
		}
		else {
			// User URL
			d = StrCat(s, "<rdf:Description rdf:about='");
			MemFree(s);
			// TODO: check that the URL is valid for a tag value
			s = StrCat(d, f_url);
			MemFree(d);
			d = StrCat(s, "' xmlns:dc='http://purl.org/dc/elements/1.1'>");
			MemFree(s);
			s = d;
		}
		if(f_title != 0) {
			d = StrCat(s, "<dc:title>");
			MemFree(s);
			s = StrCat(d, f_title);
			MemFree(d);
			d = StrCat(s, "</dc:title>");
			MemFree(s);
			s = d;
		}
		if(f_description != 0) {
			d = StrCat(s, "<dc:description>");
			MemFree(s);
			s = StrCat(d, f_description);
			MemFree(d);
			d = StrCat(s, "</dc:description>");
			MemFree(s);
			s = d;
		}
		if(f_author != 0) {
			d = StrCat(s, "<dc:creator>");
			MemFree(s);
			s = StrCat(d, f_author);
			MemFree(d);
			d = StrCat(s, "</dc:creator>");
			MemFree(s);
			s = d;
		}
		if(f_publisher != 0) {
			d = StrCat(s, "<dc:publisher>");
			MemFree(s);
			s = StrCat(d, f_publisher);
			MemFree(d);
			d = StrCat(s, "</dc:publisher>");
			MemFree(s);
			s = d;
		}
		if(f_copyright != 0) {
			d = StrCat(s, "<dc:rights>");
			MemFree(s);
			s = StrCat(d, f_copyright);
			MemFree(d);
			d = StrCat(s, "</dc:rights>");
			MemFree(s);
			s = d;
		}
		xml = StrCat(s, "<dc:type>MovingImage</dc:type>"
				"<dc:format>application/x-shockwave-flash</dc:format>"
				"</rdf:Description></rdf:RDF>"
			);
		MemFree(s);
	}
	else {
		// use a default!
		xml = StrDup(
			"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>"
			"<rdf:Description rdf:about='http://sswf.m2osw.com' xmlns:dc='http://purl.org/dc/elements/1.1'>"
			"<dc:title>Flash animation created by SSWF</dc:title>"
			"<dc:publisher>Made to Order Software Corp.</dc:publisher>"
			"<dc:description>This animation was created by SSWF. For more information, check out the website at http://sswf.m2osw.com</dc:description>"
			"<dc:type>MovingImage</dc:type>"
			"<dc:format>application/x-shockwave-flash</dc:format>"
			"</rdf:Description>"
			"</rdf:RDF>"
		);
	}

	// TODO: we are supposed to remove any extraneous, unnecessary blanks
	//	 the following is supposed to do so, we may want to write something
	//	 better for that purpose (i.e. tags without an xml:space
	//	 should have their data cleaned up)

	s = xml;
	d = s;
	while(is_space(s)) {
		s++;
	}
	while(*s != '\0') {
		if(*s == '<') {
			// within a tag definition we can remove spaces & new lines
			*d++ = *s++;
			closing = *s == '/';
			while(*s != '\0' && *s != '>') {
				if(*s == '"' || *s == '\'') {
					quote = *s++;
					*d++ = quote;
					while(*s != '\0' && *s != quote) {
						*d++ = *s++;
					}
					if(*s == quote) {
						*d++ = *s++;
					}
				}
				else if(is_space(s)) {
					// keep one space between parameters and names
					*d++ = ' ';
					do {
						s++;
					} while(is_space(s));
				}
				else {
					*d++ = *s++;
				}
			}
			// this is wrong if you can use HTML like tags such as
			//  "Here <b>My Name</b> is <i>Henri</i> the Great!"
			if(closing && *s == '>') {
				// remove all spaces after a closing tag; it never is data
				// (unless HTML like format tags are accepted in there?)
				*d++ = '>';
				do {
					s++;
				} while(is_space(s));
			}
		}
		else {
			// plain data is kept as is
			*d++ = *s++;
		}
	}
	//while(d > xml) {
	//	--d;
	//	if(!is_space(d)) {
	//		++d;
	//		break;
	//	}
	//}
	*d = '\0';

	// get the resulting length including the null terminator
	l = strlen(xml) + 1;

	SaveTag(data, SWF_TAG_METADATA, l);

	// NOTE: I allow myself to save this buffer as is since
	//	 this tag is only available after SWF6 and thus
	//	 it is expected to represent UTF-8 and will be
	//	 saved directly anyway. (see TagHeader.SaveEncodedString())
	data.Write(xml, l);

	MemFree(xml);

	return ErrorManager::ERROR_CODE_NONE;
}





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