/* -*- 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.
*/

/*
 * Class to read/store the MsWrd structures
 */

#ifndef MS_WRD_STRUCT
#  define MS_WRD_STRUCT

#include <iostream>
#include <string>
#include <vector>

#include "libmwaw_internal.hxx"

#include "MWAWFont.hxx"
#include "MWAWParagraph.hxx"

/** namespace to store the main structure which appears in a Microsoft Word 3.0-5.0 file */
namespace MsWrdStruct
{
//! generic function use to fill a border using the read data
MWAWBorder getBorder(int val, std::string &extra);

//! the font structure of a Microsoft Word file
struct Font {
  enum { NumFlags /** the number of flags needed to store all datas*/=9 };

  //! the constructor
  Font()
    : m_font(MWAWFont(-1,0))
    , m_size(0)
    , m_value(0)
    , m_picturePos(0)
    , m_unknown(0)
    , m_extra("")
  {
    for (auto &fl : m_flags) fl=MWAWVariable<int>(0);
  }
  Font(Font const &)=default;
  Font &operator=(Font const &)=default;
  //! insert new font data ( beginning by updating font flags )
  void insert(Font const &font, Font const *styleFont=nullptr);

  //! update the font to obtain the final font
  void updateFontToFinalState(Font const *styleFont=nullptr);

  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Font const &font);

  //! a comparison function
  int cmp(Font const &oth) const
  {
    int diff = m_font.get().cmp(oth.m_font.get());
    if (diff) return diff;
    if (m_size.get() < oth.m_size.get()) return -1;
    if (m_size.get() > oth.m_size.get()) return 1;
    diff = m_value.get()-oth.m_value.get();
    if (diff) return diff;
    for (int i = 0; i < NumFlags; i++) {
      diff = m_flags[i].get()-oth.m_flags[i].get();
      if (diff) return diff;
    }
    if (m_picturePos.get()<oth.m_picturePos.get()) return -1;
    if (m_picturePos.get()>oth.m_picturePos.get()) return 1;
    diff = m_unknown.get()-oth.m_unknown.get();
    if (diff) return diff;
    return 0;
  }
  //! the font
  MWAWVariable<MWAWFont> m_font;
  //! a second size
  MWAWVariable<float> m_size;
  //! a unknown value
  MWAWVariable<int> m_value;
  //! a list of flags
  MWAWVariable<int> m_flags[NumFlags];
  //! a picture file position (if this corresponds to a picture)
  MWAWVariable<long> m_picturePos;
  //! some unknown flag
  MWAWVariable<int> m_unknown;
  //! extra data
  std::string m_extra;
};

//! the section structure of a Microsoft Word file
struct Section {
  //! constructor
  Section()
    : m_id(-1)
    , m_type(0)
    , m_paragraphId(-9999)
    , m_col(1)
    , m_colSep(0.5)
    , m_colBreak(false)
    , m_flag(0)
    , m_extra("")
  {
  }
  //! returns a section
  MWAWSection getSection(double pageWidth) const;

  //! insert the new values
  void insert(Section const &sec)
  {
    m_id.insert(sec.m_id);
    m_type.insert(sec.m_type);
    m_paragraphId.insert(sec.m_paragraphId);
    m_col.insert(sec.m_col);
    m_colSep.insert(sec.m_colSep);
    m_colBreak.insert(sec.m_colBreak);
    m_flag.insert(sec.m_flag);
    m_extra+=sec.m_extra;
  }
  //! try to read a data
  bool read(MWAWInputStreamPtr &input, long endPos);
  //! try to read a data ( v3 code )
  bool readV3(MWAWInputStreamPtr &input, long endPos);

  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Section const &section);

  //! the identificator
  MWAWVariable<int> m_id;
  //! the type
  MWAWVariable<int> m_type;
  //! the paragraph id
  MWAWVariable<int> m_paragraphId;
  //! the num of columns
  MWAWVariable<int> m_col;
  //! the spacing between column
  MWAWVariable<float> m_colSep;
  //! only a column break
  MWAWVariable<bool> m_colBreak;
  //! some flag ( in the main position)
  MWAWVariable<int> m_flag;
  /** the errors */
  std::string m_extra;
};

//! the table in a Microsoft Word file
struct Table {
  struct Cell;
  //! constructor
  Table()
    : m_height(0)
    , m_justify(MWAWParagraph::JustificationLeft)
    , m_indent(0)
    , m_columns()
    , m_columnsWidthMod()
    , m_cells()
    , m_badCell()
    , m_extra("")
  {
  }
  //! insert the new values
  void insert(Table const &table);
  //! try to read a data
  bool read(MWAWInputStreamPtr &input, long endPos);
  //! returns the ith Cell
  MWAWVariable<Cell> &getCell(int id);

  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Table const &table);

  //! the cells definitions in a Microsoft Word Table
  struct Cell {
    //! constructor
    Cell()
      : m_borders()
      , m_backColor(1.0f)
      , m_extra("")
    {
    }
    //! update the cell data by merging
    void insert(Cell const &cell)
    {
      size_t cNumBorders = cell.m_borders.size();
      if (cNumBorders > m_borders.size())
        m_borders.resize(cNumBorders);
      for (size_t i=0; i < cNumBorders; i++)
        if (cell.m_borders[i].isSet()) m_borders[i]=*cell.m_borders[i];
      m_backColor.insert(cell.m_backColor);
      m_extra+=cell.m_extra;
    }
    //! returns true if the cell has borders
    bool hasBorders() const
    {
      for (auto const &bd : m_borders)
        if (bd.isSet() && bd->m_style != MWAWBorder::None)
          return true;
      return false;
    }
    //! operator<<
    friend std::ostream &operator<<(std::ostream &o, Cell const &cell);
    /** the borders TLBR */
    std::vector<MWAWVariable<MWAWBorder> > m_borders;
    /** the background gray color */
    MWAWVariable<float> m_backColor;
    /** extra data */
    std::string m_extra;
  };

  //! the row height in inches
  MWAWVariable<float> m_height;
  //! the justification
  MWAWVariable<MWAWParagraph::Justification> m_justify;
  //! the indent
  MWAWVariable<float> m_indent;
  //! the table columns
  MWAWVariable<std::vector<float> > m_columns;
  //! the columns width modifier
  MWAWVariable<std::vector<float> > m_columnsWidthMod;
  //! the table cells
  std::vector<MWAWVariable<Cell> > m_cells;
  //! empty cell used by getCell to return unknown cell
  MWAWVariable<Cell> m_badCell;
  /** the errors */
  std::string m_extra;
};

//! the paragraph information of a Microsoft Word file (PHE)
struct ParagraphInfo {
  //! constructor
  ParagraphInfo()
    : m_type(0)
    , m_dim()
    , m_numLines(-1)
    , m_error("")
  {
  }
  //! returns true if num lines is set
  bool isLineSet() const
  {
    return *m_numLines!=0;
  }
  //! returns true if no data are been set
  bool isEmpty() const
  {
    if (*m_numLines || *m_type) return false;
    if (!m_dim.isSet()) return true;
    if ((*m_dim)[0] > 0 || (*m_dim)[1] > 0) return false;
    return true;
  }
  //! try to read a data
  bool read(MWAWInputStreamPtr &input, long endPos, int vers);
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, ParagraphInfo const &pInfo)
  {
    // find also pInfo.m_type&0x40 : ?
    if (*pInfo.m_type&0xd0) o << "type?=" << ((*pInfo.m_type&0xd0)>>4) << ",";
    if (*pInfo.m_type&0x0f) o << "#unkn=" << (*pInfo.m_type&0xf) << ",";
    if (pInfo.m_dim.isSet()) {
      if ((*pInfo.m_dim)[0] > 0)
        o << "width=" << (*pInfo.m_dim)[0] << ",";
      if ((*pInfo.m_dim)[1] > 0) {
        o << "height=" << (*pInfo.m_dim)[1];
        if (*pInfo.m_type&0x20)
          o << "[total]";
        o << ",";
      }
    }
    if (pInfo.m_numLines.isSet() && *pInfo.m_numLines!=-1 && *pInfo.m_numLines!=1)
      o << "nLines=" << *pInfo.m_numLines << ",";
    if (pInfo.m_error.length()) o << pInfo.m_error << ",";
    return o;
  }
  //! insert the new values
  void insert(ParagraphInfo const &pInfo);
  //! the type
  MWAWVariable<int> m_type;
  //! the zone dimension
  MWAWVariable<MWAWVec2f> m_dim;
  //! the number of lines
  MWAWVariable<int> m_numLines;
  /** the errors */
  std::string m_error;
};

//! the paragraph structure of a Microsoft Word file
struct Paragraph final : public MWAWParagraph {
  //! Constructor
  explicit Paragraph(int version)
    : MWAWParagraph()
    , m_version(version)
    , m_styleId(-1000)
    , m_interline(0)
    , m_deletedTabs()
    , m_info()
    , m_font()
    , m_modFont()
    , m_section()
    , m_bordersStyle()
    , m_inCell(false)
    , m_tableDef(false)
    , m_table()
  {
    m_tabsRelativeToLeftMargin=false;
  }
  Paragraph(Paragraph const &)=default;
  Paragraph &operator=(Paragraph const &)=default;
  Paragraph &operator=(Paragraph &&)=default;
  //! destructor
  ~Paragraph() final;
  //! insert the new values
  void insert(Paragraph const &para, bool insertModif=true);
  //! try to read a data
  bool read(MWAWInputStreamPtr &input, long endPos);
  //! update the paragraph to obtain the final paragraph
  void updateParagraphToFinalState(Paragraph const *style=nullptr);
  //! returns the font which correspond to the paragraph if possible
  bool getFont(Font &font, Font const *styleFont=nullptr) const;
  //! returns true if we are in table
  bool inTable() const
  {
    return m_inCell.get();
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind);

  //! operator<<
  void print(std::ostream &o, MWAWFontConverterPtr converter) const;

  //! returns the number of line stored in m_info or -1
  int getNumLines() const
  {
    return m_info.get().m_numLines.get();
  }
  //! the file version
  int m_version;
  //! the style id (if known)
  MWAWVariable<int> m_styleId;
  //! the interline if set
  MWAWVariable<double> m_interline;
  //! the delete tabulation
  std::vector<float> m_deletedTabs;
  //! the dimension
  MWAWVariable<ParagraphInfo> m_info;
  //! the font
  MWAWVariable<Font> m_font, m_modFont /** font (modifier) */;
  //! the section
  MWAWVariable<Section> m_section;
  //! the border style ( old v3)
  MWAWVariable<MWAWBorder> m_bordersStyle;
  //! a cell/textbox
  MWAWVariable<bool> m_inCell;
  //! a table flag
  MWAWVariable<bool> m_tableDef;
  //! the table
  MWAWVariable<Table> m_table;
};
}
#endif
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
