/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libmspub project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <libmspub/libmspub.h>

#include <memory>

#include "MSPUBCollector.h"
#include "MSPUBParser.h"
#include "MSPUBParser2k.h"
#include "MSPUBParser97.h"
#include "libmspub_utils.h"

namespace libmspub
{

namespace
{

enum MSPUBVersion
{
  MSPUB_UNKNOWN_VERSION = 0,
  MSPUB_2K,
  MSPUB_2K2
};

MSPUBVersion getVersion(librevenge::RVNGInputStream *input)
{
  try
  {
    if (!input->isStructured())
      return MSPUB_UNKNOWN_VERSION;

    std::unique_ptr<librevenge::RVNGInputStream> contentsStream(input->getSubStreamByName("Contents"));
    if (!contentsStream)
      return MSPUB_UNKNOWN_VERSION;

    if (0xe8 != readU8(contentsStream.get()) || 0xac != readU8(contentsStream.get()))
      return MSPUB_UNKNOWN_VERSION;

    unsigned char magicVersionByte = readU8(contentsStream.get());

    if (0x00 != readU8(contentsStream.get()))
      return MSPUB_UNKNOWN_VERSION;

    MSPUBVersion version = MSPUB_UNKNOWN_VERSION;
    switch (magicVersionByte)
    {
    case 0x2C:
      version = MSPUB_2K2;
      break;
    case 0x22:
      version =  MSPUB_2K;
      break;
    default:
      break;
    }
    return version;
  }
  catch (...)
  {
    return MSPUB_UNKNOWN_VERSION;
  }

}

} // anonymous namespace



/**
Analyzes the content of an input stream to see if it can be parsed
\param input The input stream
\return A value that indicates whether the content from the input
stream is a Microsoft Publisher Document that libmspub is able to parse
*/
PUBAPI bool MSPUBDocument::isSupported(librevenge::RVNGInputStream *input)
{
  if (!input)
    return false;

  try
  {
    MSPUBVersion version = getVersion(input);
    if (version == MSPUB_UNKNOWN_VERSION)
      return false;

    if (version == MSPUB_2K2)
    {
      std::unique_ptr<librevenge::RVNGInputStream> escherStream(input->getSubStreamByName("Escher/EscherStm"));
      if (!escherStream)
        return false;
      std::unique_ptr<librevenge::RVNGInputStream> quillStream(input->getSubStreamByName("Quill/QuillSub/CONTENTS"));
      if (!quillStream)
        return false;
    }
    return true;
  }
  catch (...)
  {
    return false;
  }
}

/**
Parses the input stream content. It will make callbacks to the functions provided by a
RVNGDrawingInterface class implementation when needed. This is often commonly called the
'main parsing routine'.
\param input The input stream
\param painter A MSPUBPainterInterface implementation
\return A value that indicates whether the parsing was successful
*/
PUBAPI bool MSPUBDocument::parse(librevenge::RVNGInputStream *input, librevenge::RVNGDrawingInterface *painter)
{
  if (!input || !painter)
    return false;

  try
  {
    MSPUBCollector collector(painter);
    input->seek(0, librevenge::RVNG_SEEK_SET);
    std::unique_ptr<MSPUBParser> parser;
    switch (getVersion(input))
    {
    case MSPUB_2K:
    {
      std::unique_ptr<librevenge::RVNGInputStream> quillStream(input->getSubStreamByName("Quill/QuillSub/CONTENTS"));
      if (!quillStream)
        parser.reset(new MSPUBParser97(input, &collector));
      else
        parser.reset(new MSPUBParser2k(input, &collector));
      break;
    }
    case MSPUB_2K2:
    {
      parser.reset(new MSPUBParser(input, &collector));
      break;
    }
    default:
      return false;
    }
    if (parser)
    {
      return parser->parse();
    }
    return false;
  }
  catch (...)
  {
    return false;
  }
}

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
