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