
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#ifndef HDR_dbText
#define HDR_dbText

#include "config.h"

#include "dbTypes.h"
#include "dbMemStatistics.h"
#include "dbPoint.h"
#include "dbBox.h"
#include "dbTrans.h"
#include "dbShapeRepository.h"
#include "dbObjectTag.h"
#include "dbHersheyFont.h"
#include "tlCollection.h"
#include "tlString.h"

#include <string>
#include <string.h>

namespace db {

template <class Coord> class generic_repository;
class ArrayRepository;

/**
 *  @brief A text reference
 *  
 *  Text references are used in texts to refer to text strings through
 *  a proxy. Text references can change their strings without changing
 *  the text object's ordering. 
 *  The main use is to provide late text binding as required for the OASIS
 *  reader in some cases.
 *  String references are reference counted and remove themselves. 
 */
class KLAYOUT_DLL StringRef
  : public tl::Collectable
{
public:
  /**
   *  @brief Default constructor
   */
  StringRef () 
    : m_value (), m_ref_count (0) 
  {
    //  .. nothing yet ..  
  }

  /**
   *  @brief Constructor from a const char * argument
   */
  StringRef (const char *s) 
    : m_value (s), m_ref_count (0) 
  {
    //  .. nothing yet ..  
  }

  /**
   *  @brief Constructor from a std::string argument
   */
  StringRef (const std::string &s) 
    : m_value (s), m_ref_count (0) 
  { 
    //  .. nothing yet ..  
  }

  /**
   *  @brief Increment the reference counter
   */
  void add_ref () 
  {
     ++m_ref_count;
  }
 
  /**
   *  @brief Decrement the reference counter and remove the object when it reaches 0
   */
  void remove_ref ()
  {
     --m_ref_count;
     if (m_ref_count == 0) {
       delete this;
     }
  }

  /**
   *  @brief Assignment of a std::string object
   */
  StringRef &operator= (const std::string &s)
  {
    m_value = s;
    return *this;
  }

  /**
   *  @brief Get the actual string
   */
  const std::string &value () const
  {
    return m_value;
  }

  /**
   *  @brief Get the actual string (non-const)
   */
  std::string &value ()
  {
    return m_value;
  }

private:
  friend class tl::Collection<StringRef>;
  std::string m_value;
  size_t m_ref_count;

  ~StringRef () { }
  StringRef (const StringRef &d);
  StringRef operator= (const StringRef &d);
};

/**
 *  @brief A string repository class 
 *
 *  A string repository holds StringRef objects.
 *  It acts as a factory for StringRef objects and allows to rename strings.
 */
class KLAYOUT_DLL StringRepository
{
public:
  /** 
   *  @brief Create a string reference object.
   * 
   *  String references are intended for keeping "static" strings and 
   *  referring to them by text objects. String references are unique - 
   *  even if the strings are the same, they are sematically different. 
   *  The text objects will compare pointers rather than the content of
   *  the string references. The string reference content can be changed 
   *  therefore. The main use case for these objects is the
   *  OASIS reader, where forward references of text strings requires a
   *  late binding of the text.
   *  A string reference's text can be set by using the change_string_ref
   *  method.
   */
  const StringRef *create_string_ref ()
  {
    StringRef *ref = new StringRef ();
    m_string_refs.insert (ref);
    return ref;
  }

  /**
   *  @brief Change the string associated with a StringRef
   */
  void change_string_ref (const StringRef *ref, const std::string &s)
  {
    *(const_cast<StringRef *> (ref)) = s;
  }

  /**
   *  @brief For debugging purposes: get the number of entries
   */
  size_t size () const
  {
    size_t n = 0;
    for (tl::Collection<StringRef>::const_iterator i = m_string_refs.begin (); i != m_string_refs.end (); ++i) {
      ++n;
    }
    return n;
  }

private:
  tl::Collection<StringRef> m_string_refs;
};

/**
 *  @brief A text object
 *
 *  A text object has a point (location), a text, a text transformation,
 *  a text size and a font id. Text size and font id are provided to be
 *  be able to render the text correctly.
 */

template <class C>
class text
{
public:
  typedef C coord_type;
  typedef db::coord_traits<C> coord_traits;
  typedef db::point<C> point_type;
  typedef db::box<C> box_type;
  typedef db::simple_trans<C> trans_type;
  typedef db::object_tag< text<C> > tag;
  
  /**
   *  @brief The standard constructor without a text
   *
   *  The standard constructor is taking a transformation (with point),
   *  a text size value (which can be zero) and a font id which can 0 
   *  also.
   * 
   *  @param t The transformation of the text
   *  @param h The size
   *  @param f The font id
   */
  text (const trans_type &t, coord_type h = 0, Font f = NoFont, HAlign halign = NoHAlign, VAlign valign = NoVAlign)
    : mp_ptr (0), m_trans (t), m_size (h), m_font (f), m_halign (halign), m_valign (valign)
  {
    //  .. nothing yet ..
  }

  /**
   *  @brief The standard constructor from a StringRef object
   *
   *  The standard constructor is taking a transformation (with point),
   *  a text size value (which can be zero) and a font id which can 0 
   *  also.
   *  The StringRef object must be owned by some other entity with a 
   *  lifetime that covers the text object's lifetime.
   * 
   *  @param s The StringRef object
   *  @param t The transformation of the text
   *  @param h The size
   *  @param f The font id
   */
  text (const StringRef *sref, const trans_type &t, coord_type h = 0, Font f = NoFont, HAlign halign = NoHAlign, VAlign valign = NoVAlign)
    : m_trans (t), m_size (h), m_font (f), m_halign (halign), m_valign (valign)
  {
    const_cast <StringRef *> (sref)->add_ref ();
    mp_ptr = (char *)((size_t)sref | 1);
  }

  /**
   *  @brief The standard constructor from a const char *
   *
   *  The standard constructor is taking a transformation (with point),
   *  a text size value (which can be zero) and a font id which can 0 
   *  also.
   * 
   *  @param s The text 
   *  @param t The transformation of the text
   *  @param h The size
   *  @param f The font id
   */
  text (const char *s, const trans_type &t, coord_type h = 0, Font f = NoFont, HAlign halign = NoHAlign, VAlign valign = NoVAlign)
    : m_trans (t), m_size (h), m_font (f), m_halign (halign), m_valign (valign)
  {
    set_string_internal (s);
  }

  /**
   *  @brief The standard constructor 
   *
   *  The standard constructor is taking a transformation (with point),
   *  a text size value (which can be zero) and a font id which can 0 
   *  also.
   * 
   *  @param s The text 
   *  @param t The transformation of the text
   *  @param h The size
   *  @param f The font id
   */
  text (const std::string &s, const trans_type &t, coord_type h = 0, Font f = NoFont, HAlign halign = NoHAlign, VAlign valign = NoVAlign)
    : m_trans (t), m_size (h), m_font (f), m_halign (halign), m_valign (valign)
  {
    set_string_internal (s.c_str ());
  }

  /** 
   *  @brief Default constructor
   *
   *  Creates an empty text object at (0,0) with empty text.
   */
  text ()
    : mp_ptr (0), m_trans (), m_size (0), m_font (NoFont), m_halign (NoHAlign), m_valign (NoVAlign)
  {
    // .. nothing yet ..
  }

  /** 
   *  @brief Copy constructor
   */
  text (const text &d)
    : mp_ptr (0), m_trans (), m_size (0), m_font (NoFont), m_halign (NoHAlign), m_valign (NoVAlign)
  {
    operator= (d);
  }

  /**
   *  @brief The destructor
   */
  ~text()
  {
    cleanup ();
  }

  /**
   *  @brief Assignment 
   */
  text &operator= (const text &d)
  {
    if (&d != this) {

      m_trans = d.m_trans;
      m_size = d.m_size;
      m_font = d.m_font;
      m_halign = d.m_halign;
      m_valign = d.m_valign;

      cleanup ();

      size_t p = (size_t) d.mp_ptr;
      if (p & 1) {
        reinterpret_cast <StringRef *> (p - 1)->add_ref ();
        mp_ptr = d.mp_ptr;
      } else if (d.mp_ptr) {
        set_string_internal (d.mp_ptr);
      }

    }

    return *this;
  }

  /**
   *  @brief The constructor converting a double-type text to this type
   */
  static text<C> from_double (const db::text<double> &t)
  {
    return text<C> (t.string (), trans_type::from_double (t.trans ()), db::from_double_coord<C> () (t.size ()), t.font (), t.halign (), t.valign ());
  }

  /**
   *  @brief The (dummy) translation operator
   */
  void translate (const text<C> &d, db::generic_repository<C> &, db::ArrayRepository &)
  {
    //  don't use StringRef's on translate - since those live in the source layout, we must not copy them
    m_trans = d.m_trans;
    m_size = d.m_size;
    m_font = d.m_font;
    m_halign = d.m_halign;
    m_valign = d.m_valign;

    string (d.string ());
  }

  /**
   *  @brief The (dummy) translation operator with transformation
   */
  template <class T>
  void translate (const text<C> &d, const T &t, db::generic_repository<C> &r, db::ArrayRepository &a)
  {
    translate (d, r, a);
    transform (t);
  }

  /**
   *  @brief Resolve any string reference if there is one
   *
   *  Calling this method basically disconnects the text from the string repository and
   *  should be used if a text object is about to be transferred to another layout.
   */
  void resolve_ref ()
  {
    if ((size_t (mp_ptr) & 1) != 0) {
      string (string ());
    } 
  }

  /**
   *  @brief Ordering operator
   */
  bool operator< (const text<C> &b) const
  {
    //  Compare transformation
    if (m_trans != b.m_trans) {
      return m_trans < b.m_trans;
    }

    //  Compare strings or StringRef's by pointer (that is
    //  the intention of StringRef's: if the text changes, the sort
    //  order must not!)
    if (((size_t) mp_ptr & 1) == 0 || ((size_t) b.mp_ptr & 1) == 0) {
      int c = strcmp (string (), b.string ());
      if (c != 0) {
        return c < 0;
      }
    } else {
      if (mp_ptr != b.mp_ptr) {
        //  if references are present, use their pointers rather than the strings
        //  if they belong to the same collection
        const StringRef *r1 = reinterpret_cast<const StringRef *> (mp_ptr - 1);
        const StringRef *r2 = reinterpret_cast<const StringRef *> (b.mp_ptr - 1);
        if (r1->collection () != r2->collection ()) {
          int c = strcmp (r1->value ().c_str (), r2->value ().c_str ());
          if (c != 0) {
            return c < 0;
          }
        } else {
          return mp_ptr < b.mp_ptr;
        }
      }
    }

#if 1
    //  Compare size and presentation flags - without that, the text repository does not work properly.
    if (m_size != b.m_size) {
      return m_size < b.m_size;
    }
    if (m_font != b.m_font) {
      return m_font < b.m_font;
    }
    if (m_halign != b.m_halign) {
      return m_halign < b.m_halign;
    }
    if (m_valign != b.m_valign) {
      return m_valign < b.m_valign;
    }
#endif

    return false;
  }

  /**
   *  @brief Equality test
   */
  bool operator== (const text<C> &b) const
  {
    //  Compare transformation
    if (m_trans != b.m_trans) {
      return false;
    }

    //  Compare strings or StringRef's by pointer (that is
    //  the intention of StringRef's: if the text changes, the sort
    //  order must not!)
    if (((size_t) mp_ptr & 1) == 0 || ((size_t) b.mp_ptr & 1) == 0) {
      int c = strcmp (string (), b.string ());
      if (c != 0) {
        return false;
      }
    } else {
      if (mp_ptr != b.mp_ptr) {
        //  if references are present, use their pointers rather than the strings
        //  if they belong to the same collection
        const StringRef *r1 = reinterpret_cast<const StringRef *> (mp_ptr - 1);
        const StringRef *r2 = reinterpret_cast<const StringRef *> (b.mp_ptr - 1);
        if (r1->collection () != r2->collection ()) {
          int c = strcmp (r1->value ().c_str (), r2->value ().c_str ());
          if (c != 0) {
            return false;
          }
        } else {
          return false;
        }
      }
    }

#if 1
    //  Compare size and presentation flags - without that, the text repository does not work properly.
    if (m_size != b.m_size) {
      return false;
    }
    return m_font == b.m_font && m_halign == b.m_halign && m_valign == b.m_valign;
#else
    //  Don't compare size, font and alignment
    return true;
#endif
  }

  /** 
   *  @brief Inequality test
   */
  bool operator!= (const text<C> &b) const
  {
    return !operator== (b);
  }

  /**
   *  @brief The text string write accessor
   */
  void string (const std::string &s)
  { 
    cleanup ();
    set_string_internal (s);
  }
 
  /**
   *  @brief The text string accessor
   */
  const char *string () const
  {
    size_t p = (size_t) mp_ptr;
    if (p & 1) {
      return reinterpret_cast<StringRef *> (p - 1)->value ().c_str ();
    } else if (mp_ptr) {
      return mp_ptr;
    } else {
      return "";
    }
  }

  /**
   *  @brief The transformation write accessor
   */
  void trans (const trans_type &t) 
  { 
    m_trans = t;
  }
   
  /**
   *  @brief The transformation accessor
   */
  const trans_type &trans () const 
  { 
    return m_trans;
  }
   
  /**
   *  @brief The size write accessor
   */
  void size (coord_type s) 
  { 
    m_size = s;
  }
   
  /**
   *  @brief The size accessor
   */
  coord_type size () const 
  { 
    return m_size;
  }
   
  /**
   *  @brief The font id write accessor
   */
  void font (Font f)
  { 
    m_font = f;
  }
   
  /**
   *  @brief The font id accessor
   */
  Font font () const 
  { 
    return m_font;
  }
   
  /**
   *  @brief The horizontal alignment flags write accessor
   */
  void halign (HAlign a)
  { 
    m_halign = a;
  }
   
  /**
   *  @brief The horizontal alignment flags 
   */
  HAlign halign () const 
  { 
    return m_halign;
  }
   
  /**
   *  @brief The vertical alignment flags write accessor
   */
  void valign (VAlign a)
  { 
    m_valign = a;
  }
   
  /**
   *  @brief The vertical alignment flags 
   */
  VAlign valign () const 
  { 
    return m_valign;
  }
   
  /**
   *  @brief Transform the text.
   *
   *  Transforms the text with the given transformation.
   *  Modifies the text with the transformed text.
   *  The transformation does not transform text size and alignment flags.
   *  
   *  @param t The transformation to apply.
   *
   *  @return The transformed text.
   */
  template <class Tr>
  text<C> &transform (const Tr &t)
  {
    typedef typename Tr::target_coord_type target_coord_type;
    m_trans = simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (m_trans.disp ()));
    m_size = t.ctrans (m_size);
    return *this;
  }

  /**
   *  @brief Transform the text.
   *
   *  Transforms the text with the given transformation.
   *  Does not modify the text but returns the transformed text.
   *  The transformation does not transform text size and alignment flags.
   *  
   *  @param t The transformation to apply.
   *
   *  @return The transformed text.
   */
  template <class Tr>
  text<typename Tr::target_coord_type> transformed (const Tr &t) const
  {
    typedef typename Tr::target_coord_type target_coord_type;
    size_t p = (size_t) mp_ptr;
    if (p & 1) {
      return text<target_coord_type> (reinterpret_cast<StringRef *> (p - 1), simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (m_trans.disp ())), t.ctrans (m_size), m_font);
    } else if (mp_ptr) {
      return text<target_coord_type> (mp_ptr, simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (m_trans.disp ())), t.ctrans (m_size), m_font);
    } else {
      return text<target_coord_type> (simple_trans<target_coord_type> ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (m_trans.disp ())), t.ctrans (m_size), m_font);
    }
  }

  /**
   *  @brief Return the moved text
   *
   *  @param p The distance to move the text.
   * 
   *  @return The moved text.
   */
  text moved (const point_type &p) const
  { 
    text d (*this);
    d.move (p);
    return d;
  }
   
  /**
   *  @brief Move by a distance
   * 
   *  @param p The distance to move the path.
   */
  void move (const point_type &p) 
  { 
    m_trans = trans_type (p) * m_trans;
  }
   
  /**
   *  @brief Return the bounding box
   *
   *  The bounding box is a box consisting of a single point
   */
  box_type box () const
  {
    return box_type (m_trans.disp (), m_trans.disp ());
  }

  /**
   *  @brief String conversion
   */
  std::string to_string () const
  {
    return std::string ("(") + tl::to_quoted_string (string ()) + "," + m_trans.to_string () + ")";
  }

  /**
   *  @brief Reduce the text
   *
   *  Reduction of a text normalizes the text by extracting
   *  a suitable transformation and placing the text in a unique
   *  way.
   *
   *  @return The transformation that must be applied to render the original text
   */
  void reduce (simple_trans<coord_type> &tr)
  {
    tr = m_trans;
    m_trans = trans_type ();
  }

  /**
   *  @brief Reduce the text
   *
   *  Reduction of a text normalizes the text by extracting
   *  a suitable transformation and placing the text in a unique
   *  way.
   *
   *  @return The transformation that must be applied to render the original text
   */
  void reduce (disp_trans<coord_type> &tr)
  {
    tr = m_trans.disp ();
    m_trans = trans_type (m_trans.rot ());
  }

  /**
   *  @brief Reduce the text for unit transformation references
   *
   *  Does not do any reduction since no transformation can be provided.
   *
   *  @return A unit transformation
   */
  void reduce (unit_trans<C> &)
  {
    //  .. nothing ..
  }

  size_t mem_used () const
  {
    size_t p = (size_t) mp_ptr;
    if (! (p & 1) && mp_ptr != 0) {
      return sizeof (char *) + strlen (mp_ptr) + db::mem_used (m_trans) + db::mem_used (m_size) + db::mem_used (m_font);
    } else {
      return sizeof (char *) + db::mem_used (m_trans) + db::mem_used (m_size) + db::mem_used (m_font);
    }
  }

  size_t mem_reqd () const
  {
    return mem_used ();
  }

private:
  char *mp_ptr;
  trans_type m_trans;
  coord_type m_size;
  Font m_font : 26;
  HAlign m_halign : 3;
  VAlign m_valign : 3;

  void cleanup ()
  {
    if (mp_ptr != 0) {
      if (((size_t) mp_ptr & 1) == 0) {
        delete [] mp_ptr;
      } else {
        reinterpret_cast<StringRef *>((size_t) mp_ptr - 1)->remove_ref ();
      }
    }

    mp_ptr = 0;
  }

  void set_string_internal (const std::string &s)
  {
    mp_ptr = new char[s.size() + 1];
    strncpy (mp_ptr, s.c_str (), s.size () + 1);
  }
};

/**
 *  @brief Binary * operator (transformation)
 *
 *  Transforms the text with the given transformation and 
 *  returns the result.
 *
 *  @param t The transformation to apply
 *  @param s The text to transform
 *  @return t * s
 */
template <class C, class Tr>
inline text<typename Tr::target_coord_type> 
operator* (const Tr &t, const text<C> &s)
{
  return s.transformed (t);
}

/**
 *  @brief Output stream insertion operator
 */
template <class C>
inline std::ostream &
operator<< (std::ostream &os, const text<C> &s)
{
  return (os << s.to_string ());
}

/**
 *  @brief The standard text typedef
 */
typedef text<db::Coord> Text;

/**
 *  @brief The double coordinate text typedef
 */
typedef text<db::DCoord> DText;

/** 
 *  @brief A text reference
 *
 *  A text reference is basically a proxy to a text and
 *  is used to implement text references with a repository.
 */

template <class Text, class Trans>
struct text_ref
  : public shape_ref<Text, Trans>
{
  typedef typename Text::coord_type coord_type;
  typedef typename Text::box_type box_type;
  typedef typename Text::point_type point_type;
  typedef Trans trans_type;
  typedef Text text_type;
  typedef db::generic_repository<coord_type> repository_type;
  typedef db::object_tag< text_ref<Text, Trans> > tag;

  /**
   *  @brief The default constructor.
   *  
   *  The default constructor creates a invalid text reference
   */
  text_ref ()
    : shape_ref<Text, Trans> ()
  {
    // .. nothing yet ..
  }

  /**
   *  @brief The constructor creating a reference from an actual text
   */
  text_ref (const text_type &p, repository_type &rep)
    : shape_ref<Text, Trans> (p, rep)
  {
    // .. nothing yet ..
  }

  /**
   *  @brief The constructor creating a reference from an text pointer and transformation
   *
   *  The text pointer passed is assumed to reside in a proper repository.
   */
  text_ref (const text_type *p, const trans_type &t)
    : shape_ref<Text, Trans> (p, t)
  {
    // .. nothing yet ..
  }

  /**
   *  @brief The translation constructor.
   *  
   *  This constructor allows to copy a text reference from one
   *  repository to another
   */
  text_ref (const text_ref &ref, repository_type &rep)
    : shape_ref<Text, Trans> (ref, rep)
  {
    // .. nothing yet ..
  }

  /**
   *  @brief The transformation translation constructor
   *  
   *  This constructor allows to copy a text reference with a certain transformation
   *  to one with another transformation
   */
  template <class TransIn>
  text_ref (const text_ref<Text, TransIn> &ref)
    : shape_ref<Text, Trans> (ref.ptr (), ref.trans ())
  {
    // .. nothing yet ..
  }

  /** 
   *  @brief Return the transformed object
   * 
   *  This version does not change the object and is const.
   */
  template <class TargetTrans>
  text_ref<Text, TargetTrans> transformed (const TargetTrans &t) const
  {
    text_ref<Text, TargetTrans> tref (*this);
    tref.transform (t);
    return tref;
  }
};

/**
 *  @brief Binary * operator (transformation)
 *
 *  Transforms the text reference with the given transformation and 
 *  returns the result.
 *
 *  @param t The transformation to apply
 *  @param p The text reference to transform
 *  @return t * p
 */
template <class Text, class Tr, class TargetTr>
inline text_ref<Text, TargetTr>
operator* (const TargetTr &t, const text_ref<Text, Tr> &p)
{
  return p.transformed (t);
}

/**
 *  @brief The text reference typedef
 */
typedef text_ref<Text, Disp> TextRef;

/**
 *  @brief The text reference typedef for double coordinates
 */
typedef text_ref<DText, DDisp> DTextRef;

/**
 *  @brief The text reference (without transformation) typedef
 */
typedef text_ref<Text, UnitTrans> TextPtr;

/**
 *  @brief The text reference (without transformation) typedef for double coordinates
 */
typedef text_ref<DText, DUnitTrans> DTextPtr;


template <class X>
size_t mem_used (const text<X> &x)
{
  return x.mem_used ();
}

template <class X>
size_t mem_reqd (const text<X> &x)
{
  return x.mem_reqd ();
}

} // namespace db

namespace tl 
{
  template <class C>
  struct type_traits <db::text<C> > : public type_traits<void> 
  {
    typedef true_tag supports_extractor;
    typedef true_tag supports_to_string;
    typedef true_tag has_less_operator;
    typedef true_tag has_equal_operator;
  };

  template<> void KLAYOUT_DLL extractor_impl (tl::Extractor &ex, db::Text &p);
  template<> void KLAYOUT_DLL extractor_impl (tl::Extractor &ex, db::DText &p);

  template<> bool KLAYOUT_DLL test_extractor_impl (tl::Extractor &ex, db::Text &p);
  template<> bool KLAYOUT_DLL test_extractor_impl (tl::Extractor &ex, db::DText &p);

} // namespace tl

#endif

