/* -*- 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 <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <sstream>

#include <librevenge/librevenge.h>

#include "MWAWFontConverter.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWHeader.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPictBitmap.hxx"
#include "MWAWPictData.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"

#include "ReadySetGoParser.hxx"

#include "ReadySetGoGraph.hxx"
#include "ReadySetGoStyleManager.hxx"

/** Internal: the structures of a ReadySetGoParser */
namespace ReadySetGoParserInternal
{

////////////////////////////////////////
//! Internal: the state of a ReadySetGoParser
struct State {
  //! constructor
  State()
    : m_designStudio(false)
    , m_numLayouts(1)
    , m_numGlossary(0)
    , m_numObjectGlossary(0)
    , m_numStyles(0)
    , m_hasCustomColors(false)
    , m_hasFontsBlock2(false)
  {
  }
  //! a flag to descriminate between ReadySetGo and Design Studio
  bool m_designStudio;
  //! the number of layouts: used for v3
  int m_numLayouts;
  //! the number of glossary: used for v4
  int m_numGlossary;
  //! the number of object glossary: used for v6
  int m_numObjectGlossary;
  //! the number of styles: v4
  int m_numStyles;
  //! a flag to know if the document has custom colors: v5
  bool m_hasCustomColors;
  //! a unknown zone : v6
  bool m_hasFontsBlock2;
};

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
ReadySetGoParser::ReadySetGoParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
  : MWAWGraphicParser(input, rsrcParser, header)
  , m_graphParser()
  , m_styleManager()
  , m_state(new ReadySetGoParserInternal::State)
{
  m_styleManager.reset(new ReadySetGoStyleManager(*this));
  m_graphParser.reset(new ReadySetGoGraph(*this));
  setAsciiName("main-1");
  getPageSpan().setMargins(0.1);
}

ReadySetGoParser::~ReadySetGoParser()
{
}

bool ReadySetGoParser::isDesignStudioFile() const
{
  return m_state->m_designStudio;
}

////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void ReadySetGoParser::parse(librevenge::RVNGDrawingInterface *docInterface)
{
  if (!getInput().get() || !checkHeader(nullptr))  throw(libmwaw::ParseException());
  bool ok = false;
  try {
    // create the asciiFile
    ascii().setStream(getInput());
    ascii().open(asciiName());
    checkHeader(nullptr);

    // update the style manager, ...
    ok = createZones();
    if (ok) {
      createDocument(docInterface);
      ok=getGraphicListener() && m_graphParser->sendPages();
    }
    ascii().reset();
  }
  catch (...) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::parse: exception catched when parsing\n"));
    ok = false;
  }

  resetGraphicListener();
  if (!ok) throw(libmwaw::ParseException());
}

////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void ReadySetGoParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
{
  if (!documentInterface) return;
  if (getGraphicListener()) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::createDocument: listener already exist\n"));
    return;
  }

  // create the page list
  std::vector<MWAWPageSpan> pageList;
  m_graphParser->updatePageSpanList(pageList);
  MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
  setGraphicListener(listen);
  listen->startDocument();
  m_graphParser->sendMasterPages();
}

////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool ReadySetGoParser::createZones()
{
  auto input=getInput();
  int const vers=version();
  bool const designStudio=isDesignStudioFile();
  if (!input) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: no input\n"));
    return false;
  }
  libmwaw::DebugStream f;
  if (vers<3)
    input->seek(0, librevenge::RVNG_SEEK_SET);
  else if (vers==3) {
    input->seek(2, librevenge::RVNG_SEEK_SET);
    if (!readDocument())
      return false;
  }
  else {
    if (!input->checkPosition(vers<=5 ? 100 : 200)) {
      MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: the file seems too short\n"));
      return false;
    }
    input->seek(2, librevenge::RVNG_SEEK_SET);
    f.str("");
    f << "Entries(ZonePos):";
    std::map<long,int> posToId;
    f << "pos=[";
    for (int i=0; i<(vers==4 ? 2 : vers==5 ? 5 : 12); ++i) { // Style, ????
      long posi=input->readLong(4);
      f << std::hex << posi << std::dec << ",";
      if (posi<(vers==4 ? 0x100 : 0x300) || !input->checkPosition(posi)) {
        MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: the %d th positions seems bad\n", i));
        f << "###";
        ascii().addPos(2);
        ascii().addNote(f.str().c_str());
        return false;
      }
      posToId[posi]=i;
    }
    f << "],";
    for (int i=0; i<(vers==4 ? 45 : vers==5 ? 39 : 75); ++i) {
      int val=int(input->readLong(2));
      if (val)
        f << "f" << i << "=" << val << ",";
    }
    ascii().addPos(2);
    ascii().addNote(f.str().c_str());

    // first zone
    if (!readDocument() || !readPrintInfo() || !m_graphParser->readLayoutsList(m_state->m_numLayouts))
      return false;
    // 26 master possible A to Z times 2 (left or right)
    if (vers>=6 && !designStudio && !m_graphParser->readLayoutsList(52, true))
      return false;
    if (!readIdsList() || input->tell()>posToId.begin()->first)
      return false;
    if (input->tell()<posToId.begin()->first) {
      MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: find extra data for the beginning zone\n"));
      ascii().addPos(input->tell());
      ascii().addNote("ZonePos:extra###");
    }
    for (auto posIdIt=posToId.begin(); posIdIt!=posToId.end();) {
      long pos=posIdIt->first;
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      int id=posIdIt->second, origId=id;
      if (vers>=6) {
        if (id<3 || (id>=7 && id<=9) || id==11)
          ;
        // 2: color list?, 3: color name?
        else if (id>=4 && id<=5)
          --id;
        // 6: N, -1, -256 + Nx[8 bytes?]
        else
          id=-1;
      }
      ++posIdIt;
      long endPos=posIdIt==posToId.end() ? -1 : posIdIt->first;
      if (endPos>0)
        input->pushLimit(endPos);
      bool ok=true;
      switch (id) {
      case 0:
        ok=m_styleManager->readStyles(m_state->m_numStyles);
        break;
      case 1:
        ok=readGlossary();
        break;
      case 2:
        if (!m_state->m_hasCustomColors) {
          if (!input->checkPosition(pos+4)) {
            MWAW_DEBUG_MSG(("ReadySetGoParser::createZones[color]: can not find the data\n"));
            ok=false;
            break;
          }
          // normally followed by 0
          ascii().addPos(pos);
          ascii().addNote("_");
          break;
        }
        ok=m_styleManager->readColors();
        break;
      case 3:
        ok=m_styleManager->readColorNames();
        break;
      case 4:
        ok=m_styleManager->readFontsBlock();
        break;
      case 7:
        ok=m_styleManager->readFontsName();
        break;
      case 8:
        ok=readFontsUnknown();
        break;
      case 9:
        ok=readObjectGlossary();
        break;
      case 11:
        ok=readZone11();
        break;
      // 9: N?x40char, then ? maybe link to templates data
      // 10: find len=4c, 2 IDS + 6 small values b,b,c,d,a,a
      default:
        if (vers>=6) {
          input->seek(pos, librevenge::RVNG_SEEK_SET);
          f.str("");
          f << "Entries(Zone" << origId << "A):";
          long len=long(input->readLong(4));
          long zEndPos=pos+4+len;
          if (zEndPos<pos+4 || (endPos>0 && zEndPos>endPos) || (endPos<=0 && !input->checkPosition(zEndPos))) {
            MWAW_DEBUG_MSG(("ReadySetGoParser::createZones[unknown, last]: can not find the data\n"));
            ok=false;
            break;
          }
          ascii().addPos(pos);
          ascii().addNote(f.str().c_str());
          input->seek(zEndPos, librevenge::RVNG_SEEK_SET);
          break;
        }
        MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: find unexpected zone=%d\n", id));
        ok=false;
        break;
      }
      if (endPos>0)
        input->popLimit();
      if (ok) {
        input->popLimit();
        if (endPos>0)
          input->seek(endPos, librevenge::RVNG_SEEK_SET);
        continue;
      }
      ascii().addPos(pos);
      ascii().addNote("Entries(Bad):###");
      if (endPos==-1) // no way to continue
        return false;
    }
    if (m_state->m_hasFontsBlock2 && !m_styleManager->readFontsBlock2())
      return false;
    return m_graphParser->readShapes();
  }

  if (!readPrintInfo())
    return false;

  if (vers==3) {
    if (!m_graphParser->readLayoutsList(m_state->m_numLayouts) || !readIdsList())
      return false;
    // always empty
    long pos=input->tell();
    long len=long(input->readLong(4));
    f.str("");
    f << "Entries(Zone0):";
    if ((long)((unsigned long)pos+4+(unsigned long)len)<pos+4 || !input->checkPosition(pos+4+len)) {
      MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: can not find a initial zone0\n"));
      f << "###";
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
      return false;
    }
    if (len==0) {
      ascii().addPos(pos);
      ascii().addNote("_");
    }
    else {
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
      input->seek(pos+4+len, librevenge::RVNG_SEEK_SET);
    }
  }
  if (!m_graphParser->readShapes())
    return false;

  if (!input->isEnd()) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::createZones: find extra data\n"));
    ascii().addPos(input->tell());
    ascii().addNote("Entries(Extra):###");
  }
  return true;
}


////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool ReadySetGoParser::checkHeader(MWAWHeader *header, bool strict)
{
  *m_state = ReadySetGoParserInternal::State();
  MWAWInputStreamPtr input = getInput();
  if (!input || !input->hasDataFork() || !input->checkPosition(126))
    return false;

  libmwaw::DebugStream f;
  f << "FileHeader:";
  input->seek(0, librevenge::RVNG_SEEK_SET);
  int val=int(input->readULong(2));
  f << "vers=" << val << ",";
  int vers=1;
  m_state->m_designStudio=false;
  if (val==0x1e) { // 30
    if (input->readULong(2) || input->readULong(2)!=0x86)
      return false;
    vers=3;
  }
  else if (val>=400 && val<=402) // I only see 400
    vers=4;
  else if (val>=5000 && val<=5005) // I only see 5003 v4.5
    vers=5;
  else if (val>=6000 && val<=6003) //I only see 6000
    vers=6;
  // I only find some demo files, these files look very simillar to v6 files
  //   I try to modified the parser to parse them, but there remains some
  //   problems at least colors' problems
  else if (val==7100) // 7100 v7.1
    vers=7;
  else if (val==0x13e4) { // Design Studio, visibly was forked between RSG 4.5 and RSG 6
    f << "designStudio,";
    m_state->m_designStudio=true;
    vers=6;
  }
  else if (val!=0x78)
    return false;
  ascii().addPos(0);
  ascii().addNote(f.str().c_str());

  if (vers<3) {
    // we need to retrieve the version v1 or v2
    input->seek(2+120, librevenge::RVNG_SEEK_SET);
    int n=int(input->readULong(2));
    if (n<=0)
      return false;

    //
    // first test for version 2, the more structured
    //
    bool ok=true;
    vers=2;
    int nShapes=0;
    for (int p=0; p<n; ++p) {
      long pos=input->tell();
      if (!input->checkPosition(pos+2)) {
        ok=false;
        break;
      }
      val=int(input->readLong(2));
      if (val<0) {
        ok=false;
        break;
      }
      nShapes+=val;
    }
    for (int s=0; s<nShapes; ++s) {
      long pos=input->tell();
      if (!ok || !input->checkPosition(pos+4)) {
        ok=false;
        break;
      }
      int type=int(input->readULong(2));
      if (type<0 || type>6) {
        ok=false;
        break;
      }
      input->seek(2, librevenge::RVNG_SEEK_CUR);
      for (int i=0; i<2; ++i) {
        pos=input->tell();
        int len=int(input->readULong(2));
        if ((i==0 && len!=0x1c) || !input->checkPosition(pos+2+len)) {
          ok=false;
          break;
        }
        input->seek(pos+2+len, librevenge::RVNG_SEEK_SET);
      }
      if (!ok)
        break;
      if (type==3 && !input->isEnd()) {
        // we must check if there is a picture
        pos=input->tell();
        int len=int(input->readULong(2));
        if (len<10)
          input->seek(pos, librevenge::RVNG_SEEK_SET);
        else {
          if (!input->checkPosition(pos+2+len)) {
            ok=false;
            break;
          }
          input->seek(pos+2+len, librevenge::RVNG_SEEK_SET);
        }
      }
      if (type!=4)
        continue;
      for (int st=0; st<2; ++st) { // zone1=[text, style], zone2=[para]
        pos=input->tell();
        int len=int(input->readULong(2));
        if (!input->checkPosition(pos+2+len)) {
          ok=false;
          break;
        }
        input->seek(pos+2+len, librevenge::RVNG_SEEK_SET);
      }
    }
    if (ok && nShapes<=10 && !input->isEnd())
      ok=false;

    if (!ok) {
      // test for version 1
      ok=true;
      vers=1;
      input->seek(2+120+2, librevenge::RVNG_SEEK_SET);
      for (int i=0; i<std::min(10,n); ++i) {
        long pos=input->tell();
        if (!input->checkPosition(pos+26)) {
          ok=false;
          break;
        }
        int type=int(input->readLong(2));
        if (type<0 || type>5 || type==2) {
          ok=false;
          break;
        }
        int const expectedSize[]= {26, 74, 0, 30, 28, 28};
        if (expectedSize[type]<=0 || !input->checkPosition(pos+expectedSize[type])) {
          ok=false;
          break;
        }
        input->seek(pos+expectedSize[type], librevenge::RVNG_SEEK_SET);
        if (type==0 && i+1!=n) {
          ok=false;
          break;
        }
        if (type==5 && !input->isEnd()) {
          // we must check if there is a picture
          pos=input->tell();
          int len=int(input->readULong(2));
          if (len<10)
            input->seek(pos, librevenge::RVNG_SEEK_SET);
          else {
            if (!input->checkPosition(pos+2+len)) {
              ok=false;
              break;
            }
            input->seek(pos+2+len, librevenge::RVNG_SEEK_SET);
          }
        }
        if (type!=1)
          continue;
        for (int st=0; st<2; ++st) {
          pos=input->tell();
          int len=int(input->readULong(2));
          if (!input->checkPosition(pos+2+len)) {
            ok=false;
            break;
          }
          input->seek(pos+2+len, librevenge::RVNG_SEEK_SET);
        }
      }
      if (ok && n<=10 && !input->isEnd())
        ok=false;
    }
    if (!ok)
      return false;
  }
  else if (vers==3) {
    if (strict) {
      input->seek(2, librevenge::RVNG_SEEK_SET);
      for (int i=0; i<3; ++i) {
        long pos=input->tell();
        long len=long(input->readLong(4));
        if ((long)((unsigned long)pos+4+(unsigned long)len)<pos+4 || !input->checkPosition(pos+4+len))
          return false;
        if (len==0 && i<2)
          return false;
        input->seek(pos+4+len, librevenge::RVNG_SEEK_SET);
      }
    }
  }
  else { // vers>3
    if (vers>=6 && !input->checkPosition(200+20))
      return false;

    input->seek(2, librevenge::RVNG_SEEK_SET);
    // first check the initial list of pointers
    int emptyZone=0;
    for (int i=0; i<(vers==4 ? 2 : vers==5 ? 5 : 12); ++i) {
      long pos=long(input->readLong(4));
      if (pos==0)
        ++emptyZone;
      else if (pos<(vers==4 ? 0x100 : 0x300) || !input->checkPosition(pos))
        return false;
    }
    if (emptyZone>1)
      return false;
    if (strict) {
      input->seek(vers<=5 ? 100 : 200, librevenge::RVNG_SEEK_SET);
      for (int step=0; step<4; ++step) {
        long pos=input->tell();
        long len=long(input->readLong(4));
        if (pos+4+len<pos+len || !input->checkPosition(pos+4+len))
          return false;
        if (step==0 && len<(vers==4 ? 0xcc : vers==5 ? 0x188 : vers==6 ? (m_state->m_designStudio ? 0x2be : 0x58c) : 0x5a0))
          return false;
        if (step==1 && len!=0x78)
          return false;
        input->seek(pos+4+len, librevenge::RVNG_SEEK_SET);
      }
    }
  }
  setVersion(vers);
  if (header)
    header->reset(MWAWDocument::MWAW_T_READYSETGO, vers, MWAWDocument::MWAW_K_DRAW);

  return true;
}

////////////////////////////////////////////////////////////
// try to read the main zone
////////////////////////////////////////////////////////////
bool ReadySetGoParser::readDocument()
{
  int const vers=version();
  if (vers<3) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: unexpected version\n"));
    return false;
  }
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  long pos=input->tell();
  if (!input->checkPosition(pos+4)) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: can not read the zone length\n"));
    return false;
  }
  libmwaw::DebugStream f;
  f << "Entries(Document):";
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if (len<0 || !input->checkPosition(endPos)) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: can not read the zone length\n"));
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    return false;
  }
  bool const designStudio=isDesignStudioFile();
  long const expectedLength=vers==3 ? 0x86 : vers==4 ? 0xcc : vers==5 ? 0x188 : vers== 6 ? (designStudio ? 0x2be : 0x58c) : 0x5a0; // v7.1: I find len=5a0 or len=211b
  if (len<expectedLength) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: unexpected zone length\n"));
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    return true;
  }
  if (len>expectedLength) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: find extra data\n"));
    ascii().addPos(pos+4+expectedLength);
    ascii().addNote("Document-extra:###");
  }
  f << "ID=" << std::hex << input->readULong(4) << std::dec << ",";
  if (vers>=6) {
    int cLen=int(input->readULong(1));
    if (cLen>63) {
      f << "###";
      MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: unexpected file name len\n"));
      cLen=0;
    }
    std::string name;
    for (int i=0; i<cLen; ++i) {
      char c=char(input->readLong(1));
      if (c==0)
        break;
      name+=c;
    }
    f << "printer=" << name << ",";
    input->seek(pos+4+4+64, librevenge::RVNG_SEEK_SET);
    for (int i=0; i<8; ++i) { // f2=0|6|8
      int val=int(input->readLong(2));
      int const expected[]= {0,0,0,100,0,256,2,0};
      if (val!=expected[i])
        f << "f" << i << "=" << val << ",";
    }
    for (int i=0; i<12; ++i) {
      int val=int(input->readLong(2));
      if (val)
        f << "g" << i << "=" << val << ",";
    }
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    input->seek(pos+4+108, librevenge::RVNG_SEEK_SET);

    pos=input->tell();
    f.str("");
    f << "Document-0:";
  }
  int val=int(input->readLong(2));
  if (val!=1)
    f << "first[page]=" << val << ",";
  m_state->m_numLayouts=int(input->readLong(2));
  if (m_state->m_numLayouts)
    f << "num[layout]=" << m_state->m_numLayouts << ",";
  val=int(input->readLong(2));
  if (val+1!=m_state->m_numLayouts)
    f << "act[layout]=" << val << ",";
  if (vers>=6 && !designStudio) {
    f << "IDS=[";
    for (int i=0; i<2; ++i)
      f << std::hex << input->readULong(4) << std::dec << ",";
    f << "],";
    val=int(input->readLong(2));
    if (val)
      f << "num[unkn]=" << val << ",";
    f << "ID0=[";
    for (int i=0; i<2; ++i)
      f << std::hex << input->readULong(4) << std::dec << ",";
    f << "],";
  }
  else {
    f << "IDS=[";
    for (int i=0; i<3; ++i)
      f << std::hex << input->readULong(4) << std::dec << ",";
    f << "],";
  }
  val=int(input->readLong(2));
  if (val) {
    m_state->m_numGlossary=val;
    f << "num[glossary]=" << val << ",";
  }
  f << "ID1=" << std::hex << input->readULong(4) << std::dec << ",";
  if (vers>3) {
    m_state->m_numStyles=int(input->readLong(2));
    if (m_state->m_numStyles)
      f << "num[styles]=" << m_state->m_numStyles << ",";
    f << "ID2=" << std::hex << input->readULong(4) << std::dec << ",";
  }
  f << "margins=[";
  for (int i=0; i<4; ++i) f << float(input->readLong(4))/65536 << ",";
  f << "],";
  f << "unkns=[";
  for (int i=0; i<2; ++i) f << float(input->readLong(4))/65536 << ",";
  f << "],";
  for (int i=0; i<2; ++i) {
    val=int(input->readLong(2));
    int const expected[]= {4,2};
    if (val!=expected[i])
      f << "f" << i+3 << "=" << val << ",";
  }
  if (vers>3) {
    for (int i=0; i<2; ++i) {
      val=int(input->readLong(1));
      if (val==-1)
        f << "fl" << i << ",";
      else if (val)
        f << "fl" << i << "=" << val << ",";
    }
  }
  f << "ID3=" << std::hex << input->readULong(4) << std::dec << ",";
  val=int(input->readLong(2));
  if (val)
    f << "f4=" << val << ",";
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());

  pos=input->tell();
  f.str("");
  f << "Document-A:";
  if (vers>3) {
    int cLen=int(input->readULong(1));
    if (cLen>61) {
      f << "###";
      MWAW_DEBUG_MSG(("ReadySetGoParser::readDocument: unexpected file name len\n"));
      cLen=0;
    }
    std::string name;
    for (int i=0; i<cLen; ++i) {
      char c=char(input->readLong(1));
      if (c==0)
        break;
      name+=c;
    }
    f << "file=" << name << ",";
    input->seek(pos+62, librevenge::RVNG_SEEK_SET);
    ascii().addDelimiter(input->tell(),'|');
    if (vers>=6) {
      input->seek(pos+62+12, librevenge::RVNG_SEEK_SET);
      ascii().addDelimiter(input->tell(),'|');
      input->seek(pos+62+12+25*6, librevenge::RVNG_SEEK_SET);
      ascii().addDelimiter(input->tell(),'|');
      // then something like 0009206000088000000b00000001000000020003
    }
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());

    if (vers>=5) {
      input->seek(pos+(vers<=5 ? 134 : 244), librevenge::RVNG_SEEK_SET);
      pos=input->tell();
      f.str("");
      f << "Document-B:";
      f << "IDS=[";
      for (int i=0; i<4; ++i)
        f << std::hex << input->readULong(4) << std::dec << ",";
      f << "],";
      ascii().addDelimiter(input->tell(),'|');
      input->seek(pos+24, librevenge::RVNG_SEEK_SET);
      ascii().addDelimiter(input->tell(),'|');
      val=int(input->readULong(4));
      if (val) {
        m_state->m_hasCustomColors=true;
        f << "color[IDS]=" << std::hex << val << std::dec << ",";
      }
      if (vers>=6) {
        val=int(input->readULong(4));
        if (val)
          f << "color[IDS,unkn]=" << std::hex << val << std::dec << ",";
      }
      val=int(input->readULong(4));
      if (val)
        f << "color[IDS,name]=" << std::hex << val << std::dec << ",";
      ascii().addDelimiter(input->tell(),'|');
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
    }
    if (vers>=6) {
      input->seek(pos+188, librevenge::RVNG_SEEK_SET);
      pos=input->tell();
      f.str("");
      f << "Document-C:";
      f << "IDS=[";
      for (int i=0; i<4; ++i)
        f << std::hex << input->readULong(4) << std::dec << ",";
      f << "],";
      for (int i=0; i<2; ++i) { // f0=0|2bc
        val=int(input->readULong(2));
        int const expected[]= { 0, 0x1f21};
        if (val!=expected[i])
          f << "f" << i << "=" << val << ",";
      }
      int dim[2];
      for (auto &d : dim) d=int(input->readULong(2));
      f << "dim=" << MWAWVec2i(dim[1],dim[0]) << ",";
      f << "margins?=[";
      for (int i=0; i<4; ++i) f << input->readLong(2) << ",";
      f << "],";
      for (int i=0; i<4; ++i) {
        val=int(input->readULong(2));
        if (val!=(i==2 ? 90 : 0))
          f << "f" << i+2 << "=" << val << ",";
      }
      if (designStudio)
        ascii().addDelimiter(input->tell(),'|');
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
    }
    if (vers>=6 && !designStudio) {
      for (int i=0; i<26; ++i) {
        pos=input->tell();
        f.str("");
        f << "Document-D" << i << ":";
        // 000600008000000080000000800000008000000040000002ff8[01]
        input->seek(pos+26, librevenge::RVNG_SEEK_SET);
        ascii().addPos(pos);
        ascii().addNote(f.str().c_str());
      }
      pos=input->tell();
      f.str("");
      f << "Document-E:";
      m_state->m_numObjectGlossary=int(input->readLong(2));
      if (m_state->m_numObjectGlossary) f << "num[object,glossary]=" << m_state->m_numObjectGlossary << ",";
      f << "ID0=" << std::hex << input->readULong(4) << std::dec << ",";
      for (int i=0; i<3; ++i) {
        val=int(input->readLong(2));
        if (val) f << "f" << i << "=" << val << ",";
      }
      f << "ID1=" << std::hex << input->readULong(4) << std::dec << ",";
      for (int i=0; i<7; ++i) { // f4=0|256
        val=int(input->readLong(2));
        if (val) f << "f" << i+4 << "=" << val << ",";
      }
      for (int i=0; i<7; ++i) {
        val=int(input->readLong(2));
        int const expected[]= {-1, 45, 0, 60, 0, 0, 300};
        if (val!=expected[i]) f << "f" << i+11 << "=" << val << ",";
      }
      val=int(input->readULong(4));
      if (val) { // checkme
        f << "ID2[font]=" << std::hex << val << std::dec << ",";
        m_state->m_hasFontsBlock2=true;
      }

      ascii().addDelimiter(input->tell(),'|');
      // endPos-4: int16: ragzone percent, a flag:0,1 related to ragged method?
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
    }

    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    return true;
  }
  // vers <= 3
  std::string name;
  for (int i=0; i<62; ++i) { // checkme, maybe a string of size 32, followed by...
    char c=char(input->readLong(1));
    if (c==0)
      break;
    name+=c;
  }
  f << "file=" << name << ",";
  input->seek(pos+62, librevenge::RVNG_SEEK_SET);
  int dim[2];
  for (auto &d : dim) d=int(input->readLong(2));
  f << "dim=" << MWAWVec2i(dim[0],dim[1]) << ",";
  val=int(input->readLong(2));
  if (val!=1) f << "unit=" << val << ","; // inch, centimeters, pica
  for (int i=0; i<4; ++i) {
    val=int(input->readLong(1));
    if (val==-1)
      continue;
    if (i==0)
      f << "hide[ruler]";
    else if (i==1)
      f << "hide[grid]";
    else
      f << "fl" << i;
    if (val!=0)
      f << "=" << val << ",";
    else
      f << ",";
  }
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());

  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}

bool ReadySetGoParser::readIdsList()
{
  if (version()<3) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readIdsList: unexpected version\n"));
    return false;
  }
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  long pos=input->tell();
  if (!input->checkPosition(pos+4)) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readIdsList: can not read the zone length\n"));
    return false;
  }
  libmwaw::DebugStream f;
  f << "Entries(IDLists):";
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if (len<0 || endPos<pos+4 || !input->checkPosition(endPos)) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readIdsList: can not read the zone length\n"));
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    return false;
  }
  if (len==0) {
    ascii().addPos(pos);
    ascii().addNote("_");
    return true;
  }
  if (len%4) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readIdsList: can not determine the number of IDS\n"));
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    return true;
  }
  f << "ids=[";
  for (int i=0; i<len/4; ++i) {
    auto val=input->readULong(4);
    if (val)
      f << std::hex << val << std::dec << ",";
    else
      f << "_,";
  }
  f << "],";
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());
  input->seek(endPos, librevenge::RVNG_SEEK_SET);

  return true;
}

////////////////////////////////////////////////////////////
// try to read the shapes zone
////////////////////////////////////////////////////////////
bool ReadySetGoParser::readGlossary()
{
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  int const vers=version();
  long pos=input->tell();
  libmwaw::DebugStream f;
  f << "Entries(Glossary):";
  if (vers<4) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary: unexpected version\n"));
    return false;
  }
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if (m_state->m_numGlossary<0 || len<52*m_state->m_numGlossary || endPos<pos+4 || !input->checkPosition(endPos)) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary: the zone's length seems bad\n"));
    return false;
  }

  if (len==0) {
    ascii().addPos(pos);
    ascii().addNote("_");
    return true;
  }
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());
  std::vector<bool> hasTabs;
  for (int i=0; i<m_state->m_numGlossary; ++i) {
    pos=input->tell();
    f.str("");
    f << "Glossary-" << i << ":";
    int cLen=int(input->readULong(1));
    if (cLen>35) {
      MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary: the name's length seems bad\n"));
      f << "###";
      cLen=0;
    }
    std::string name;
    for (int c=0; c<cLen; ++c) {
      char ch=char(input->readLong(1));
      if (!ch)
        break;
      name+=ch;
    }
    f << name << ",";
    input->seek(pos+36, librevenge::RVNG_SEEK_SET);
    f << "IDS=[";
    for (int j=0; j<4; ++j) {
      auto id=input->readULong(4);
      if (j==2)
        hasTabs.push_back(id!=0);
      if (!id)
        f << "_,";
      else
        f << std::hex << id << std::dec << ",";
    }
    f << "],";
    input->seek(pos+52, librevenge::RVNG_SEEK_SET);
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
  }
  input->seek(endPos, librevenge::RVNG_SEEK_SET);

  for (size_t i=0; i<size_t(m_state->m_numGlossary); ++i) {
    for (int step=0; step<(hasTabs[i] ? 3 : 2); ++step) {
      pos=input->tell();
      len=long(input->readLong(4));
      f.str("");
      f << "Glossary-" << (step==0 ? "text" : step==1 ? "style" : "tabs") << "[" << i << "]:";
      endPos=pos+4+len;
      if (endPos<pos+4 || !input->checkPosition(endPos)) {
        MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary: can not find a data block\n"));
        f << "###";
        ascii().addPos(pos);
        ascii().addNote(f.str().c_str());
        return false;
      }
      switch (step) {
      case 0: {
        int const begTextPos=vers<6 ? 18 : 58;
        if (len<begTextPos+2) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary[text]: the zone length seems bad\n"));
          f << "###";
          break;
        }
        int cLen=int(input->readULong(4));
        f << "N=" << cLen << ",";
        if (cLen+begTextPos>len || (long)((unsigned long)cLen+(unsigned long)begTextPos)<begTextPos) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary: can not read the number of caracters\n"));
          f << "###";
          cLen=0;
        }
        for (int j=0; j<2; ++j) { // maybe last change font/para
          int val=int(input->readLong(4));
          if (val!=cLen)
            f << "N" << j+1 << "=" << val << ",";
        }
        f << "IDS=[";
        for (int j=0; j<2; ++j) {
          auto val=input->readULong(4);
          if (val)
            f << std::hex << val << std::dec << ",";
          else
            f << "_,";
        }
        f << "],";
        if (vers>=6) {
          ascii().addDelimiter(input->tell(),'|');
          input->seek(38, librevenge::RVNG_SEEK_CUR);
          ascii().addDelimiter(input->tell(),'|');
        }
        for (int c=0; c<cLen; ++c) {
          unsigned char ch=(unsigned char)input->readULong(1);
          if (ch<0x1f && ch!=0x9)
            f << "[#" << std::hex << int(ch) << std::dec << "]";
          else
            f << ch;
        }
        break;
      }
      case 1: {
        if (len<4) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary[style]: the zone length seems bad\n"));
          f << "###";
          break;
        }
        int N=int(input->readULong(4));
        f << "N=" << N << ",";
        if (N<0 || (len-4)/(vers==4 ? 30 : vers==5 ? 38 : 48)<N) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary[style]: can not detect the number of styles\n"));
          f << "###";
          break;
        }
        for (int j=0; j<N; ++j) {
          int cPos;
          MWAWFont font;
          MWAWParagraph para;
          if (!m_styleManager->readStyle(font, para, &cPos))
            break;
        }
        break;
      }
      case 2: {
        if (len<2) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary[tab]: the zone length seems bad\n"));
          f << "###";
          break;
        }
        int N=int(input->readULong(2));
        f << "N=" << N << ",";
        if (N<0 || (len-2)/148<N) {
          MWAW_DEBUG_MSG(("ReadySetGoParser::readGlossary[tab]: can not detect the number of tabulations\n"));
          f << "###";
          break;
        }
        for (int j=0; j<N; ++j) {
          int cPos;
          std::vector<MWAWTabStop> tabs;
          if (!m_styleManager->readTabulations(tabs, 148, &cPos))
            break;
        }
        break;
      }
      default:
        break;
      }
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
      input->seek(endPos, librevenge::RVNG_SEEK_SET);
    }
  }

  return true;
}

bool ReadySetGoParser::readObjectGlossary()
{
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  int const vers=version();
  long pos=input->tell();
  libmwaw::DebugStream f;
  f << "Entries(ObjGloss):";
  if (vers<6) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readObjectGlossary: unexpected version\n"));
    return false;
  }
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if (m_state->m_numObjectGlossary<0 || len<40*m_state->m_numObjectGlossary || endPos<pos+4 || !input->checkPosition(endPos)) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readObjectGlossary: the zone's length seems bad\n"));
    return false;
  }

  if (len==0) {
    ascii().addPos(pos);
    ascii().addNote("_");
    return true;
  }
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());
  for (int i=0; i<m_state->m_numObjectGlossary; ++i) {
    pos=input->tell();
    f.str("");
    f << "ObjGloss-" << i << ":";
    int cLen=int(input->readULong(1));
    if (cLen>35) {
      MWAW_DEBUG_MSG(("ReadySetGoParser::readObjectGlossary: the name's length seems bad\n"));
      f << "###";
      cLen=0;
    }
    std::string name;
    for (int c=0; c<cLen; ++c) {
      char ch=char(input->readLong(1));
      if (!ch)
        break;
      name+=ch;
    }
    f << name << ",";
    input->seek(pos+36, librevenge::RVNG_SEEK_SET);
    f << "IDS=[";
    for (int j=0; j<1; ++j) {
      auto id=input->readULong(4);
      if (!id)
        f << "_,";
      else
        f << std::hex << id << std::dec << ",";
    }
    f << "],";
    input->seek(pos+40, librevenge::RVNG_SEEK_SET);
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
  }
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  for (int i=0; i<m_state->m_numObjectGlossary; ++i) {
    if (!m_graphParser->readShapesInObject())
      return false;
  }

  return true;
}

bool ReadySetGoParser::readFontsUnknown()
{
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  int const vers=version();
  long pos=input->tell();
  libmwaw::DebugStream f;
  f << "Entries(FontUnkn):";
  if (vers<6) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readFontsUnknown: unexpected version\n"));
    return false;
  }
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if ((len && (len%8)!=2) || endPos<pos+4 || !input->checkPosition(endPos)) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readFontsUnknown: the zone's length seems bad\n"));
    return false;
  }

  if (len==0) {
    ascii().addPos(pos);
    ascii().addNote("_");
    return true;
  }

  int N=int(input->readULong(2));
  f << "N=" << N << ",";
  if (8*N+2 != len) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readFontsUnknown: can not read the number of elements\n"));
    return false;
  }
  f << "fonts=[";
  for (int i=0; i<N; ++i) {
    f << "[";
    f << "fId=" << input->readULong(2) << ",";
    int val=int(input->readULong(1));
    if (val) f << "fFlags=" << std::hex << val << std::dec << ",";
    val=int(input->readULong(1));
    if (val)
      f << "fl=" << val << ",";
    val=int(input->readULong(4));
    if (val)
      f << "ID=" << std::hex << val << std::dec << ",";
    f << "],";
  }
  f << "],";
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());
  for (int i=0; i<N; ++i) {
    pos=input->tell();
    f.str("");
    f << "FontUnkn-" << i << ":";
    len=long(input->readLong(4));
    endPos=pos+4+len;
    if (endPos<pos+4 || len<2 || !input->checkPosition(endPos)) {
      f << "###";
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
      MWAW_DEBUG_MSG(("ReadySetGoParser::readFontsUnknown: can not find a sub zone\n"));
      return false;
    }
    int N1=int(input->readULong(2));
    f << "N=" << N1 << ",";
    if (2+4*N1>len) {
      f << "###";
      ascii().addPos(pos);
      ascii().addNote(f.str().c_str());
      MWAW_DEBUG_MSG(("ReadySetGoParser::readFontsUnknown: can not find the number of data\n"));
      return false;
    }
    ascii().addDelimiter(input->tell(),'|');
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
  }
  return true;
}

bool ReadySetGoParser::readZone11()
{
  MWAWInputStreamPtr input = getInput();
  if (!input)
    return false;
  int const vers=version();
  long pos=input->tell();
  libmwaw::DebugStream f;
  f << "Entries(Zone11A):";
  if (vers<6) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readZone11: unexpected version\n"));
    return false;
  }
  long len=long(input->readLong(4));
  long endPos=pos+4+len;
  if ((len && len<0x138) || endPos<pos+4 || !input->checkPosition(endPos)) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readZone11: the zone's length seems bad\n"));
    return false;
  }

  if (len==0) {
    ascii().addPos(pos);
    ascii().addNote("_");
    return true;
  }

  input->seek(pos+300, librevenge::RVNG_SEEK_SET);
  ascii().addDelimiter(input->tell(), '|');
  int N=int(input->readULong(2));
  f << "N=" << N << ",";
  if (len<0x138+28*N) {
    f << "###";
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    MWAW_DEBUG_MSG(("ReadySetGoParser::readZone11: can not read the number of data\n"));
    return false;
  }
  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());

  input->seek(pos+4+0x138, librevenge::RVNG_SEEK_SET);
  for (int i=0; i<N; ++i) {
    pos=input->tell();
    f.str("");
    f << "Zone11A-" << i << ":";
    input->seek(pos+28, librevenge::RVNG_SEEK_SET);
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
  }
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}

////////////////////////////////////////////////////////////
// try to read the print info zone
////////////////////////////////////////////////////////////
bool ReadySetGoParser::readPrintInfo()
{
  MWAWInputStreamPtr input = getInput();
  int const vers=version();
  long pos=input->tell();
  long endPos=pos+120+(vers<3 ? 2 : 4);
  if (!input->checkPosition(endPos) || input->readULong((vers<3 ? 2 : 4))!=0x78) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readPrintInfo: file seems too short\n"));
    return false;
  }
  libmwaw::DebugStream f;
  f << "Entries(PrintInfo):";
  libmwaw::PrinterInfo info;
  if (!info.read(input)) {
    MWAW_DEBUG_MSG(("ReadySetGoParser::readPrintInfo: can not read print info\n"));
    return false;
  }
  f << info;
  MWAWVec2i paperSize = info.paper().size();
  MWAWVec2i pageSize = info.page().size();
  if (pageSize.x() <= 0 || pageSize.y() <= 0 ||
      paperSize.x() <= 0 || paperSize.y() <= 0) {
    ascii().addPos(pos);
    ascii().addNote(f.str().c_str());
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    return true;
  }

  // define margin from print info
  MWAWVec2i lTopMargin= -1 * info.paper().pos(0);
  MWAWVec2i rBotMargin=info.paper().size() - info.page().size();

  // move margin left | top
  int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0;
  int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0;
  lTopMargin -= MWAWVec2i(decalX, decalY);
  rBotMargin += MWAWVec2i(decalX, decalY);

  // decrease right | bottom
  int rightMarg = rBotMargin.x() -50;
  if (rightMarg < 0) rightMarg=0;
  int botMarg = rBotMargin.y() -50;
  if (botMarg < 0) botMarg=0;

  getPageSpan().setMarginTop(lTopMargin.y()/72.0);
  getPageSpan().setMarginBottom(botMarg/72.0);
  getPageSpan().setMarginLeft(lTopMargin.x()/72.0);
  getPageSpan().setMarginRight(rightMarg/72.0);
  getPageSpan().setFormLength(paperSize.y()/72.);
  getPageSpan().setFormWidth(paperSize.x()/72.);

  ascii().addPos(pos);
  ascii().addNote(f.str().c_str());
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}

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