
/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#ifndef OPENNURBS_TEXT_H_INCLUDED
#define OPENNURBS_TEXT_H_INCLUDED

#if defined(ON_DLL_TEMPLATE)
ON_DLL_TEMPLATE template ON_ClassArray< class ON_Font >;
ON_DLL_TEMPLATE template ON_SimpleArray< class ON_TextRun* >;
ON_DLL_TEMPLATE template ON_SimpleArray< class ON_FontGlyph* >;
#endif

#define ON_TEXT_BRACKET_FRACTION
class ON_CLASS ON_TextContent  : public ON_Geometry
{
  ON_OBJECT_DECLARE(ON_TextContent);

public:
  static const ON_TextContent Empty;

public:

  ON_TextContent()= default;
  ~ON_TextContent();
 
  ON_TextContent(const ON_TextContent& src);
  ON_TextContent& operator=(const ON_TextContent& src);

private:
  void Internal_Destroy();
  void Internal_CopyFrom(
    const ON_TextContent& src
    );

public:

  /*
  Returns:
    A hash of the information that determines the text content
    using wrapped text with evaluated fields.
  */
  ON_SHA1_Hash TextContentHash() const;

  /*
  Parameters:
    bApplyWrapping - [in]
      true - hash wrapped text
      false - has unwrapped text
    bEvaluateFields - [in]
      true - hash text with fields evaluated
      false - hash text with fields unevaluated
  Returns:
    A hash of the information that determines the text content
    without evaluating the fields.
  */
  ON_SHA1_Hash TextContentHash(
    bool bApplyWrapping,
    bool bEvaluateFields
  ) const;

public:

  bool IsValid( class ON_TextLog* text_log = nullptr ) const override;

  // Parses text string and makes runs
  bool Create(
    const wchar_t* RtfString,
    ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings
    const ON_DimStyle* dimstyle,
    bool bWrapped,
    double rect_width,
    double text_rotation_radians
    );

  bool Create(
    const wchar_t* RtfString,
    ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings
    const ON_DimStyle* dimstyle
    );

  bool ReplaceTextString(
    const wchar_t* RtfString,
    ON::AnnotationType annotation_type, // used to select type specific dimstyle properties like text alignment settings
    const ON_DimStyle* dimstyle
    );

  bool RebuildRuns(
    ON::AnnotationType annotation_type,
    const ON_DimStyle* dimstyle
  );

  /*
  Returns:
    The value of ON_DimStyle.TextPositionPropertiesHash() of the ON_DimStyle
    passed to Create(), ReplaceTextString(), or RebuildRuns().
  */
  ON_SHA1_Hash DimStyleTextPositionPropertiesHash() const;

  /*
  Returns:
    True if this text position information used to create this text
    is identical to the text position paramters on dimstyle.
  */
  bool EqualTextPositionProperties(
    ON::AnnotationType annotation_type,
    const class ON_DimStyle* dimstyle
  ) const;

  bool GetGlyphContours(
    const ON_Font* text_font,
    bool bSingleStrokeFont,
    double text_height,
    ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours
  ) const;

  bool GetGlyphContours(
    const ON_Font* text_font,
    bool bSingleStrokeFont,
    const ON_Xform& text_xform,
    ON_ClassArray< ON_ClassArray< ON_SimpleArray< ON_Curve* > > >& text_contours
  ) const;

private:
  static ON::AnnotationType Internal_AlignmentAnnotationType(
    ON::AnnotationType annotation_type
  );

private:

  bool Internal_SetText(
    const wchar_t* text,
    const ON_DimStyle* dimstyle);
  /*
  Parameters:
    dim_style - [in]
      Parse and compose text using the parameters in this dimstyle.
      If nullptr, then ON_DimStyle::Default is used as the dimstyle.
    bComposeAndUpdateRtf - [in]
      If true, then the parsed runs are passed to RtfComposer::Compose()
      to compose create an efficient and compressed rtf. Then
      ON_TextContent.m_text is set to the efficient and compressed rtf
      returned from RtfComposer::Compose().

      When this->m_text might be a bloated result from a text editing
      control or a simple string like "Hello world" set during
      construction, then bComposeAndUpdateRtf should be true.

      When this->m_text has already been through RtfComposer::Compose()
      and you simply need to create the runs from m_text, then
      bComposeAndUpdateRtf should be false.  Reading binary archives
      is one case when bComposeAndUpdateRtf should be false.

      Internal_ParseRtf() sets the m_dimstyle_text_position_properties_hash member
      on ON_TextContent
  */
 bool Internal_ParseRtf(
    const wchar_t* rtf_string,
    const ON_DimStyle* dim_style,
    bool bComposeAndUpdateRtf
    );
  bool Internal_ParseRtfDefault(
    const wchar_t* rtf_string,
    bool bComposeAndUpdateRtf
    );

public:

  /*
  Returns:
    Raw text that can contain rich text formatting instructions.
    Fields are not evaluated.
  */
  const ON_wString RichText() const;

  /*
  Returns:
  Plain text information with any rich text formatting instructions removed.
  The result string from evaluating fields is included
  Field results may be cached from previous evaluation
  */
  const ON_wString PlainText() const;
  /*
  Same as PlainText() but separated wrapped run lines with '\n' for soft return
  and '\r''\n' for hard returns
  */
  const ON_wString WrappedPlainText() const;

  /*
  Returns:
  Plain text information with any rich text formatting instructions removed.
  Fields are not evaluated
  */
  const ON_wString PlainTextWithFields() const;
  /*
  Same as PlainTextWithFields() but separated wrapped run lines with '\n' for soft return
  and '\r''\n' for hard returns
  */
  const ON_wString WrappedPlainTextWithFields() const;

private:
  void Internal_SetRunTextHeight(double height);

public:
  void GetAlignment(ON::TextHorizontalAlignment& horz, ON::TextVerticalAlignment& vert) const;
  void SetAlignment(ON::TextHorizontalAlignment horz, ON::TextVerticalAlignment vert);

  // FormattingRectangleWidth is a width set by text wrapping. It's in model units
  double FormattingRectangleWidth() const;
  void SetFormattingRectangleWidth(double width);
  // Rotation in radians around origin
  double TextRotationRadians() const;
  void SetTextRotationRadians(double rotation);
  // Rotation in degrees around origin
  double TextRotationDegrees() const;
  void SetTextRotationDegrees(double rotation);

  unsigned int EvaluationSerialNumber() const;
  void SetEvaluationSerialNumber(unsigned int sn) const;

  void RealignTextRuns(ON::TextHorizontalAlignment new_h_align);

  // virtual
  void Dump( ON_TextLog& ) const override; // for debugging

  int Dimension() const override;

  ON::object_type ObjectType() const override;

  bool Write(
    ON_BinaryArchive&  // serialize definition to binary archive
    ) const override;

  bool Read(
    ON_BinaryArchive&  // restore definition from binary archive
    ) override;

  const ON_BoundingBox TextContentBoundingBox() const;

  // virtual ON_Geometry GetBBox override		
  bool GetBBox( double* boxmin, double* boxmax, bool bGrowBox = false ) const override;

  void ClearBoundingBox() override;

  bool Transform(const ON_Xform& xform) override;

  // Wrap text to a specified width in model space
  bool WrapText(double width) const;
  
  // True if text has wrapped runs, else false
  bool HasWrappedRuns() const;

  // True if flag to wrap is set
  bool TextIsWrapped() const;
  void SetTextIsWrapped(bool wrapped);

private:
  void Internal_DeleteWrappedRuns() const;

  const ON_wString Internal_GetPlainText(bool evaluate_fields, bool wrapped) const;


public:

  // Get the size of a 2d rectangle enclosing text
  bool Get2dSize(bool raw, double& width, double& height) const;

  // Get corners of the whole text object
  // corners requires space for 4 points
  bool Get2dCorners(ON_2dPoint corners[4]) const;

  // Get corners of the whole text object
  // corners requires space for 4 points
  // Basic size of text, not including annotation scaling or 2d rotation
  // And rotated and translated in 3d to text's plane (not object's plane)
  bool Get3dCorners(ON_3dPoint corners[4]) const;

  // Get corners of the whole text object
  // inflated by the border distance for mask drawing
  // corners requires space for 4 points
  bool Get3dMaskCorners(double border, ON_3dPoint corners[4]) const;

  // Gets endpoints of a line under the text offset down by textgap
  bool Get3dUnderline(ON_3dPoint ends[2], double scaled_gap) const;

  // Get corners of individual runs
  // corners requires space for 4 points
  bool GetRun3dCorners(const ON_TextRun* run, ON_3dPoint corners[4]) const;

  // returns the base point and with grip using the current alignments
  void GetGripPoints(ON_2dPoint& base, ON_2dPoint& width) const;

  ON_Mesh* Get2dPickMesh() const;
  ON_Mesh* Get3dPickMesh() const;

  // Returns pointer to either m_runs, the basic parsed and evaluated text
  // or m_wrapped_runs which is the runs after text wrapping
  // m_wrapped_runs will be null unless the text has been wrapped
  // If raw is false and m_wrapped_runs is not null, m_wrapped_runs will be returned
  // If raw is true or m_wrapped_runs is null, m_runs will be returned  
  ON_TextRunArray* TextRuns(bool bRaw) const;

  const wchar_t* RtfText() const;

  /*
  With runs in place, compose the text in the runs and
  fill in the Text's string that is returned by RtfText
  */
  bool ComposeText();

  ///*
  //Parameters:
  //  dimsytle - [in]
  //Returns:
  //  true if style was passed as dimstyle paramter to Create(), ReplaceTextString(),
  //  or RebuildRuns() and used to create the current text runs.
  //*/
  //bool IsCurrentDimStyle(
  //  const ON_DimStyle* dimsytle
  //) const;

  //void SetCurrentDimStyle(const ON_DimStyle* dimstyle) const;

private:
  // Data members
  //-----------------------
  ON_wString                  m_text;          // Rtf laden string
  double                      m_rect_width = 1.0e300;  // formatting rectangle width in model units
  double                      m_rotation_radians = 0.0;   // radians rotation around origin
  double m_reserved_dbl = 0.0;
  ON::TextHorizontalAlignment m_h_align = ON::TextHorizontalAlignment::Left;    // Left, Center, Right
  ON::TextVerticalAlignment   m_v_align = ON::TextVerticalAlignment::Bottom;  // Top, Middle, Bottom
  
  // true when text is wrapped
  // Set by calling WrapText() or SetTextIsWrapped(true).
  // Query by calling TextIsWrapped().
  mutable bool                m_bWrapText = false;
  
  // m__runs and m__wrapped_runs are runtime information 
  // generated by parsing m_text and other information.
  mutable ON_TextRunArray     m__runs;

  mutable ON_TextRunArray*    m__wrapped_runs = nullptr;

  // display cache runtime value
  mutable unsigned int        m_run_evaluation_sn = 0; 

  // annotation type used to select dimstyle text alignment settings.
  mutable ON::AnnotationType  m_annotation_type = ON::AnnotationType::Unset;

  // dimstyle text position properties used to calculate the runs
  mutable ON_SHA1_Hash m_dimstyle_text_position_properties_hash = ON_SHA1_Hash::ZeroDigest;

  // hash of m_text, m_bWrapping, m_rect_width, m_rotation_radians, alignment
  mutable ON_SHA1_Hash m_text_content_sub_hash = ON_SHA1_Hash::ZeroDigest;

  ON_SHA1_Hash Internal_TextContentSubHash() const;
  void Internal_ClearTextContentHash() const;

  // runtime bounding box
  // Value of TextContentHash() when m_text_content_bbox was set.
  mutable ON_SHA1_Hash   m_text_content_bbox_hash = ON_SHA1_Hash::ZeroDigest;
  mutable ON_BoundingBox m_text_content_bbox = ON_BoundingBox::EmptyBoundingBox;
  
  ON__INT_PTR m_reserved0 = (ON__INT_PTR)0;
public:
  friend class ON_Text;

  /*
  Description:
  Calculates the size, spacing and position of the runs in the ON_TextContent
  Parameters:
  [in/out] ON_TextContent& text  - Text to measure. Modified to store results
  [in] bool raw - if true, measure m_runs
  [in] bool wrapped - if true, measure m_wrapped_runs
  Returns:
  true = Success
  false = Failure
  Remarks:
  The runs in the text are modified to store the location info for positioning
  within the text object
  */
  static bool MeasureTextContent(ON_TextContent* text, bool raw, bool wrapped);

  /*
  Description:
  Calculates the size, spacing and position of the runs in the ON_TextRunArray
  Parameters:
  [in/out] ON_TextRunArray* runs  - TextRuns to measure. Modified to store results
  [in] ON::TextVerticalAlignment v_align - how to align the text
  [in] ON::TextHorizontalAlignment h_align)
  Returns:
  true = Success
  false = Failure
  Remarks:
  The runs in the text are modified to store the location info for positioning
  within the text object
  */
  static bool MeasureTextRunArray(
    ON_TextRunArray* runs,
    ON::TextVerticalAlignment v_align,
    ON::TextHorizontalAlignment h_align);

  /*
  Description:
    Calculates the size, spacing and position of the ON_TextRun within an ON_TextContent object
  Parameters:
    [in/out] ON_TextRun& run  - Run to measure. Modified to store results
  Returns:
    true = Success
    false = Failure
  Remarks:
    The runs in the text are modified to store the location info for positioning
    within the text object
  */
  static bool MeasureTextRun(ON_TextRun* run);
  static bool CreateStackedText(ON_TextRun* run);
  static bool CreateStackedText(
    ON_TextRun* run,
    int cpcount,
    const ON__UINT32* cp,
    ON__UINT32 stack_delimiter = L'/');
  
  /*
      Replaces runs[i] with stacked runs if any 
      "[[xx/xx]]" strings are found in wstr
      Returns the number of runs added to the array
  */
  static int FindAndStackFractions(ON_TextRunArray* runs, int i, ON_wString wstr);

  /*
  Description:
  Evaluates the field instructions in the run and puts the results
  in run->m_display_string.
  Parameters:
  [in/out] ON_TextRun& run  - Run to evsluste. Modified to store results
  Returns:
  true = Success
  false = Failure
  Remarks:
  The runs in the text are modified to store the string result of evaluating any fields
  in the run m_string or m_codepoints
  */
  static bool EvaluateField(ON_TextRun* run);

  /*
  Description:
    Returns the height in model units of the run, including text height and inter-line spacing
  */
  static double GetLinefeedHeight(ON_TextRun& run);


  const ON_Font* FirstCharFont() const;


  // Dimension text formatting
  static bool FormatDistance(
    double distance,
    ON::LengthUnitSystem units_in,
    const ON_DimStyle* dimstyle,
    bool alternate,                     // Primary or alternate
    ON_wString& formatted_string);      // Output

  static bool FormatTolerance(
    double distance,
    ON::LengthUnitSystem units_in,
    const ON_DimStyle* dimstyle,
    bool alternate,                     // Primary or alternate
    ON_wString& formatted_string);      // Output

  static bool FormatDistanceAndTolerance(
    double distance,
    ON::LengthUnitSystem units_in,
    const ON_DimStyle* dimstyle,
    bool alternate,                     // Primary or alternate
    ON_wString& formatted_string);      // Output

  static bool FormatDistanceMeasurement(
    double distance_in,
    ON::LengthUnitSystem units_in,
    const ON_DimStyle* dimstyle,
    const wchar_t* user_text,           // Replace "<>" in user_text with formatted dimension
    ON_wString& formatted_string);      // Output

  static bool FormatAngleMeasurement(
    double angle,
    const ON_DimStyle* dimstyle,  // Angle format comes from dimstyle
    const wchar_t* user_text,
    ON_wString& formatted_string);

};


#endif
