/****************************************************************************
** libmatroska : parse Matroska files, see http://www.matroska.org/
**
** <file/class description>
**
** Copyright (C) 2002-2004 Steve Lhomme.  All rights reserved.
**
** This file is part of libmatroska.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
** 
** This library 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
** Lesser General Public License for more details.
** 
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
** See http://www.gnu.org/licenses/lgpl-2.1.html for LGPL licensing information.**
** Contact license@matroska.org if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

/*!
    \file
    \version \$Id$
    \author Steve Lhomme     <robux4 @ users.sf.net>
		Test the writing a basic EBML file
*/

#include <stdio.h>
#include <string>

#include "ebml/StdIOCallback.h"

#include "ebml/EbmlHead.h"
#include "ebml/EbmlSubHead.h"
#include "ebml/EbmlStream.h"
#include "ebml/EbmlVoid.h"
#include "ebml/EbmlContexts.h"
#include "matroska/KaxSegment.h"
#include "matroska/KaxContexts.h"
#include "matroska/KaxSemantic.h"

#include "ebml/EbmlVersion.h"
#include "matroska/KaxVersion.h"

using namespace LIBMATROSKA_NAMESPACE;

const char RW_FILENAME[] = "test.kax";

#undef SKIP_ATTACHED

/*!
    \test Some test on the Cluster use
		\todo each Master element should have a context !
		\todo move the base EBML classes to a template of each type ? (defining the default value and the ID)
		\todo find another way than using all those static_cast (using a template ?)
*/
int main(void)
{
	printf("Test00 :\nEBML %s %s / Matroska %s %s\n", EbmlCodeVersion.c_str(), EbmlCodeDate.c_str(), KaxCodeVersion.c_str(), KaxCodeDate.c_str());
	StdIOCallback Ebml_file(RW_FILENAME, ::MODE_CREATE);
	
	///// Writing test
	EbmlHead TestHead;

	printf("Write EBML + Matroska elements\n");
	EDocType & MyDocType = GetChild<EDocType>(TestHead);
	*static_cast<EbmlString *>(&MyDocType) = "matroska";

	EDocTypeVersion & MyDocTypeVer = GetChild<EDocTypeVersion>(TestHead);
	*(static_cast<EbmlUInteger *>(&MyDocTypeVer)) = 1;

	EDocTypeReadVersion & MyDocTypeReadVer = GetChild<EDocTypeReadVersion>(TestHead);
	*(static_cast<EbmlUInteger *>(&MyDocTypeReadVer)) = 1;

	TestHead.Render(Ebml_file);

	KaxSegment FirstSegment;
	// size is unknown and will always be, we can render it right away
	FirstSegment.Render(Ebml_file);

	KaxAttachments * pAllAttachments = static_cast<KaxAttachments *>(FirstSegment.FindFirstElt(KaxAttachments::ClassInfos, true));
	if (pAllAttachments == NULL)
		return -1;
	pAllAttachments->SetSizeInfinite();
	// size is unknown and will always be, we can render it right away
	pAllAttachments->Render(Ebml_file);
	
	KaxAttached * pAttachment1 = static_cast<KaxAttached *>(pAllAttachments->FindFirstElt(KaxAttached::ClassInfos, true));
	if (pAttachment1 == NULL)
		return -1;
	KaxFileName * pFileName1 = static_cast<KaxFileName *>(pAttachment1->FindFirstElt(KaxFileName::ClassInfos, true));
	if (pFileName1 == NULL)
		return -1;
	*static_cast<EbmlUnicodeString *>(pFileName1) = "file1.txt";
	KaxFileData * pFileData1 = static_cast<KaxFileData *>(pAttachment1->FindFirstElt(KaxFileData::ClassInfos, true));
	if (pFileData1 == NULL)
		return -1;
	char Buffer1[] = "Ah ah ah !";
	static_cast<EbmlBinary *>(pFileData1)->SetBuffer((const binary*) Buffer1, countof(Buffer1));
	// should produce an error if the size is not infinite and the data has been rendered
	pAttachment1->Render(Ebml_file);
	
	KaxAttached * pAttachment2 = static_cast<KaxAttached *>(pAllAttachments->AddNewElt(KaxAttached::ClassInfos));
	if (pAttachment2 == NULL)
		return -1;
	KaxFileName * pFileName2 = static_cast<KaxFileName *>(pAttachment2->FindFirstElt(KaxFileName::ClassInfos, true));
	if (pFileName2 == NULL)
		return -1;
	*static_cast<EbmlUnicodeString *>(pFileName2) = "file2.txt";
	// Add a void element (data is discarded)
	EbmlVoid * pVoid = static_cast<EbmlVoid *>(pAttachment2->FindFirstElt(EbmlVoid::ClassInfos, true));
	if (pVoid == NULL)
		return -1;
	static_cast<EbmlBinary *>(pVoid)->SetBuffer((const binary*) Buffer1, countof(Buffer1));
	pAttachment2->Render(Ebml_file);

#ifdef SKIP_ATTACHED
	KaxSegment SecondSegment;
	// size is unknown and will always be, we can render it right away
	SecondSegment.Render(Ebml_file);
#endif // SKIP_ATTACHED

	Ebml_file.close();

	///// Reading test
	StdIOCallback Ebml_Wfile(RW_FILENAME, ::MODE_READ);
	
	// example 1 skip all the elements found
	EbmlStream aStream(Ebml_Wfile);
	EbmlElement * ElementLevel0;
	EbmlElement * ElementLevel1;
	EbmlElement * ElementLevel2;
	EbmlElement * ElementLevel3;
	
	// read the data until a possible element is found (valid ID + size combination)
	printf("Read EBML elements & skip data\n");
	// find the EBML head in the file
	ElementLevel0 = aStream.FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL, false);
	if (ElementLevel0 != NULL)
	{
		printf("ID : ");
		for (unsigned int i=0; i<EbmlId(*ElementLevel0).Length; i++)
		{
			printf("[%02X]", EbmlId(*ElementLevel0).Value[i]);
		}
		printf("\n");

		ElementLevel0->SkipData(aStream, EbmlHead_Context);
		if (ElementLevel0 != NULL)
			delete ElementLevel0;
	}

	// example to read attachements in the file
	ElementLevel0 = aStream.FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFL, false);
	while (ElementLevel0 != NULL)
	{
		printf("ID : ");
		for (unsigned int i=0; i<EbmlId(*ElementLevel0).Length; i++)
		{
			printf("[%02X]", EbmlId(*ElementLevel0).Value[i]);
		}
		printf("\n");

		int bUpperElement = 0;

		ElementLevel1 = aStream.FindNextID(KaxSegment_Context, bUpperElement, 0xFFFFFFFFL, true);

		while (ElementLevel1 != NULL) {
			/// \todo switch the type of the element to check if it's one we want to handle, like attachements
			if (EbmlId(*ElementLevel1) == KaxAttachments::ClassInfos.GlobalId) {
				printf("Attachments detected\n");

				ElementLevel2 = aStream.FindNextID(KaxAttachments_Context, bUpperElement, 0xFFFFFFFFL, true);
				while (ElementLevel2 != NULL) {
					/// \todo switch the type of the element to check if it's one we want to handle, like attachements
					if (EbmlId(*ElementLevel2) == KaxAttached::ClassInfos.GlobalId) {
						printf("Attached file detected\n");
					}
#ifdef SKIP_ATTACHED
					ElementLevel2 = ElementLevel2->SkipData(aStream, KaxAttached_Context);
					if (ElementLevel2 == NULL) {
						ElementLevel2 = aStream.FindNextID(KaxAttachments_Context, bUpperElement, 0xFFFFFFFFL, true);

						if (bUpperElement) {
							printf("Upper level1 element found\n");
							delete ElementLevel1;
							ElementLevel1 = ElementLevel2;
							ElementLevel2 = NULL;
						}
					}
#else // SKIP_ATTACHED
					// Display the filename (if it exists)
					ElementLevel3 = aStream.FindNextID(KaxAttached_Context, bUpperElement, 0xFFFFFFFFL, false);
					while (ElementLevel3 != NULL) {
						/// \todo switch the type of the element to check if it's one we want to handle, like attachements
						if (EbmlId(*ElementLevel3) == KaxFileName::ClassInfos.GlobalId) {
							KaxFileName & tmp = *static_cast<KaxFileName*>(ElementLevel3);
							tmp.ReadData(aStream.I_O());
							printf("File Name = %ls\n", UTFstring(tmp).c_str());
						} else {
							ElementLevel3->SkipData(aStream, KaxAttached_Context);
						}
						delete ElementLevel3;
						ElementLevel3 = aStream.FindNextID(KaxAttached_Context, bUpperElement, 0xFFFFFFFFL, false);
						if (bUpperElement)
							break;
					}

					if (bUpperElement) {
						delete ElementLevel2;
						ElementLevel2 = ElementLevel3;
					} else {
						ElementLevel2->SkipData(aStream, KaxAttached_Context);
						delete ElementLevel2;

						ElementLevel2 = aStream.FindNextID(KaxAttachments_Context, bUpperElement, 0xFFFFFFFFL, true);
					}
#endif // SKIP_ATTACHED
				}
			}
			ElementLevel1->SkipData(aStream, KaxAttachments_Context);
			delete ElementLevel1;
	
			ElementLevel1 = aStream.FindNextID(KaxSegment_Context, bUpperElement, 0xFFFFFFFFL, true);
		}

		ElementLevel0->SkipData(aStream, KaxSegment_Context);
		if (ElementLevel0 != NULL)
			delete ElementLevel0;
	
		ElementLevel0 = aStream.FindNextID(KaxSegment_Context, bUpperElement, 0xFFFFFFFFL, true);
	}

	Ebml_Wfile.close();

	return 0;
}
