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

#include <librevenge/librevenge.h>

#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWListener.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"

#include "RagTime5ClusterManager.hxx"
#include "RagTime5Parser.hxx"
#include "RagTime5StructManager.hxx"
#include "RagTime5StyleManager.hxx"

#include "RagTime5Layout.hxx"

#include "libmwaw_internal.hxx"

/** Internal: the structures of a RagTime5Layout */
namespace RagTime5LayoutInternal
{
//! the layout cluster ( 4001 zone)
struct ClusterLayout : public RagTime5ClusterManager::Cluster {
  //! constructor
  ClusterLayout() : Cluster(C_Layout), m_zoneList(), m_pipelineLink(), m_listItemLink(), m_childIdSet(),
    m_numChild(0), m_numMasterChild(0)
  {
  }
  //! destructor
  virtual ~ClusterLayout() {}

  //! a zone of in a layout
  struct Zone {
    //! constructor
    Zone() : m_mainId(0), m_masterId(0), m_dimension()
    {
    }
    //! the main zone id
    int m_mainId;
    //! the master zone id or 0
    int m_masterId;
    //! the dimension
    MWAWVec2f m_dimension;
  };

  //! list of zone's
  std::vector<Zone> m_zoneList;
  //! link to a pipeline cluster list
  RagTime5ClusterManager::Link m_pipelineLink;
  //! link to  a zone of fieldSize 8(unknown)
  RagTime5ClusterManager::Link m_listItemLink;
  //! list of child id
  std::set<int> m_childIdSet;
  //! the number of classic child
  int m_numChild;
  //! the number of master child
  int m_numMasterChild;
};

////////////////////////////////////////
//! Internal: the state of a RagTime5Layout
struct State {
  //! constructor
  State() : m_numPages(-1), m_idLayoutMap(), m_masterIdSet(), m_layoutIdToSendList() { }
  //! the number of pages
  int m_numPages;
  //! map data id to text zone
  std::map<int, shared_ptr<ClusterLayout> > m_idLayoutMap;
  //! the list of master id
  std::set<int> m_masterIdSet;
  //! the list of layout to send
  std::vector<int> m_layoutIdToSendList;
};

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
RagTime5Layout::RagTime5Layout(RagTime5Parser &parser) :
  m_mainParser(parser), m_structManager(m_mainParser.getStructManager()),
  m_parserState(parser.getParserState()), m_state(new RagTime5LayoutInternal::State)
{
}

RagTime5Layout::~RagTime5Layout()
{
}

int RagTime5Layout::version() const
{
  return m_parserState->m_version;
}

int RagTime5Layout::numPages() const
{
  if (m_state->m_numPages<0)
    const_cast<RagTime5Layout *>(this)->updateLayouts();
  return m_state->m_numPages;
}

bool RagTime5Layout::sendPageContents()
{
  int page=0;
  for (size_t i=0; i<m_state->m_layoutIdToSendList.size(); ++i) {
    int lId=m_state->m_layoutIdToSendList[i];
    if (m_state->m_idLayoutMap.find(lId)==m_state->m_idLayoutMap.end() || !m_state->m_idLayoutMap.find(lId)->second) {
      MWAW_DEBUG_MSG(("RagTime5Layout::sendPageContents: can not find layout %d\n", lId));
      continue;
    }
    RagTime5LayoutInternal::ClusterLayout &layout=*m_state->m_idLayoutMap.find(lId)->second;
    layout.m_isSent=true;
    size_t n=layout.m_zoneList.size();
    for (size_t p=0; p<n; ++p) {
      MWAWPosition position(MWAWVec2f(0,0), MWAWVec2f(100,100), librevenge::RVNG_POINT);
      position.m_anchorTo=MWAWPosition::Page;
      position.setPage(++page);
      RagTime5LayoutInternal::ClusterLayout::Zone &zone=layout.m_zoneList[p];
      if (zone.m_masterId) {
        if (m_state->m_idLayoutMap.find(zone.m_masterId)==m_state->m_idLayoutMap.end() || !m_state->m_idLayoutMap.find(zone.m_masterId)->second) {
          MWAW_DEBUG_MSG(("RagTime5Layout::sendPageContents: can not find layout %d\n", zone.m_masterId));
        }
        else {
          RagTime5LayoutInternal::ClusterLayout &master=*m_state->m_idLayoutMap.find(zone.m_masterId)->second;
          int cId=0;
          if (master.m_zoneList.size()==1)
            cId=master.m_zoneList[0].m_mainId;
          else if (n<master.m_zoneList.size())
            cId=master.m_zoneList[n].m_mainId;
          if (cId)
            m_mainParser.send(cId, position);
        }
      }
      if (zone.m_mainId)
        m_mainParser.send(zone.m_mainId, position);
    }
  }
  return true;
}

////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
void RagTime5Layout::updateLayouts()
{
  std::map<int, shared_ptr<RagTime5LayoutInternal::ClusterLayout> >::iterator it;
  for (it=m_state->m_idLayoutMap.begin(); it!=m_state->m_idLayoutMap.end(); ++it) {
    if (!it->second) continue;
    updateLayout(*it->second);
  }
  // look for no master layout
  int nPages=0;
  std::multimap<int,int> numZonesToLayout;
  for (it=m_state->m_idLayoutMap.begin(); it!=m_state->m_idLayoutMap.end(); ++it) {
    if (!it->second || it->second->m_zoneList.empty() || m_state->m_masterIdSet.find(it->first)!=m_state->m_masterIdSet.end()) {
      if (it->second)
        it->second->m_isSent=true;
      continue;
    }
    numZonesToLayout.insert(std::multimap<int,int>::value_type(it->second->m_numChild,it->first));
    nPages+=(int) it->second->m_zoneList.size();
  }
  m_state->m_numPages=nPages;

  std::multimap<int,int>::iterator lIt;
  for (lIt=numZonesToLayout.end(); lIt!=numZonesToLayout.begin();)
    m_state->m_layoutIdToSendList.push_back((--lIt)->second);
}

void RagTime5Layout::updateLayout(RagTime5LayoutInternal::ClusterLayout &layout)
{
  int numChild=0, numMasterChild=0;
  for (size_t i=0; i<layout.m_zoneList.size(); ++i) {
    RagTime5LayoutInternal::ClusterLayout::Zone &zone=layout.m_zoneList[i];
    if (zone.m_mainId) {
      if (m_mainParser.getClusterType(zone.m_mainId)==RagTime5ClusterManager::Cluster::C_GraphicZone)
        ++numChild;
      else {
        MWAW_DEBUG_MSG(("RagTime5Layout::updateLayout: find unexpected type for cluster %d\n", zone.m_mainId));
        zone.m_mainId=0;
      }
    }
    if (zone.m_masterId) {
      if (m_mainParser.getClusterType(zone.m_masterId)==RagTime5ClusterManager::Cluster::C_Layout) {
        m_state->m_masterIdSet.insert(zone.m_masterId);
        ++numMasterChild;
      }
      else {
        MWAW_DEBUG_MSG(("RagTime5Layout::updateLayout: find unexpected type for cluster %d\n", zone.m_masterId));
        zone.m_masterId=0;
      }
    }
  }
  layout.m_numChild=numChild;
  layout.m_numMasterChild=numMasterChild;
}


////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// interface send function
////////////////////////////////////////////////////////////
void RagTime5Layout::flushExtra()
{
  MWAW_DEBUG_MSG(("RagTime5Layout::flushExtra: not implemented\n"));
}

bool RagTime5Layout::send(RagTime5LayoutInternal::ClusterLayout &/*cluster*/, int /*page*/)
{
  MWAWListenerPtr listener=m_parserState->getMainListener();
  if (!listener) {
    MWAW_DEBUG_MSG(("RagTime5Layout::send: can not find the listener\n"));
    return false;
  }
  static bool first=true;
  if (first) {
    first=false;
    MWAW_DEBUG_MSG(("RagTime5Layout::send: sorry not implemented\n"));
  }
  return true;
}

////////////////////////////////////////////////////////////
// cluster parser
////////////////////////////////////////////////////////////

namespace RagTime5LayoutInternal
{
//! Internal: the helper to read a clustList
struct ClustListParser : public RagTime5StructManager::DataParser {
  //! constructor
  ClustListParser(RagTime5ClusterManager &clusterManager, int fieldSize, std::string const &zoneName) :
    RagTime5StructManager::DataParser(zoneName), m_fieldSize(fieldSize), m_linkList(), m_clusterManager(clusterManager)
  {
    if (m_fieldSize<4) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::ClustListParser: bad field size\n"));
      m_fieldSize=0;
    }
  }

  //! returns the not null list dataId list
  std::vector<int> getIdList() const
  {
    std::vector<int> res;
    for (size_t i=0; i<m_linkList.size(); ++i) {
      if (m_linkList[i].m_dataId>0)
        res.push_back(m_linkList[i].m_dataId);
    }
    return res;
  }
  //! return the cluster name
  std::string getClusterName(int id) const
  {
    return m_clusterManager.getClusterName(id);
  }

  //! try to parse a data
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f)
  {
    long pos=input->tell();
    if (endPos-pos!=m_fieldSize) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::ClustListParser::parse: bad data size\n"));
      return false;
    }
    std::vector<int> listIds;
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::ClustListParser::parse: can not read an cluster id\n"));
      f << "##clusterIds,";
      return false;
    }
    RagTime5StructManager::ZoneLink link;
    link.m_dataId=listIds[0];
    if (listIds[0])
      f << getClusterName(listIds[0]) << ",";
    f << link;
    m_linkList.push_back(link);
    return true;
  }

  //! the field size
  int m_fieldSize;
  //! the list of read cluster
  std::vector<RagTime5StructManager::ZoneLink> m_linkList;
private:
  //! the main zone manager
  RagTime5ClusterManager &m_clusterManager;
  //! copy constructor, not implemented
  ClustListParser(ClustListParser &orig);
  //! copy operator, not implemented
  ClustListParser &operator=(ClustListParser &orig);
};

//
//! low level: parser of layout cluster
//
struct LayoutCParser : public RagTime5ClusterManager::ClusterParser {
  //! constructor
  LayoutCParser(RagTime5ClusterManager &parser, int zoneType) : ClusterParser(parser, zoneType, "ClustLayout"), m_cluster(new ClusterLayout),
    m_actualZoneId(0), m_numZones(0), m_what(-1), m_linkId(-1), m_fieldName(""), m_actualZone()
  {
  }
  //! return the current cluster
  shared_ptr<RagTime5ClusterManager::Cluster> getCluster()
  {
    return m_cluster;
  }
  //! return the current layout cluster
  shared_ptr<ClusterLayout> getLayoutCluster()
  {
    return m_cluster;
  }
  //! start a new zone
  void startZone()
  {
    if (m_what<=0)
      ++m_what;
    else if (m_what==1) {
      if (++m_actualZoneId>=m_numZones)
        ++m_what;
      m_actualZone=ClusterLayout::Zone();
    }
  }
  //! end of a start zone call
  void endZone()
  {
    if (m_what==1)
      m_cluster->m_zoneList.push_back(m_actualZone);
    if (m_link.empty())
      return;
    switch (m_linkId) {
    case 0:
      m_cluster->m_listItemLink=m_link;
      break;
    case 1:
      m_cluster->m_pipelineLink=m_link;
      break;
    case 2:
      m_cluster->m_settingLinks.push_back(m_link);
      break;
    case 3:
      if (m_cluster->m_nameLink.empty())
        m_cluster->m_nameLink=m_link;
      else {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::endZone: oops the name link is already set\n"));
        m_cluster->m_linksList.push_back(m_link);
      }
      break;
    default:
      m_cluster->m_linksList.push_back(m_link);
      break;
    }
  }
  //! parse a zone
  bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
  {
    m_fieldName="";
    m_linkId=-1;
    if (m_what==0)
      return parseHeaderZone(input,fSz,N,flag,f);
    if (m_what==1)
      return parseZoneBlock(input,fSz,N,flag,f);
    m_what=2;

    if (N<0) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: N value seems bad\n"));
      f << "###N=" << N << ",";
      return true;
    }
    int val;
    m_link.m_N=N;
    switch (fSz) {
    case 28:
    case 29:
    case 30:
    case 32: {
      std::string mess;
      long linkValues[4];
      if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
        f << "###fType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: the field fSz28... type seems bad\n"));
        return true;
      }
      f << m_link << "," << mess;
      long expectedFileType1=0;
      if (m_link.m_fileType[0]==0x35800) { // fSz=28
        m_what=4;
        m_fieldName="zone:longs";
      }
      else if (m_link.m_fileType[0]==0x3e800) { // fSz=28
        m_what=4;
        m_fieldName="list:longs0";
      }
      else if (m_link.m_fileType[0]==0x3c052) {
        m_what=4;
        m_fieldName="list:longs2";
        expectedFileType1=0x50;
      }
      else if (m_link.m_fileType[0]==0x14b9800) { // fSz=30
        m_linkId=0;
        m_what=3;
        m_fieldName="data0";
        expectedFileType1=0x10;
      }
      else if (m_link.m_fileType[0]==0x47040) {
        m_what=4;
        m_linkId=2;
        m_link.m_name=m_fieldName="settings";
      }
      else if (m_link.m_fileType[0]==0 && fSz==30) { // fSz=30
        m_linkId=1;
        m_fieldName="pipeline";
      }
      else if (m_link.m_fileType[0]==0 && fSz==32) {
        expectedFileType1=0x200;
        m_linkId=3;
        m_what=4;
        m_link.m_type=RagTime5ClusterManager::Link::L_UnicodeList;
        m_fieldName="unicodeList";
      }
      else {
        f << "###fType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: the field fSz%d... type seems bad\n", int(fSz)));
        return true;
      }
      if (expectedFileType1>=0 && (m_link.m_fileType[1]&0xFFD7)!=expectedFileType1) {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: fileType1 seems odd[fSz=28...]\n"));
        f << "###fileType1=" << std::hex << m_link.m_fileType[1] << std::dec << ",";
      }
      break;
    }
    case 36: // follow when it exists the fSz=30 zone, no auxilliar data
      m_fieldName="unicode[def]";
      for (int i=0; i<2; ++i) { // always 0
        val=(int) input->readLong(2);
        if (val) f << "f" << i << "=" << val << ",";
      }
      val=(int) input->readULong(4);
      if (val!=0x7d01a) {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected filetype[fSz=36]\n"));
        f << "###fileType=" << std::hex << val << std::dec << ",";
      }
      val=(int) input->readLong(4); // 0 or small number
      if (val) f << "id0=" << val << ",";
      for (int i=0; i<3; ++i) { // f4=10
        val=(int) input->readLong(2);
        if (val) f << "f" << i+2 << "=" << val << ",";
      }
      f << "ids=[";
      for (int i=0; i<3; ++i) { // an increasing sequence
        val=(int) input->readLong(4);
        if (val)
          f << val << ",";
        else
          f << "_,";
      }
      f << "],";
      break;
    case 38: // in 1 or 2 exemplar, no auxilliar data
      m_fieldName="settings[Def]";
      val=(int) input->readULong(4);
      if (val!=0x47040) {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected type[fSz=38]\n"));
        f << "##fileType=" << std::hex << val << std::dec << ",";
      }
      val=(int) input->readULong(4); // 0|1c07007|1492042
      if (val) f << "fileType1=" << std::hex << val << std::dec << ",";
      for (int i=0; i<5; ++i) { // f1=0|8, f4=10
        val=(int) input->readLong(2);
        if (val) f << "f" << i << "=" << val << ",";
      }
      f << "ids=[";
      for (int i=0; i<3; ++i) { // small increasing sequence
        val=(int) input->readLong(4);
        if (val) f << val << ",";
        else f << "_,";
      }
      f << "],";
      val=(int) input->readLong(2); // always 0
      if (val) f << "f5=" << val << ",";
      break;
    case 54: { // can be in multiple exemplar ~ 1 by zone, no auxilliar data
      m_fieldName="data1";
      for (int i=0; i<2; ++i) { // always 0
        val=(int) input->readLong(2);
        if (val) f << "f" << i << "=" << val << ",";
      }
      float dim[2];
      for (int i=0; i<2; ++i) dim[i]=float(input->readLong(4))/65536.f;
      f << "sz=" << MWAWVec2f(dim[0],dim[1]) << ",";

      std::vector<int> listIds;
      long actPos=input->tell();
      if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
        f << "###cluster1,";
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: can not read cluster block[fSz=54]\n"));
        input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
      }
      else if (listIds[0]) { // link to the new cluster e zone ? (can be also 0)
        m_cluster->m_clusterIdsList.push_back(listIds[0]);
        f << "cluster0=" << getClusterName(listIds[0]) << ",";
      }
      for (int i=0; i<7; ++i) { // always 0
        val=(int) input->readLong(2);
        if (val) f << "f" << i+2 << "=" << val << ",";
      }
      for (int i=0; i<9; ++i) { // g0=1, g1=1-7, g2=0-d, g3=0-1, g8=2-8
        val=(int) input->readLong(2);
        if (val) f << "g" << i << "=" << val << ",";
      }
      break;
    }
    case 60: // unsure
      m_fieldName="data2";
      m_what=5;
      for (int i=0; i<4; ++i) { // f3=1
        val=(int) input->readLong(2);
        if (val) f << "f" << i << "=" << val << ",";
      }
      val=(int) input->readLong(4); // 0 or 72cc
      if (val) f << "f4=" << std::hex << val << std::dec << ",";
      val=(int) input->readULong(4);
      if (val!=0x14b6842) {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected filetype[fSz=60]\n"));
        f << "#fileType1=" << std::hex << val << std::dec << ",";
      }
      for (int i=0; i<7; ++i) { // g4=g6=1
        val=(int) input->readLong(2);
        if (val) f << "g" << i << "=" << val << ",";
      }
      val=(int) input->readULong(4);
      if (val!=0x35800) { // maybe a link here
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected filetype2[fSz=60]\n"));
        f << "#fileType2=" << std::hex << val << std::dec << ",";
      }
      for (int i=0; i<4; ++i) { // 0
        val=(int) input->readLong(2);
        if (val) f << "h" << i << "=" << val << ",";
      }
      val=(int) input->readULong(4);
      if (val!=0x14b4817) {
        MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected filetype3[fSz=60]\n"));
        f << "#fileType3=" << std::hex << val << std::dec << ",";
      }
      for (int i=0; i<4; ++i) { // h4=47
        val=(int) input->readLong(2);
        if (val) f << "h" << i+4 << "=" << val << ",";
      }
      break;
    default:
      f << "###fSz=" << fSz << ",";
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZone: find unexpected file size\n"));
      break;
    }
    if (!m_fieldName.empty())
      f << m_fieldName << ",";
    return true;
  }
  //! parse a field
  bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f)
  {
    if (!m_fieldName.empty())
      f << m_fieldName << ",";
    switch (m_what) {
    case 0: // main
      if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x14b5815) {
        for (size_t i=0; i<field.m_fieldList.size(); ++i) {
          RagTime5StructManager::Field const &child=field.m_fieldList[i];
          if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xcf042) {
            // TODO: storeme
            f << "ids=[";
            for (size_t j=0; j<child.m_longList.size(); ++j) {
              if (child.m_longList[j]==0)
                f << "_,";
              else
                f << child.m_longList[j] << ",";
            }
            continue;
          }
          MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected main field\n"));
          f << "###[" << child << "],";
        }
        break;
      }
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected main field\n"));
      f << "###" << field;
      break;
    case 3: // data0
      if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
        // rare, always 2
        f << "unkn="<< field.m_extra << ",";
        break;
      }
      if (field.m_type==RagTime5StructManager::Field::T_Long && field.m_fileType==0xcf817) {
        // a small value between 2a and 61
        f << "f0="<<field.m_longValue[0] << ",";
        break;
      }
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected data0 field\n"));
      f << "###" << field;
      break;
    case 4: // list
      if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
        f << "pos=[";
        for (size_t i=0; i<field.m_longList.size(); ++i) {
          if (field.m_longList[i]==0)
            f << "_,";
          else if (field.m_longList[i]>1000)
            f << std::hex << field.m_longList[i] << std::dec << ",";
          else
            f << field.m_longList[i] << ",";
        }
        f << "],";
        m_link.m_longList=field.m_longList;
        break;
      }
      if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xcf042) { // with 3c052
        f << "unkn=[";
        for (size_t j=0; j<field.m_longList.size(); ++j) {
          if (field.m_longList[j]==0)
            f << "_,";
          else
            f << field.m_longList[j] << ",";
        }
        break;
      }
      if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
        // a small value 2|4|a|1c|40
        f << "unkn="<<field.m_extra << ",";
        break;
      }
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected list field\n"));
      f << "###" << field;
      break;
    case 5: // fSz=60
      if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x14b4815) {
        for (size_t i=0; i<field.m_fieldList.size(); ++i) {
          RagTime5StructManager::Field const &child=field.m_fieldList[i];
          if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xce842) {
            f << "unkn=[";
            for (size_t j=0; j<child.m_longList.size(); ++j) {
              if (child.m_longList[j]==0)
                f << "_,";
              else
                f << child.m_longList[j] << ",";
            }
            continue;
          }
          MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected data2 field\n"));
          f << "###[" << child << "],";
        }
        break;
      }
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected data2 field\n"));
      f << "###" << field;
      break;

    default:
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseField: find unexpected sub field\n"));
      f << "###" << field;
      break;
    }
    return true;
  }
protected:
  //! parse a zone block
  bool parseZoneBlock(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
  {
    if (N<0 || m_what!=1 || (fSz!=50 && fSz!=66)) {
      f << "###N=" << N << ",fSz=" << fSz << ",";
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZoneBlock: find unexpected main field\n"));
      return false;
    }
    f << "block, fl=" << std::hex << flag << std::dec << ",";
    m_fieldName="block";
    if (N!=1) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZoneBlock: zone N seems badA\n"));
      f << "#N=" << N << ",";
    }
    int val;
    for (int i=0; i<2; ++i) { // always 0
      val=(int) input->readLong(2);
      if (val)
        f << "f" << i << "=" << val << ",";
    }
    float dim[2];
    for (int i=0; i<2; ++i) dim[i]=float(input->readLong(4))/65536.f;
    m_actualZone.m_dimension=MWAWVec2f(dim[0],dim[1]);
    f << "sz=" << MWAWVec2f(dim[0],dim[1]) << ",";
    std::vector<int> listIds;
    long actPos=input->tell();
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
      f << "###cluster0,";
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZoneBlock: can not read first cluster block\n"));
      input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
    }
    else if (listIds[0]) { // link to a cluster main zone
      m_actualZone.m_mainId=listIds[0];
      m_cluster->m_childIdSet.insert(listIds[0]);
      f << "cluster0=" << getClusterName(listIds[0]) << ",";
    }
    for (int i=0; i<2; ++i) { // always 0: another item?
      val=(int) input->readLong(2);
      if (val)
        f << "f" << i+2 << "=" << val << ",";
    }
    listIds.clear();
    actPos=input->tell();
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
      f << "###cluster1,";
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseZoneBlock: can not read second cluster block\n"));
      input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
    }
    else if (listIds[0]) { // link to the master layout
      m_actualZone.m_masterId=listIds[0];
      m_cluster->m_childIdSet.insert(listIds[0]);
      f << "cluster1=" << getClusterName(listIds[0]) << ",";
    }
    for (int i=0; i<2; ++i) { // either 0,0 or 1,small number (but does not seems a data id )
      val=(int) input->readLong(2);
      if (val)
        f << "f" << i+4 << "=" << val << ",";
    }
    val=(int) input->readLong(4); // alwas 1?
    if (val!=1) f << "f6=" << val << ",";
    f << "unkn=[";
    for (int i=0; i<4; ++i) { // small number
      val=(int) input->readLong(2);
      if (val)
        f << val << ",";
      else
        f << "_,";
    }
    f << "],";
    for (int i=0; i<2; ++i) { // g1=0|4|5
      val=(int) input->readLong(2);
      if (val)
        f << "g" << i << "=" << val << ",";
    }
    if (fSz==66) {
      for (int i=0; i<2; ++i) { // g2=0|7|8, g3=5-12
        val=(int) input->readLong(4);
        if (val)
          f << "g" << i+2 << "=" << val << ",";
      }
      for (int i=0; i<2; ++i) { // fl0=0|1
        val=(int) input->readLong(1);
        if (val)
          f << "fl" << i << "=" << val << ",";
      }
      for (int i=0; i<3; ++i) { // g4=0|1, g6=g4
        val=(int) input->readLong(2);
        if (val)
          f << "g" << i+4 << "=" << val << ",";
      }
    }
    return true;
  }

  //! parse the header zone
  bool parseHeaderZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
  {
    f << "header, fl=" << std::hex << flag << std::dec << ",";
    m_fieldName="header";
    if (N!=-5 || m_what!=0 || (fSz!=123 && fSz!=127 && fSz!=128 && fSz!=132)) {
      f << "###N=" << N << ",fSz=" << fSz << ",";
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseHeaderZone: find unexpected main field\n"));
      return false;
    }
    int val;
    for (int i=0; i<2; ++i) { // always 0?
      val=(int) input->readLong(2);
      if (val) f << "f" << i+1 << "=" << val << ",";
    }
    val=(int) input->readLong(2);
    f << "id=" << val << ",";
    val=(int) input->readULong(2);
    if (val!=m_type) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseHeaderZone: unexpected zone type\n"));
      f << "##zoneType=" << std::hex << val << std::dec << ",";
    }
    for (int i=0; i<2; ++i) { // f0=0, f1=4-6
      val=(int) input->readLong(4);
      if (val)
        f << "f" << i << "=" << val << ",";
    }
    val=(int) input->readLong(2); // always 16
    if (val!=16)
      f << "f2=" << val << ",";
    m_numZones=(int) input->readLong(4);
    if (m_numZones!=1)
      f << "num[zone1]=" << m_numZones << ",";
    long fileType=(long) input->readULong(4);
    if (fileType!=0x14b6052) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseHeaderZone: find unexpected filetype\n"));
      f << "#fileType0=" << std::hex << fileType << std::dec << ",";
    }
    for (int i=0; i<9; ++i) { // f11=0x60,
      val=(int) input->readLong(2);
      if (val)
        f << "f" << i+5 << "=" << val << ",";
    }
    val=(int) input->readLong(1);
    if (val!=1) f << "fl=" << val << ",";
    if (fSz==128 || fSz==132) {
      for (int i=0; i<5; ++i) { // unsure find only 0 here
        val=(int) input->readLong(1);
        if (val)
          f << "flA" << i << "=" << val << ",";
      }
    }
    val=(int) input->readLong(4);
    if (val) // 0-20
      f << "g0=" << val << ",";
    long actPos=input->tell();
    std::vector<int> listIds;
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
      MWAW_DEBUG_MSG(("RagTime5LayoutInternal::LayoutCParser::parseHeaderZone: can not read first cluster block\n"));
      f << "##badCluster,";
      input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
    }
    else if (listIds[0]) { // find link to a named frame cluster
      m_cluster->m_clusterIdsList.push_back(listIds[0]);
      f << "clusterId1=" << getClusterName(listIds[0]) << ",";
    }
    for (int i=0; i<2; ++i) { // always 0
      val=(int) input->readLong(2);
      if (val)
        f << "g" << i+1 << "=" << val << ",";
    }
    float dim[4];
    for (int i=0; i<2; ++i) dim[i]=float(input->readLong(4))/65536.f;
    MWAWVec2f frameSize(dim[0],dim[1]);
    f << "sz=" << frameSize << ",";
    for (int i=0; i<2; ++i) dim[i]=float(input->readLong(4))/65536.f;
    if (MWAWVec2f(dim[0],dim[1])!=frameSize)
      f << "sz2=" << MWAWVec2f(dim[0],dim[1]) << ",";
    for (int i=0; i<10; ++i) { // find g3=0|1|69, g8=0|5|8, g9=g11=g12=1, g10=small number(but not a data id)
      val=(int) input->readLong(2);
      if (val)
        f << "g" << i+3 << "=" << val << ",";
    }
    for (int i=0; i<4; ++i) dim[i]=float(input->readLong(4))/65536.f;
    f << "dim=" << MWAWBox2f(MWAWVec2f(dim[0],dim[1]), MWAWVec2f(dim[2],dim[3])) << ",";
    for (int i=0; i<4; ++i) { // h3=0|1|3|6
      val=(int) input->readLong(2);
      if (val)
        f << "h" << i << "=" << val << ",";
    }
    if (fSz==127 || fSz==132) {
      for (int i=0; i<2; ++i) { // always 0
        val=(int) input->readLong(2);
        if (val)
          f << "h" << i+3 << "=" << val << ",";
      }
    }
    return true;
  }

  //! the current cluster
  shared_ptr<ClusterLayout> m_cluster;
  //! the actual zone id
  int m_actualZoneId;
  //! the number of zones
  int m_numZones;
  //! a index to know which field is parsed :  0: main, 1:list of zones, 2: unknown, 3:data0, 4:list, 5: unknown
  int m_what;
  //! the link id : 0: listItem, 1: pipeline, 2: settinglinks, 3: namelink,
  int m_linkId;
  //! the actual field name
  std::string m_fieldName;

  //! the actual zone
  ClusterLayout::Zone m_actualZone;
};

}

shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Layout::readLayoutCluster(RagTime5Zone &zone, int zoneType)
{
  shared_ptr<RagTime5ClusterManager> clusterManager=m_mainParser.getClusterManager();
  if (!clusterManager) {
    MWAW_DEBUG_MSG(("RagTime5Layout::readLayoutCluster: oops can not find the cluster manager\n"));
    return shared_ptr<RagTime5ClusterManager::Cluster>();
  }
  RagTime5LayoutInternal::LayoutCParser parser(*clusterManager, zoneType);
  if (!clusterManager->readCluster(zone, parser) || !parser.getLayoutCluster()) {
    MWAW_DEBUG_MSG(("RagTime5Layout::readLayoutCluster: oops can not find the cluster\n"));
    return shared_ptr<RagTime5ClusterManager::Cluster>();
  }

  shared_ptr<RagTime5LayoutInternal::ClusterLayout> cluster=parser.getLayoutCluster();
  if (!cluster->m_listItemLink.empty())
    m_mainParser.readFixedSizeZone(cluster->m_listItemLink, "LayoutUnknown0");
  if (!cluster->m_pipelineLink.empty() && cluster->m_pipelineLink.m_ids.size()==1) {
    if (cluster->m_pipelineLink.m_fieldSize==4) {
      RagTime5LayoutInternal::ClustListParser listParser(*clusterManager, 4, "LayoutPipeline");
      m_mainParser.readFixedSizeZone(cluster->m_pipelineLink, listParser);
      m_mainParser.checkClusterList(listParser.getIdList());
    }
    else {
      MWAW_DEBUG_MSG(("RagTime5Layout::readClusterLayoutData: find unexpected field size for pipeline data\n"));
      m_mainParser.readFixedSizeZone(cluster->m_pipelineLink, "LayoutPipelineBAD");
    }
  }
  // can have some setting
  for (int wh=0; wh<2; ++wh) {
    std::vector<RagTime5ClusterManager::Link> const &list=wh==0 ? cluster->m_conditionFormulaLinks : cluster->m_settingLinks;
    for (size_t i=0; i<list.size(); ++i) {
      if (list[i].empty()) continue;
      RagTime5ClusterManager::Cluster unknCluster(RagTime5ClusterManager::Cluster::C_Unknown);
      unknCluster.m_dataLink=list[i];
      RagTime5StructManager::FieldParser defaultParser(wh==0 ? "CondFormula" : "Settings");
      m_mainParser.readStructZone(unknCluster, defaultParser, 0);
    }
  }

  if (!cluster->m_nameLink.empty()) {
    std::map<int, librevenge::RVNGString> idToStringMap;
    m_mainParser.readUnicodeStringList(cluster->m_nameLink, idToStringMap);
  }

  for (size_t i=0; i<cluster->m_linksList.size(); ++i) {
    RagTime5ClusterManager::Link const &lnk=cluster->m_linksList[i];
    if (lnk.m_type==RagTime5ClusterManager::Link::L_List) {
      m_mainParser.readListZone(lnk);
      continue;
    }
    std::stringstream s;
    s << "Layout_Data" << lnk.m_fieldSize;
    RagTime5StructManager::DataParser defaultParser(s.str());
    m_mainParser.readFixedSizeZone(lnk, defaultParser);
  }

  if (m_state->m_idLayoutMap.find(zone.m_ids[0])!=m_state->m_idLayoutMap.end()) {
    MWAW_DEBUG_MSG(("RagTime5Layout::readLayoutCluster: cluster %d already exists\n", zone.m_ids[0]));
  }
  else
    m_state->m_idLayoutMap[zone.m_ids[0]]=cluster;
  return cluster;
}


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