/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <utility>

#include <librevenge/librevenge.h>

#include "MWAWDebug.hxx"
#include "MWAWEntry.hxx"
#include "MWAWOLEParser.hxx"

#include "PowerPoint3OLE.hxx"

/** Internal: the structures of a PowerPoint3OLE */
namespace PowerPoint3OLEInternal
{
////////////////////////////////////////
//! Internal: the state of a PowerPoint3OLE
struct State {
  //! constructor
  explicit State(MWAWInputStreamPtr input, int vers)
    : m_input(input)
    , m_version(vers)
    , m_oleParser()
    , m_unparsedNameSet()
  {
  }
  /** the input */
  MWAWInputStreamPtr m_input;
  /** the version */
  int m_version;
  /** the ole parser */
  std::shared_ptr<MWAWOLEParser> m_oleParser;
  /** the list of unparsed zone */
  std::set<std::string> m_unparsedNameSet;
};

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
PowerPoint3OLE::PowerPoint3OLE(MWAWInputStreamPtr const &input, int vers, MWAWFontConverterPtr const &fontConverter, int fId)
  : m_state(new PowerPoint3OLEInternal::State(input, vers))
{
  char const(*mainOle)=(version()<=4 ? "PP40" : "PowerPoint Document");
  if (input && input->isStructured() && input->getSubStreamByName(mainOle))
    m_state->m_oleParser.reset(new MWAWOLEParser(mainOle, fontConverter, fId));
}

PowerPoint3OLE::~PowerPoint3OLE()
{
}

int PowerPoint3OLE::version() const
{
  return m_state->m_version;
}

int PowerPoint3OLE::getFontEncoding() const
{
  if (m_state->m_oleParser)
    return m_state->m_oleParser->getFontEncoding();
  return -1;
}

void PowerPoint3OLE::updateMetaData(librevenge::RVNGPropertyList &metaData) const
{
  if (m_state->m_oleParser)
    m_state->m_oleParser->updateMetaData(metaData);
}

////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
bool PowerPoint3OLE::parse()
{
  int const vers=version();
  MWAWInputStreamPtr input=m_state->m_input;
  if (!input || !m_state->m_oleParser || !m_state->m_oleParser->parse(input)) return false;
  std::vector<std::string> unparsed = m_state->m_oleParser->getNotParse();

  for (auto const &name : unparsed) {
    // separated the directory and the name
    //    MatOST/MatadorObject1/Ole10Native
    //      -> dir="MatOST/MatadorObject1", base="Ole10Native"
    std::string::size_type pos = name.find_last_of('/');
    std::string base;
    if (pos == std::string::npos) base = name;
    else if (pos == 0) base = name.substr(1);
    else
      base = name.substr(pos+1);

    MWAWInputStreamPtr ole = input->getSubStreamByName(name.c_str());
    if (!ole.get()) {
      MWAW_DEBUG_MSG(("PowerPoint3OLE::createZones: error: can not find OLE part: \"%s\"\n", name.c_str()));
      continue;
    }
    ole->setReadInverted(true);
    bool done=false;
    switch (base[0]) {
    case 'C':
      if (base=="Current User")
        done=parseCurrentUser(ole, name);
      else if (base=="Current ID")
        done=parseCurrentId(ole, name);
      break;
    case 'H':
      if (vers>=7 && name=="Header")
        done=parseHeader(ole, name);
      break;
    case 'P':
      if (vers>=7 && name=="PersistentStorage Directory")
        done=parsePersistentStorage(ole, name);
      break;
    default:
      break;
    }
    if (done) continue;
    m_state->m_unparsedNameSet.insert(name);
  }
  return true;
}

void PowerPoint3OLE::checkForUnparsedStream()
{
  int const vers=version();
  for (auto const &name : m_state->m_unparsedNameSet) {
    if (vers>=7 && name=="Text_Content") continue;
    auto pos = name.find_last_of('/');
    std::string base;
    if (pos == std::string::npos) base = name;
    else if (pos == 0) base = name.substr(1);
    else
      base = name.substr(pos+1);
    MWAWInputStreamPtr ole = m_state->m_input->getSubStreamByName(name.c_str());
    if (!ole.get()) {
      MWAW_DEBUG_MSG(("PowerPoint3OLE::checkForUnparsedStream: error: can not find OLE part: \"%s\"\n", name.c_str()));
      continue;
    }
    libmwaw::DebugFile asciiFile(ole);
    asciiFile.open(name);
    libmwaw::DebugStream f;
    f << "Entries(" << base << "):";
    asciiFile.addPos(0);
    asciiFile.addNote(f.str().c_str());
  }
}

////////////////////////////////////////////////////////////
// try to read the different stream
////////////////////////////////////////////////////////////
bool PowerPoint3OLE::parseCurrentId(MWAWInputStreamPtr input, std::string const &name)
{
  if (!input||input->size()!=4) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parseCurrentId: can not find the input\n"));
    return false;
  }
  libmwaw::DebugFile ascFile(input);
  ascFile.open(name);
  input->seek(0, librevenge::RVNG_SEEK_SET);
  libmwaw::DebugStream f;
  f << "Entries(CurrentId):";
  auto val=int(input->readLong(4));
  if (val) f << "id=" << val << ",";
  ascFile.addPos(0);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint3OLE::parseCurrentUser(MWAWInputStreamPtr input, std::string const &name)
{
  int const vers=version();
  int const szSize=vers<=4 ? 1 : 4;
  if (!input||input->size() < szSize) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parseCurrentUser: can not find the input\n"));
    return false;
  }
  libmwaw::DebugFile ascFile(input);
  ascFile.open(name);
  input->seek(0, librevenge::RVNG_SEEK_SET);
  long endPos=input->size();
  libmwaw::DebugStream f;
  f << "Entries(CurrentUser):";
  auto sSz=int(input->readULong(szSize));
  if (sSz<0 || sSz>input->size()-szSize) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parseCurrentUser: the stream size seems bad\n"));
    f << "###sSz,";
    ascFile.addPos(0);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  std::string user;
  for (int c=0; c<sSz; ++c) {
    auto ch=char(input->readULong(1));
    if (ch)
      user+=ch;
    else if (c+1!=sSz)
      f << "###";
  }
  f << user;
  ascFile.addPos(0);
  ascFile.addNote(f.str().c_str());
  if (input->tell()!=endPos) {
    ascFile.addPos(input->tell());
    ascFile.addNote("CurrentUser:##extra");
  }
  return true;
}

bool PowerPoint3OLE::parseHeader(MWAWInputStreamPtr input, std::string const &name)
{
  if (!input || input->size()<19) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parseHeader: the input seems bad\n"));
    return false;
  }
  input->seek(0, librevenge::RVNG_SEEK_SET);
  long endPos=input->size();
  libmwaw::DebugFile ascFile(input);
  ascFile.open(name);
  libmwaw::DebugStream f;
  f << "Entries(Headr):";
  std::string text; // Microsoft (R) PowerPoint (R) Windows
  for (int i=0; i<endPos; ++i) {
    auto c=char(input->readULong(1));
    if (!c) break;
    text+=c;
  }
  f << text << ",";
  if (input->tell()+18>endPos) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parseHeader: the input seems short\n"));
    f << "###";
    ascFile.addDelimiter(input->tell(),'|');
    ascFile.addPos(0);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  for (int i=0; i<9; ++i) {
    auto val=int(input->readULong(2));
    int const(expected[])= {7,0,0x3f0,0,0xc05f,0xe391,1,0,0};
    if (val!=expected[i]) f << "f" << i << "=" << std::hex << val << std::dec << ",";
  }
  ascFile.addPos(0);
  ascFile.addNote(f.str().c_str());
  if (input->tell()!=endPos) {
    ascFile.addPos(input->tell());
    ascFile.addNote("Headr:###");
  }
  return true;
}

bool PowerPoint3OLE::parsePersistentStorage(MWAWInputStreamPtr input, std::string const &name)
{
  if (!input || input->size()<62) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parsePersistentStorage: the input seems bad\n"));
    return false;
  }
  input->seek(0, librevenge::RVNG_SEEK_SET);
  long endPos=input->size();
  libmwaw::DebugFile ascFile(input);
  ascFile.open(name);
  libmwaw::DebugStream f;
  f << "Entries(PersistentStorage):";
  int val;
  for (int i=0; i<2; ++i) { // f1=3c6|3e0
    val=int(input->readULong(2));
    int const(expected[])= {7,0x3e0};
    if (val!=expected[i])
      f << "f" << i << "=" << val << ",";
  }
  for (int i=0; i<13; ++i) { // f9=f11=f13=1
    val=int(input->readULong(2));
    if (val) f << "f" << i+2 << "=" << val << ",";
  }
  auto sSz=int(input->readULong(4));
  if (sSz<0 || endPos-30-8<sSz || 30+sSz+8>endPos) {
    MWAW_DEBUG_MSG(("PowerPoint3OLE::parsePersistentStorage: the string size seems bad\n"));
    f << "###sSz=" << sSz << ",";
    ascFile.addPos(0);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  std::string text; // PowerPoint Document
  for (int i=0; i<sSz; ++i) {
    auto c=char(input->readULong(1));
    if (c)
      text+=c;
    else if (i+1!=sSz) {
      MWAW_DEBUG_MSG(("PowerPoint3OLE::parsePersistentStorage: the name seems bad\n"));
      f << "##name,";
    }
  }
  f << text << ",";
  for (int i=0; i<4; ++i) { // g0=1
    val=int(input->readULong(2));
    if (val) f << "g" << i << "=" << val << ",";
  }
  ascFile.addPos(0);
  ascFile.addNote(f.str().c_str());
  if (input->tell()!=endPos) { // seems junk, unsure
    ascFile.addPos(input->tell());
    ascFile.addNote("_");
  }
  return true;
}

////////////////////////////////////////////////////////////
// try to send data
////////////////////////////////////////////////////////////


// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
