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

#ifndef MWAW_FONT
#  define MWAW_FONT

#include <string>
#include <vector>

#include "libmwaw_internal.hxx"

//! Class to store font
class MWAWFont
{
public:
  /** a small struct to define a line in MWAWFont */
  struct Line {
    /** the line style */
    enum Style { None, Simple, Dot, LargeDot, Dash, Wave };
    /** the line style */
    enum Type { Single, Double, Triple };
    //! constructor
    explicit Line(Style style=None, Type type=Single, bool wordFlag=false, float w=1.0)
      : m_style(style)
      , m_type(type)
      , m_width(w)
      , m_color(MWAWColor::black())
      , m_word(wordFlag)
    {
    }
    //! return true if the line is not empty
    bool isSet() const
    {
      return m_style != None && m_width>0;
    }
    //! add a line to the propList knowing the type (line-through, underline, overline )
    void addTo(librevenge::RVNGPropertyList &propList, std::string const &type) const;
    //! operator<<
    friend std::ostream &operator<<(std::ostream &o, Line const &line);
    //! operator==
    bool operator==(Line const &oth) const
    {
      return cmp(oth)==0;
    }
    //! operator!=
    bool operator!=(Line const &oth) const
    {
      return cmp(oth)!=0;
    }
    //! small comparison function
    int cmp(Line const &oth) const
    {
      if (m_style != oth.m_style) return int(m_style)-int(oth.m_style);
      if (m_type != oth.m_type) return int(m_type)-int(oth.m_type);
      if (m_word != oth.m_word) return m_word ? -1 : 1;
      if (m_width < oth.m_width) return -1;
      if (m_width > oth.m_width) return 1;
      if (m_color.isSet() != oth.m_color.isSet())
        return m_color.isSet();
      if (m_color.get() < oth.m_color.get()) return -1;
      if (m_color.get() > oth.m_color.get()) return 1;
      return 0;
    }
    /** the style */
    Style m_style;
    /** the type */
    Type m_type;
    /** the width in point */
    float m_width;
    /** the color ( if not set, we use the font color )*/
    MWAWVariable<MWAWColor> m_color;
    /** word or not word line */
    bool m_word;
  };
  /** a small struct to define the script position in MWAWFont */
  struct Script {
    //! constructor
    explicit Script(float delta=0, librevenge::RVNGUnit deltaUnit=librevenge::RVNG_PERCENT, int scale=100)
      : m_delta(delta)
      , m_deltaUnit(deltaUnit)
      , m_scale(scale)
    {
    }
    //! return true if the position is not default
    bool isSet() const
    {
      return *this != Script();
    }
    //! return a yposition which correspond to a basic subscript
    static Script sub()
    {
      return Script(-33,librevenge::RVNG_PERCENT,58);
    }
    //! return a yposition which correspond to a basic subscript100
    static Script sub100()
    {
      return Script(-20);
    }
    //! return a yposition which correspond to a basic superscript
    static Script super()
    {
      return Script(33,librevenge::RVNG_PERCENT,58);
    }
    //! return a yposition which correspond to a basic superscript100
    static Script super100()
    {
      return Script(20);
    }
    //! return a string which correspond to style:text-position
    std::string str(float fSize) const;

    //! operator==
    bool operator==(Script const &oth) const
    {
      return cmp(oth)==0;
    }
    //! operator!=
    bool operator!=(Script const &oth) const
    {
      return cmp(oth)!=0;
    }
    //! operator<
    bool operator<(Script const &oth) const
    {
      return cmp(oth)<0;
    }
    //! operator<=
    bool operator<=(Script const &oth) const
    {
      return cmp(oth)<=0;
    }
    //! operator>
    bool operator>(Script const &oth) const
    {
      return cmp(oth)>0;
    }
    //! operator>=
    bool operator>=(Script const &oth) const
    {
      return cmp(oth)>=0;
    }
    //! small comparison function
    int cmp(Script const &oth) const
    {
      if (m_delta > oth.m_delta) return -1;
      if (m_delta < oth.m_delta) return 1;
      if (m_deltaUnit != oth.m_deltaUnit) return int(m_deltaUnit)-int(oth.m_deltaUnit);
      if (m_scale != oth.m_scale) return m_scale-oth.m_scale;
      return 0;
    }
    //! the ydelta
    float m_delta;
    //! the ydelta unit ( point or percent )
    librevenge::RVNGUnit m_deltaUnit;
    //! the font scaling ( in percent )
    int m_scale;
  };

  //! the different font bit
  enum FontBits { boldBit=1, italicBit=2, blinkBit=4, embossBit=8, engraveBit=0x10,
                  hiddenBit=0x20, outlineBit=0x40, shadowBit=0x80,
                  reverseVideoBit=0x100, smallCapsBit=0x200, uppercaseBit=0x400,
                  lowercaseBit=0x800,
                  initialcaseBit=2*lowercaseBit,
                  boxedBit=2*initialcaseBit,
                  boxedRoundedBit=2*boxedBit,
                  reverseWritingBit=2*boxedRoundedBit
                };
  /** constructor
   *
   * \param newId system id font
   * \param sz the font size
   * \param f the font attributes bold, ... */
  explicit MWAWFont(int newId=-1, float sz=12, uint32_t f = 0)
    : m_id(newId)
    , m_size(sz)
    , m_sizeIsRelative(false)
    , m_deltaSpacing(0)
    , m_deltaSpacingUnit(librevenge::RVNG_POINT)
    , m_widthStreching(1)
    , m_scriptPosition()
    , m_flags(f)
    , m_overline(Line(Line::None))
    , m_strikeoutline(Line(Line::None))
    , m_underline(Line(Line::None))
    , m_color(MWAWColor::black())
    , m_backgroundColor(MWAWColor::white())
    , m_language("")
    , m_extra("")
  {
    resetColor();
  }
  //! returns true if the font id is initialized
  bool isSet() const
  {
    return m_id.isSet();
  }
  //! inserts the set value in the current font
  void insert(MWAWFont const &ft)
  {
    m_id.insert(ft.m_id);
    m_size.insert(ft.m_size);
    m_sizeIsRelative.insert(ft.m_sizeIsRelative);
    m_deltaSpacing.insert(ft.m_deltaSpacing);
    m_deltaSpacingUnit.insert(ft.m_deltaSpacingUnit);
    m_widthStreching.insert(ft.m_widthStreching);
    m_scriptPosition.insert(ft.m_scriptPosition);
    if (ft.m_flags.isSet()) {
      if (m_flags.isSet())
        setFlags(flags()| ft.flags());
      else
        m_flags = ft.m_flags;
    }
    m_overline.insert(ft.m_overline);
    m_strikeoutline.insert(ft.m_strikeoutline);
    m_underline.insert(ft.m_underline);
    m_color.insert(ft.m_color);
    m_backgroundColor.insert(ft.m_backgroundColor);
    m_extra += ft.m_extra;
  }
  //! sets the font id and resets size to the previous size for this font
  void setFont(int newId)
  {
    resetColor();
    m_id=newId;
  }

  //! returns the font id
  int id() const
  {
    return m_id.get();
  }
  //! sets the font id
  void setId(int newId)
  {
    m_id = newId;
  }

  //! returns the font size
  float size() const
  {
    return m_size.get();
  }
  //! sets the font size
  void setSize(float sz, bool isRelative=false)
  {
    m_size = sz;
    m_sizeIsRelative = isRelative;
  }

  //! returns the condensed(negative)/extended(positive) width
  float deltaLetterSpacing() const
  {
    return m_deltaSpacing.get();
  }
  //! returns the condensed(negative)/extended(positive) unit
  librevenge::RVNGUnit deltaLetterSpacingUnit() const
  {
    return m_deltaSpacingUnit.get();
  }
  //! sets the letter spacing ( delta value in point )
  void setDeltaLetterSpacing(float d, librevenge::RVNGUnit unit=librevenge::RVNG_POINT)
  {
    m_deltaSpacing=d;
    m_deltaSpacingUnit=unit;
  }
  //! returns the text width streching
  float widthStreching() const
  {
    return m_widthStreching.get();
  }
  //! sets the text width streching
  void setWidthStreching(float scale=1.0)
  {
    m_widthStreching = scale;
  }
  //! returns the script position
  Script const &script() const
  {
    return m_scriptPosition.get();
  }

  //! sets the script position
  void set(Script const &newscript)
  {
    m_scriptPosition = newscript;
  }

  //! returns the font flags
  uint32_t flags() const
  {
    return m_flags.get();
  }
  //! sets the font attributes bold, ...
  void setFlags(uint32_t fl)
  {
    m_flags = fl;
  }

  //! returns true if the font color is not black
  bool hasColor() const
  {
    return m_color.isSet() && !m_color.get().isBlack();
  }
  //! returns the font color
  void getColor(MWAWColor &c) const
  {
    c = m_color.get();
  }
  //! sets the font color
  void setColor(MWAWColor color)
  {
    m_color = color;
  }

  //! returns the font background color
  void getBackgroundColor(MWAWColor &c) const
  {
    c = m_backgroundColor.get();
  }
  //! sets the font background color
  void setBackgroundColor(MWAWColor color)
  {
    m_backgroundColor = color;
  }
  //! resets the font color to black and the background color to white
  void resetColor()
  {
    m_color = MWAWColor::black();
    m_backgroundColor = MWAWColor::white();
  }

  //! return true if the font has decorations line (overline, strikeout, underline)
  bool hasDecorationLines() const
  {
    return (m_overline.isSet() && m_overline->isSet()) ||
           (m_strikeoutline.isSet() && m_strikeoutline->isSet()) ||
           (m_underline.isSet() && m_underline->isSet());
  }
  //! reset the decoration
  void resetDecorationLines()
  {
    if (m_overline.isSet()) m_overline=Line(Line::None);
    if (m_strikeoutline.isSet()) m_strikeoutline=Line(Line::None);
    if (m_underline.isSet()) m_underline=Line(Line::None);
  }
  //! returns the overline
  Line const &getOverline() const
  {
    return m_overline.get();
  }
  //! sets the overline
  void setOverline(Line const &line)
  {
    m_overline = line;
  }
  //! sets the overline style ( by default, we also reset the style)
  void setOverlineStyle(Line::Style style=Line::None, bool doReset=true)
  {
    if (doReset)
      m_overline = Line(style);
    else
      m_overline->m_style = style;
  }
  //! sets the overline type
  void setOverlineType(Line::Type type=Line::Single)
  {
    m_overline->m_type = type;
  }
  //! sets the overline word flag
  void setOverlineWordFlag(bool wordFlag=false)
  {
    m_overline->m_word = wordFlag;
  }
  //! sets the overline width
  void setOverlineWidth(float w)
  {
    m_overline->m_width = w;
  }
  //! sets the overline color
  void setOverlineColor(MWAWColor const &color)
  {
    m_overline->m_color = color;
  }

  //! returns the strikeoutline
  Line const &getStrikeOut() const
  {
    return m_strikeoutline.get();
  }
  //! sets the strikeoutline
  void setStrikeOut(Line const &line)
  {
    m_strikeoutline = line;
  }
  //! sets the strikeoutline style ( by default, we also reset the style)
  void setStrikeOutStyle(Line::Style style=Line::None, bool doReset=true)
  {
    if (doReset)
      m_strikeoutline = Line(style);
    else
      m_strikeoutline->m_style = style;
  }
  //! sets the strikeoutline type
  void setStrikeOutType(Line::Type type=Line::Single)
  {
    m_strikeoutline->m_type = type;
  }
  //! sets the strikeoutline word flag
  void setStrikeOutWordFlag(bool wordFlag=false)
  {
    m_strikeoutline->m_word = wordFlag;
  }
  //! sets the strikeoutline width
  void setStrikeOutWidth(float w)
  {
    m_strikeoutline->m_width = w;
  }
  //! sets the strikeoutline color
  void setStrikeOutColor(MWAWColor const &color)
  {
    m_strikeoutline->m_color = color;
  }

  //! returns the underline
  Line const &getUnderline() const
  {
    return m_underline.get();
  }
  //! sets the underline
  void setUnderline(Line const &line)
  {
    m_underline = line;
  }
  //! sets the underline style ( by default, we also reset the style)
  void setUnderlineStyle(Line::Style style=Line::None, bool doReset=true)
  {
    if (doReset)
      m_underline = Line(style);
    else
      m_underline->m_style = style;
  }
  //! sets the underline type
  void setUnderlineType(Line::Type type=Line::Single)
  {
    m_underline->m_type = type;
  }
  //! sets the underline word flag
  void setUnderlineWordFlag(bool wordFlag=false)
  {
    m_underline->m_word = wordFlag;
  }
  //! sets the underline width
  void setUnderlineWidth(float w)
  {
    m_underline->m_width = w;
  }
  //! sets the underline color
  void setUnderlineColor(MWAWColor const &color)
  {
    m_underline->m_color = color;
  }

  //! returns the language
  std::string const &language() const
  {
    return m_language.get();
  }
  //! set the language ( in the for en_US, en_GB, en, ...)
  void setLanguage(std::string const &lang)
  {
    m_language=lang;
  }
  //! add to the propList
  void addTo(librevenge::RVNGPropertyList &propList, std::shared_ptr<MWAWFontConverter> fontConverter) const;
  //! add to the propList to a list level
  void addToListLevel(librevenge::RVNGPropertyList &propList, std::shared_ptr<MWAWFontConverter> fontConverter) const;

  //! returns a string which can be used for debugging
  std::string getDebugString(std::shared_ptr<MWAWFontConverter> &converter) const;

  //! operator==
  bool operator==(MWAWFont const &f) const
  {
    return cmp(f) == 0;
  }
  //! operator!=
  bool operator!=(MWAWFont const &f) const
  {
    return cmp(f) != 0;
  }

  //! a comparison function
  int cmp(MWAWFont const &oth) const
  {
    int diff = id() - oth.id();
    if (diff != 0) return diff;
    if (size() < oth.size()) return -1;
    if (size() > oth.size()) return 1;
    if (m_sizeIsRelative.get() != oth.m_sizeIsRelative.get()) return m_sizeIsRelative.get() ? 1 : -1;
    if (flags() < oth.flags()) return -1;
    if (flags() > oth.flags()) return 1;
    if (m_deltaSpacing.get() < oth.m_deltaSpacing.get()) return -1;
    if (m_deltaSpacing.get() > oth.m_deltaSpacing.get()) return 1;
    if (m_deltaSpacingUnit.get() < oth.m_deltaSpacingUnit.get()) return -1;
    if (m_deltaSpacingUnit.get() > oth.m_deltaSpacingUnit.get()) return 1;
    if (m_widthStreching.get() < oth.m_widthStreching.get()) return -1;
    if (m_widthStreching.get() > oth.m_widthStreching.get()) return 1;
    diff = script().cmp(oth.script());
    if (diff != 0) return diff;
    diff = m_overline.get().cmp(oth.m_overline.get());
    if (diff != 0) return diff;
    diff = m_strikeoutline.get().cmp(oth.m_strikeoutline.get());
    if (diff != 0) return diff;
    diff = m_underline.get().cmp(oth.m_underline.get());
    if (diff != 0) return diff;
    if (m_color.get() < oth.m_color.get()) return -1;
    if (m_color.get() > oth.m_color.get()) return 1;
    if (m_backgroundColor.get() < oth.m_backgroundColor.get()) return -1;
    if (m_backgroundColor.get() > oth.m_backgroundColor.get()) return 1;
    if (m_language.get() < oth.m_language.get()) return -1;
    if (m_language.get() > oth.m_language.get()) return 1;
    return diff;
  }

protected:
  MWAWVariable<int> m_id /** font identificator*/;
  MWAWVariable<float> m_size /** font size */;
  MWAWVariable<bool> m_sizeIsRelative /** true if the size is percent */;
  MWAWVariable<float> m_deltaSpacing /** expand(&gt; 0), condensed(&lt; 0) depl*/;
  MWAWVariable<librevenge::RVNGUnit> m_deltaSpacingUnit /** the delta spacing unit */;
  MWAWVariable<float> m_widthStreching /** the width streching in percent */;
  MWAWVariable<Script> m_scriptPosition /** the sub/super script definition */;
  MWAWVariable<uint32_t> m_flags /** font attributes */;
  MWAWVariable<Line> m_overline /** overline attributes */;
  MWAWVariable<Line> m_strikeoutline /** overline attributes */;
  MWAWVariable<Line> m_underline /** underline attributes */;
  MWAWVariable<MWAWColor> m_color /** font color */;
  MWAWVariable<MWAWColor> m_backgroundColor /** font background color */;
  MWAWVariable<std::string> m_language /** the language if set */;
public:
  //! extra data
  std::string m_extra;
};

namespace MWAWFontManagerInternal
{
struct State;
}

//! a font manager which can be used to store fonts, ...
class MWAWFontManager
{
public:
  //! constructor
  explicit MWAWFontManager(std::shared_ptr<MWAWFontConverter> const &fontConverter);
  //! destructor
  ~MWAWFontManager();
  //! returns a span id which can be used to call the list
  int getId(MWAWFont const &font);
  //! returns the font corresponding to an id
  bool getFont(int id, MWAWFont &font) const;
  //! returns the font converter
  std::shared_ptr<MWAWFontConverter> getFontConverter();
protected:
  //! the state
  std::shared_ptr<MWAWFontManagerInternal::State> m_state;
private:
  MWAWFontManager(MWAWFontManager const &) = delete;
  MWAWFontManager &operator=(MWAWFontManager const &) = delete;
};
#endif
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
