// Copyright (c) 2006,2007,2008,2009,2010,2011 Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.3/Arrangement_on_surface_2/include/CGAL/IO/Fig_stream.h $
// $Id: include/CGAL/IO/Fig_stream.h cefe3007d59 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Ron Wein           <wein@post.tau.ac.il>

#ifndef CGAL_FIG_STREAM_H
#define CGAL_FIG_STREAM_H

#include <CGAL/license/Arrangement_on_surface_2.h>


#include <CGAL/basic.h>
#include <CGAL/Polygon_2.h>

#include <vector>
#include <fstream>
#include <sstream>
#include <cstdio>

namespace CGAL {

/*!
 * FIG colors.
 */
enum Fig_color
{
  // Predefined colors:
  FIG_BLACK = 0,
  FIG_BLUE = 1,
  FIG_GREEN = 2,
  FIG_CYAN = 3,
  FIG_RED = 4,
  FIG_MAGENTA = 5,
  FIG_YELLOW = 6,
  FIG_WHITE = 7,
  FIG_BLUE_1 = 8, FIG_BLUE_2 = 9, FIG_BLUE_3 = 10, FIG_BLUE_4 = 11,
  FIG_GREEN_1 = 12, FIG_GREEN_2 = 13, FIG_GREEN_3 = 14,
  FIG_CYAN_1 = 15, FIG_CYAN_2 = 16, FIG_CYAN_3 = 17,
  FIG_RED_1 = 18, FIG_RED_2 = 19, FIG_RED_3 = 20,
  FIG_MAGENTA_1 = 21, FIG_MAGENTA_2 = 22, FIG_MAGENTA_3 = 23,
  FIG_BROWN_1 = 24, FIG_BROWN_2 = 25, FIG_BROWN_3 = 26,
  FIG_PINK_1 = 27, FIG_PINK_2 = 28, FIG_PINK_3 = 29, FIG_PINK_4 = 30,
  FIG_GOLD = 31,

  // User-defined colors:
  FIG_FIRST_USER_DEFINED_COLOR = 32,
  FIG_LAST_USER_DEFINED_COLOR = 543
};

/*!
 * FIG line styles.
 */
enum Fig_line_style
{
  FIG_SOLID = 0,
  FIG_DASHED = 1,
  FIG_DOTTED = 2,
  FIG_DASH_DOTTED = 3,
  FIG_DASH_DOUBLE_DOTTED = 4,
  FIG_DASH_TRIPLE_DOTTED = 5
};

#define FIG_DEFAULT_STYLE_VALUE   4.0

/*!
 * FIG fill styles.
 */
enum Fig_fill_style
{
  FIG_NOT_FILLED = -1,
  FIG_FILL_BLACK = 0,
  /// Values from 1 to 19 are shades of the color from darker to lighter.
  FIG_FILLED = 20,
  /// Values from 21 to 39 are tints of the color from the color to white.
  FIG_FILL_WHITE = 40,
  FIG_LEFT_DIAG_30DEG = 41,
  FIG_RIGHT_DIAG_30DEG = 42,
  FIG_CROSS_DIAG_30DEG = 43,
  FIG_LEFT_DIAG_45DEG = 44,
  FIG_RIGHT_DIAG_45DEG = 45,
  FIG_CROSS_DIAG_45DEG = 46,
  FIG_HORIZONTAL_BRICKS = 47,
  FIG_VERTICAL_BRICKS = 48,
  FIG_HORIZONTAL_LINES = 49,
  FIG_VERTICAL_LINES = 50,
  FIG_CROSS_LINES = 51,
  FIG_HORIZONTAL_RIGHT_SHINGLES = 52,
  FIG_HORIZONTAL_LEFT_SHINGLES = 53,
  FIG_VERTICAL_RIGHT_SHINGLES = 54,
  FIG_VERTICAL_LEFT_SHINGLES = 55,
  FIG_FISH_SCALES = 56,
  FIG_SMALL_FISH_SCALES = 57,
  FIG_CIRCLES = 58,
  FIG_HEXAGONS = 59,
  FIG_OCTAGONS = 60,
  FIG_HORIZONTAL_TIRE_TREADS = 61,
  FIG_VERTICAL_TIRE_TREADS = 62
};

/*!
 * FIG arrow types.
 */
enum Fig_arrow_type
{
  FIG_STICK = 0,
  FIG_TRIANGLE = 1,
  FIG_INDENTED_BUTT = 2,
  FIG_POINTED_BUTT = 3
};

/*!
 * Arrow modes (not based on the FIG format).
 */
enum Fig_arrow_mode
{
  FIG_NO_ARROW,
  FIG_FORWARD_ARROW,
  FIG_BACKWARD_ARROW,
  FIG_BOTH_ARROWS
};

/*!
 * Point styles (not based on the FIG format).
 */
enum Fig_point_style
{
  FIG_CROSS,
  FIG_PLUS,
  FIG_CIRCLE,
  FIG_DISC,
  FIG_SQUARE,
  FIG_BOX,
  FIG_RHOMBUS,
  FIG_DIAMOND
};

/*!
 * FIG fonts.
 */
enum Fig_font
{
  FIG_ROMAN = 1,
  FIG_BOLD = 2,
  FIG_ITALIC = 3,
  FIG_SANS_SERIF = 4,
  FIG_TYPEWRITER = 5
};

/*!
 * Depth constants.
 */
enum Fig_depth
{
  FIG_MIN_DEPTH = 0,
  FIG_DEFAULT_DEPTH = 50,
  FIG_MAX_DEPTH = 99
};

/*!
 * \class A class for writing geometric objects in a FIG format (version 3.2).
 * For more details, see: https://mcj.sourceforge.net/
 */
template <class Kernel_>
class Fig_stream
{
public:

  typedef Kernel_                                Kernel;

  // Define the kernel objects.
  typedef typename Kernel::FT                    NT;
  typedef typename Kernel::Point_2               Point_2;
  typedef typename Kernel::Segment_2             Segment_2;
  typedef typename Kernel::Ray_2                 Ray_2;
  typedef typename Kernel::Line_2                Line_2;
  typedef typename Kernel::Triangle_2            Triangle_2;
  typedef typename Kernel::Iso_rectangle_2       Iso_rectangle_2;
  typedef Polygon_2<Kernel>                      Polygon_2;
  typedef typename Kernel::Circle_2              Circle_2;

protected:

  // Data members:
  std::ofstream           _ofile;       // The output file.

  Iso_rectangle_2         _bound_rect;  // A rectangle bounding the workspace.
  int                     _width;       // Figure width (in pixels).
  int                     _height;      // Figure height (in pixels).
  double                  _scale;       // Scaling factor.

  int                     _depth;

  Fig_color               _color;
  int                     _line_width;
  Fig_line_style          _line_style;
  double                  _style_value;

  Fig_color               _fill_color;
  Fig_fill_style          _fill_style;

  Fig_point_style         _point_style;
  NT                      _point_size;

  Fig_arrow_mode          _arrow_mode;
  Fig_arrow_type          _arrow_type;
  NT                      _arrow_width;
  NT                      _arrow_height;

  Fig_font                _font;
  int                     _font_size;

  bool                    colors[FIG_LAST_USER_DEFINED_COLOR + 1];

  // Kernel functors.
  typename Kernel::Intersect_2       intersect_func;

private:

  // Copy constructor and assignment operator - not supported.
  Fig_stream (const Fig_stream<Kernel>& );
  const Fig_stream<Kernel>& operator= (const Fig_stream<Kernel>& );

public:

  /// \name Constructors and destructor.
  //@{

  /*!
   * Default constructor.
   */
  Fig_stream () :
    _width (0),
    _height (0),
    _scale (0),
    _depth (FIG_DEFAULT_DEPTH),
    _color (FIG_BLACK),
    _line_width (1),
    _line_style (FIG_SOLID),
    _style_value (FIG_DEFAULT_STYLE_VALUE),
    _fill_color (FIG_WHITE),
    _fill_style (FIG_NOT_FILLED),
    _point_style (FIG_DISC),
    _arrow_mode (FIG_NO_ARROW),
    _arrow_type (FIG_STICK),
    _font (FIG_ROMAN),
    _font_size (12)
  {
    // Reset all colors.
    _reset_colors ();

    // Construct the necessary kernel functors.
    Kernel        ker;

    intersect_func = ker.intersect_2_object();
  }

  /*!
   * Constructor.
   * \param filename The name of the output FIG file.
   * \param rect A rectangle bounding the logical drawing area.
   * \param width The physical width of the figure (in FIG units).
   * \param height The physical height of the figure (in FIG units).
   * \pre The bounding rectangle is valid and the physical dimensions are
   *      both positive.
   */
  Fig_stream (const char *filename,
              const Iso_rectangle_2& rect,
              const int& width = 12000,
              const int& height = 12000) :
    _width (0),
    _height (0),
    _scale (0),
    _depth (FIG_DEFAULT_DEPTH),
    _color (FIG_BLACK),
    _line_width (1),
    _line_style (FIG_SOLID),
    _style_value (FIG_DEFAULT_STYLE_VALUE),
    _fill_color (FIG_WHITE),
    _fill_style (FIG_NOT_FILLED),
    _point_style (FIG_DISC),
    _arrow_mode (FIG_NO_ARROW),
    _arrow_type (FIG_STICK),
    _font (FIG_ROMAN),
    _font_size (12)
  {
    // Reset all colors.
    _reset_colors ();

    // Construct the necessary kernel functors.
    Kernel        ker;

    intersect_func = ker.intersect_2_object();

    // Open the file.
    open (filename, rect, width, height);
  }

  /*!
   * Destructor.
   */
  virtual ~Fig_stream ()
  {
    _ofile.close();
  }
  //@}

  /// \name Opening and closing the file.
  //@{

  /*!
   * Check whether the file is open.
   */
  bool is_open ()
  {
    return (_ofile.is_open());
  }

  /*!
   * Open a FIG file.
   * \param filename The name of the output FIG file.
   * \param rect A rectangle bounding the logical drawing area.
   * \param width The physical width of the figure (in FIG units).
   * \param height The physical height of the figure (in FIG units).
   * \pre The bounding rectangle is valid and the physical dimensions are
   *      both positive.
   * \return Whether the file was successfully opened.
   */
  bool open (const char *filename,
             const Iso_rectangle_2& rect,
             const int& width = 12000,
             const int& height = 12000)
  {
    CGAL_precondition (width > 0);
    CGAL_precondition (height > 0);
    CGAL_precondition (rect.xmax() > rect.xmin());
    CGAL_precondition (rect.ymax() > rect.ymin());

    // Reset all colors.
    _reset_colors ();

    // Close the current output file, if necessary.
    if (_ofile.is_open())
      _ofile.close();

    // Open the output file.
    _ofile.open (filename);

    // Set the logical and physical dimensions.
    _bound_rect = rect;
    _width = width;
    _height = height;

    // Compute the scale.
    const double x_scale = width / CGAL::to_double(rect.xmax() - rect.xmin());
    const double y_scale = height / CGAL::to_double(rect.ymax() - rect.ymin());
    _scale = (x_scale < y_scale) ? x_scale : y_scale;

    // Set the default point size and arrow dimensions.
    _point_size = (rect.xmax() - rect.xmin()) / NT(500);
    _arrow_width = _point_size;
    _arrow_height = 2*_point_size;

    // End here if the file is not opened.
    if (! _ofile.is_open())
      return (false);

    // Write the FIG header.
    _ofile << "#FIG 3.2" << std::endl;
    _ofile << "Landscape" << std::endl;
    _ofile << "Center" << std::endl;
    _ofile << "Inches" << std::endl;
    _ofile << "Letter" << std::endl;
    _ofile << "100.00" << std::endl;
    _ofile << "Single" << std::endl;
    _ofile << "-2" << std::endl;
    _ofile << "1200 2" << std::endl;

    return (true);
  }

  /*!
   * Close the FIG file.
   */
  void close ()
  {
    if (_ofile.is_open())
      _ofile.close();

    // Reset all colors.
    _reset_colors ();
  }
  //@}

  /// \name Accessing drawing properties.
  //@{

  /*!
   * Get the workspace bounding rectangle.
   */
  Iso_rectangle_2 bounding_rect() const
  {
      return (_bound_rect);
  }

/*   /\*! */
/*    * Set the workspace bounding rectangle. */
/*    *\/ */
/*   void set_bounding_rect(const Iso_rectangle_2& rect) */
/*   { */
/*       _bound_rect = rect; */
/*   } */


  /*!
   * Get the physical width of the fig
   */
  int width () const
  {
    return _width;
  }

  /*!
   * Get the physical height of the fig
   */
  int height () const
  {
    return _height;
  }

  /*!
   * Get the depth.
   */
  int depth () const
  {
    return (depth);
  }

  /*!
   * Get the color.
   */
  Fig_color color () const
  {
    return (_color);
  }

  /*!
   * Get the line width.
   */
  int line_width () const
  {
    return (line_width);
  }

  /*!
   * Get the line style.
   */
  Fig_line_style line_style () const
  {
    return (_line_style);
  }

  /*!
   * Get the style value.
   */
  double style_value () const
  {
    return (_style_value);
  }

  /*!
   * Get the fill color.
   */
  Fig_color fill_color () const
  {
    return (_fill_color);
  }

  /*!
   * Get the fill style.
   */
  Fig_fill_style fill_style () const
  {
    return (_fill_style);
  }

  /*!
   * Get the point style.
   */
  Fig_point_style point_style () const
  {
    return (_point_style);
  }

  /*!
   * Get the point size.
   */
  const NT& point_size () const
  {
    return (_point_size);
  }

  /*!
   * Get the arrow drawing mode (this mode is relevant when drawing segments,
   * polylines, circular arcs or splines).
   */
  Fig_arrow_mode arrow_mode () const
  {
    return (_arrow_mode);
  }

  /*!
   * Get the arrow type.
   */
  Fig_arrow_type arrow_type () const
  {
    return (_arrow_type);
  }

  /*!
   * Get the arrow width.
   */
  const NT& arrow_width () const
  {
    return (_arrow_width);
  }

  /*!
   * Get the arrow height.
   */
  const NT& arrow_height () const
  {
    return (_arrow_height);
  }

  /*!
   * Get the font.
   */
  Fig_font font () const
  {
    return (_font);
  }

  /*!
   * Get the font size.
   */
  int font_size () const
  {
    return (_font_size);
  }
  //@}

  /// \name Set the drawing properties.
  //@{

  /*!
   * Set the depth.
   */
  void set_depth (const int& depth)
  {
    if (depth < static_cast<int>(FIG_MIN_DEPTH))
      _depth = static_cast<int>(FIG_MIN_DEPTH);
    else if (depth > static_cast<int>(FIG_MAX_DEPTH))
      _depth = static_cast<int>(FIG_MAX_DEPTH);
    else
      _depth = depth;

    return;
  }

  /*!
   * Set the color.
   * \pre The color must be defined.
   */
  void set_color (const Fig_color& color)
  {
    CGAL_precondition (color_defined (color));

    if (color_defined (color))
        _color = color;

    return;
  }

  /*!
   * Set the line width.
   */
  void set_line_width (const unsigned int& width)
  {
    _line_width = static_cast<int>(width);
    return;
  }

  /*!
   * Set the line style.
   */
  void set_line_style (const Fig_line_style& style)
  {
    _line_style = style;
    return;
  }

  /*!
   * Set the style value.
   */
  void set_style_value (const double& val)
  {
    CGAL_precondition (val > 0);

    _style_value = val;
    return;
  }

  /*!
   * Set the fill color.
   * \pre The color must be defined.
   */
  void set_fill_color (const Fig_color& color)
  {
    CGAL_precondition (color_defined (color));

    if (color_defined (color))
        _fill_color = color;

    return;
  }

  /*!
   * Set the fill style.
   */
  void set_fill_style (const Fig_fill_style& style)
  {
    _fill_style = style;
    return;
  }

  /*!
   * Set the point style.
   */
  void set_point_style (const Fig_point_style& style)
  {
    _point_style = style;
    return;
  }

  /*!
   * Set the point size.
   */
  void set_point_size (const NT& size)
  {
    _point_size = CGAL::abs(size);
    return;
  }

  /*!
   * Set the arrow drawing mode. This mode will be applied when drawing
   * segments, polylines, circular arcs or splines.
   */
  void set_arrow_mode (const Fig_arrow_mode& mode)
  {
    _arrow_mode = mode;
    return;
  }

  /*!
   * Set the arrow type.
   */
  void set_arrow_type (const Fig_arrow_type& type)
  {
    _arrow_type = type;
    return;
  }

  /*!
   * Set the arrow width.
   */
  void set_arrow_width (const NT& width)
  {
    _arrow_width = CGAL::abs(width);
    return;
  }

  /*!
   * Get the arrow height.
   */
  void set_arrow_height (const NT& height)
  {
    _arrow_height = CGAL::abs(height);
    return;
  }

  /*!
   * Set the font.
   */
  void set_font (const Fig_font& font)
  {
    _font = font;
    return;
  }

  /*!
   * Set the font size.
   */
  void set_font_size (const unsigned int& size)
  {
    _font_size = static_cast<int>(size);
    return;
  }
  //@}

  /// \name Defining colors.
  //@{

  /*!
   * Check if a color is defined.
   */
  bool color_defined (const Fig_color& color) const
  {
    int     col = static_cast<int>(color);

    if (col < 0 || col > FIG_LAST_USER_DEFINED_COLOR)
        return (false);

    return (colors[col]);
  }

  /*!
   * Add a user-defined color.
   * Use this function after opening the FIG stream and before writing any
   * other object (i.e. before calling the write_<object> () functions).
   * \param color The color.
   * \param r The red component (0 - 255).
   * \param g The green component (0 - 255).
   * \param b The blue component (0 - 255).
   * \pre The color must be undefined.
   */
  void define_color (const Fig_color& color,
                     const unsigned char& r,
                     const unsigned char& g,
                     const unsigned char& b)
  {
    CGAL_precondition (color_defined (color));
    CGAL_precondition (_ofile.is_open());

    if (color_defined (color))
      return;

    // Prepare a string describing the color.

    std::stringstream out;
    out << "0x"  << std::hex
        << std::setfill('0') << std::setw(2) << r
        << std::setfill('0') << std::setw(2) << g
        << std::setfill('0') << std::setw(2) << b;

    // Write the color to the FIG file.
    _ofile << "0 "                        // Desginates a color pseudo-object.
           << static_cast<int>(color) << ' '
           << out.str() << std::endl;

    // Mark that the color is now defined.
    colors[static_cast<int>(color)] = true;

    return;
  }

  //@}

  /// \name Writing objects.
  //@{

  /*!
   * Write a point.
   */
  void write_point (const Point_2& p)
  {
    CGAL_precondition (_ofile.is_open());

    //is the point outside the iso-rectangle?
    if(_bound_rect.has_on_unbounded_side(p))
      return;

    switch (_point_style)
    {
    case (FIG_CROSS):
    case (FIG_PLUS):
    {
      // Draw two segments intersecting at p.
      Point_2  s1, t1;
      Point_2  s2, t2;

      if (_point_style == FIG_PLUS)
      {
        // Draw a '+'.
        s1 = Point_2 (p.x() - _point_size, p.y());
        t1 = Point_2 (p.x() + _point_size, p.y());
        s2 = Point_2 (p.x(), p.y() - _point_size);
        t2 = Point_2 (p.x(), p.y() + _point_size);
      }
      else
      {
        // Draw an 'x'.
        s1 = Point_2 (p.x() - _point_size, p.y() - _point_size);
        t1 = Point_2 (p.x() + _point_size, p.y() + _point_size);
        s2 = Point_2 (p.x() - _point_size, p.y() + _point_size);
        t2 = Point_2 (p.x() + _point_size, p.y() - _point_size);
      }

      // Draw solid lines with width 1.
      _write_segment (Segment_2(s1, t1),
                      _color, 1, FIG_SOLID, _style_value,
                      false);
      _write_segment (Segment_2(s2, t2),
                      _color, 1, FIG_SOLID, _style_value,
                      false);

      break;
    }

    case (FIG_CIRCLE):
    {
      // Draw an empty circle (use a solid line with width 1).
      _write_ellipse (p,
                      CGAL::square(_point_size),
                      CGAL::square(_point_size),
                      _color, 1, FIG_SOLID, _style_value,
                      FIG_WHITE, FIG_NOT_FILLED);

      break;
    }

    case (FIG_DISC):
    {
      // Draw a filled disc.
      _write_ellipse (p,
                      CGAL::square(_point_size),
                      CGAL::square(_point_size),
                      _color, 1, FIG_SOLID, _style_value,
                      _color, FIG_FILLED);

      break;
    }

    case (FIG_SQUARE):
    case (FIG_BOX):
    case (FIG_RHOMBUS):
    case (FIG_DIAMOND):
    {
      // Prepare the rectangle vertices.
      std::vector<Point_2>   vertices (4);

      if (_point_style == FIG_SQUARE || _point_style == FIG_BOX)
      {
        vertices[0] = Point_2 (p.x() - _point_size, p.y() - _point_size);
        vertices[1] = Point_2 (p.x() - _point_size, p.y() + _point_size);
        vertices[2] = Point_2 (p.x() + _point_size, p.y() + _point_size);
        vertices[3] = Point_2 (p.x() + _point_size, p.y() - _point_size);
      }
      else
      {
        vertices[0] = Point_2 (p.x(), p.y() - _point_size);
        vertices[1] = Point_2 (p.x() + _point_size, p.y());
        vertices[2] = Point_2 (p.x(), p.y() + _point_size);
        vertices[3] = Point_2 (p.x() - _point_size, p.y());
      }

      if (_point_style == FIG_SQUARE || _point_style == FIG_RHOMBUS)
      {
        // Draw an empty rectangular shape (use a solid line with width 1).
        _write_polygon (4, vertices.begin(), vertices.end(),
                        _color, 1, FIG_SOLID, _style_value,
                        FIG_WHITE, FIG_NOT_FILLED);
      }
      else
      {
        // Draw a filled rectangular shape.
        _write_polygon (4, vertices.begin(), vertices.end(),
                        _color, 1, FIG_SOLID, _style_value,
                        _color, FIG_FILLED);
      }

      break;
      }
    }

    return;
  }

  /*!
   * Write a segment.
   */
  void write_segment (const Segment_2& seg)
  {
    CGAL_precondition (_ofile.is_open());

    // Clip the ray using the bounding rectangle.
    CGAL::Object    obj = intersect_func (_bound_rect, seg);
    Segment_2       clipped_seg;

    // Draw only the clipped segment (draw nothing if the segment does not
    // intersect the bounding rectangle).
    if (CGAL::assign (clipped_seg, obj))
    {
      _write_segment (clipped_seg,
                      _color, _line_width, _line_style, _style_value,
                      false);
    }

    return;
  }

  /*!
   * Write a ray.
   */
  void write_ray (const Ray_2& ray)
  {
    CGAL_precondition (_ofile.is_open());

    // Clip the ray using the bounding rectangle.
    CGAL::Object    obj = intersect_func (_bound_rect, ray);
    Segment_2       seg;

    // Draw only the clipped segment (draw nothing if the ray does not
    // intersect the bounding rectangle).
    if (CGAL::assign (seg, obj))
    {
      _write_segment (seg,
                      _color, _line_width, _line_style, _style_value,
                      false);
    }

    return;
  }

  /*!
   * Write a line.
   */
  void write_line (const Line_2& line)
  {
    CGAL_precondition (_ofile.is_open());

    // Clip the ray using the bounding rectangle.
    CGAL::Object    obj = intersect_func (_bound_rect, line);
    Segment_2       seg;

    // Draw only the clipped segment (draw nothing if the ray does not
    // intersect the bounding rectangle).
    if (CGAL::assign (seg, obj))
    {
      _write_segment (seg,
                      _color, _line_width, _line_style, _style_value,
                      false);
    }

    return;
  }

  /*!
   * Write a triangle.
   */
  void write_triangle (const Triangle_2& tri)
  {
    CGAL_precondition (_ofile.is_open());

    std::vector<Point_2>   vertices(3);

    vertices[0] = tri.vertex(0);
    vertices[1] = tri.vertex(1);
    vertices[2] = tri.vertex(2);

    _write_polygon (3, vertices.begin(), vertices.end(),
                    _color, _line_width, _line_style, _style_value,
                    _fill_color, _fill_style);
    return;
  }

  /*!
   * Write an iso-rectangle.
   */
  void write_rectangle (const Iso_rectangle_2& rect)
  {
    CGAL_precondition (_ofile.is_open());

    std::vector<Point_2>   vertices(4);

    vertices[0] = rect.vertex(0);
    vertices[1] = rect.vertex(1);
    vertices[2] = rect.vertex(2);
    vertices[3] = rect.vertex(3);

    _write_polygon (4, vertices.begin(), vertices.end(),
                    _color, _line_width, _line_style, _style_value,
                    _fill_color, _fill_style);
    return;
  }

  /*!
   * Write a polyline.
   * \param begin An iterator of the control points (of type Point_2).
   * \param end A past-the-end iterator for the control points.
   */
  template <class Input_iterator>
  void write_polyline (const Input_iterator& begin, const Input_iterator& end)
  {
    CGAL_precondition (_ofile.is_open());

    _write_polyline (begin, end,
                     _color, _line_width, _line_style, _style_value, false);
    return;
  }

  /*!
   * Write a polygon.
   */
  void write_polygon (const Polygon_2& pgn)
  {
    CGAL_precondition (_ofile.is_open());

    _write_polygon (pgn.size(),
                    pgn.vertices_begin(), pgn.vertices_end(),
                    _color, _line_width, _line_style, _style_value,
                    _fill_color, _fill_style);
    return;
  }

  /*!
   * Write a circle.
   */
  void write_circle (const Circle_2& circ)
  {
    CGAL_precondition (_ofile.is_open());

    _write_ellipse (circ.center(),
                    circ.squared_radius(), circ.squared_radius(),
                    _color, _line_width, _line_style, _style_value,
                    _fill_color, _fill_style);
    return;
  }

  /*!
   * Write a canonical ellipse.
   * \param center The center of the ellipse (the intersection of its axes).
   * \param r1_squared The squared length of the axis parallel to the x-axis.
   * \param r2_squared The squared length of the axis parallel to the y-axis.
   */
  void write_ellipse (const Point_2& center,
                      const NT& r1_squared, const NT& r2_squared)
  {
    CGAL_precondition (_ofile.is_open());

    _write_ellipse (center,
                    r1_squared, r2_squared,
                    _color, _line_width, _line_style, _style_value,
                    _fill_color, _fill_style);
    return;
  }

  /*!
   * Write a circular arc.
   * \param p1 The source point of the arc.
   * \param p2 A midpoint on the arc.
   * \param p3 The target point of the arc.
   * \pre The three points are not collinear.
   */
  void write_circular_arc (const Point_2& p1,
                           const Point_2& p2,
                           const Point_2& p3)
  {
    CGAL_precondition (_ofile.is_open());

    _write_arc (p1, p2, p3,
                _color, _line_width, _line_style, _style_value);
    return;
  }

  /*!
   * Write a spline.
   * \param begin An iterator of the control points (of type Point_2).
   * \param end A past-the-end iterator for the control points.
   * \param factor A shape factor for the spline: A value in the range [-1,1],
   *               where negative values are used for interpolated splines
   *               and positive values for approximated splines.
   *               The default value if 1.
   */
  template <class Input_iterator>
  void write_spline (const Input_iterator& begin, const Input_iterator& end,
                     const float& factor = 1)
  {
    CGAL_precondition (_ofile.is_open());

    if (begin == end)
      return;

    // Normalize the shape factor.
    float shape_factor;

    if (factor > 1)
      shape_factor = 1;
    else if (factor < -1)
      shape_factor = -1;
    else
      shape_factor = factor;

    _write_spline (begin, end, shape_factor,
                   _color, _line_width, _line_style, _style_value);
  }

  /*!
   * Write a text box.
   * \param pos The lower-left corner of the text box.
   * \param text The text to write.
   * \param angle The angle (in radians) that the text forms with the x-axis
   *              (0 by default).
   */
  void write_text (const Point_2& pos,
                   const char *text,
                   const double& angle = 0)
  {
    CGAL_precondition (_ofile.is_open());

    if (text == nullptr || strlen(text) == 0)
      return;

    _write_text (pos,
                 reinterpret_cast<const unsigned char*>(text), strlen(text),
                 angle,
                 _color, _font, _font_size);
    return;
  }
  //@}

  /// \name Setting the draw properties via the << operator.
  //@{

  /*!
   * Set the depth.
   */
  Fig_stream& operator<< (const Fig_depth& depth)
  {
    set_depth (static_cast<int>(depth));
    return (*this);
  }

  /*!
   * Set the color.
   */
  Fig_stream& operator<< (const Fig_color& color)
  {
    set_color (color);
    return (*this);
  }

  /*!
   * Set the line style.
   */
  Fig_stream& operator<< (const Fig_line_style& style)
  {
    set_line_style (style);
    return (*this);
  }

  /*!
   * Set the fill style.
   */
  Fig_stream& operator<< (const Fig_fill_style& style)
  {
    set_fill_style (style);
    return (*this);
  }

  /*!
   * Set the point style.
   */
  Fig_stream& operator<< (const Fig_point_style& style)
  {
    set_point_style (style);
    return (*this);
  }

  /*!
   * Set the arrow drawing mode. This mode will be applied when drawing
   * segments, polylines, circular arcs or splines.
   */
  Fig_stream& operator<< (const Fig_arrow_mode& mode)
  {
    set_arrow_mode (mode);
    return (*this);
  }

  /*!
   * Set the arrow type.
   */
  Fig_stream& operator<< (const Fig_arrow_type& type)
  {
    set_arrow_type (type);
    return (*this);
  }

  /*!
   * Set the font.
   */
  Fig_stream& operator<< (const Fig_font& font)
  {
    set_font (font);
    return (*this);
  }
  //@}

  /// \name Drawing objects via the << operator.
  //@{

  /*!
   * Write a point.
   */
  Fig_stream& operator<< (const Point_2& p)
  {
    write_point (p);
    return (*this);
  }

  /*!
   * Write a line segment.
   */
  Fig_stream& operator<< (const Segment_2& seg)
  {
    write_segment (seg);
    return (*this);
  }

  /*!
   * Write a ray.
   */
  Fig_stream& operator<< (const Ray_2& ray)
  {
    write_ray (ray);
    return (*this);
  }

  /*!
   * Write a line.
   */
  Fig_stream& operator<< (const Line_2& line)
  {
    write_line (line);
    return (*this);
  }

  /*!
   * Write a triangle.
   */
  Fig_stream& operator<< (const Triangle_2& tri)
  {
    write_triangle (tri);
    return (*this);
  }

  /*!
   * Write a rectangle.
   */
  Fig_stream& operator<< (const Iso_rectangle_2& rect)
  {
    write_rectangle (rect);
    return (*this);
  }

  /*!
   * Write a polygon.
   */
  Fig_stream& operator<< (const Polygon_2& pgn)
  {
    write_polygon (pgn);
    return (*this);
  }

  /*!
   * Write a circle.
   */
  Fig_stream& operator<< (const Circle_2& circ)
  {
    write_circle (circ);
    return (*this);
  }
  //@}

protected:

  /*!
   * Convert a point to FIG units.
   */
  void _convert_point (const Point_2& p,
                       int& ix, int& iy) const
  {
    ix = static_cast<int> (_scale *
                           CGAL::to_double(p.x() - _bound_rect.xmin()));
    iy = static_cast<int> (_scale *
                           CGAL::to_double( _bound_rect.ymax() - p.y()));
    return;
  }

  /*!
   * Write a segment.
   */
  void _write_segment (const Segment_2& seg,
                       const Fig_color&      line_color,
                       const int&            line_width,
                       const Fig_line_style& line_style,
                       const double&         style_value,
                       const bool&      draw_arrows)
  {
    // Convert the segment to a polyline with two points and write it.
    std::vector<Point_2>   points (2);

    points[0] = seg.source();
    points[1] = seg.target();

    _write_polyline (points.begin(), points.end(),
                     line_color, line_width, line_style, style_value,
                     draw_arrows);
    return;
  }

  /*!
   * Write a polyline.
   */
  template <class Input_iterator>
  void _write_polyline (Input_iterator begin, Input_iterator end,
                        const Fig_color&      line_color,
                        const int&            line_width,
                        const Fig_line_style& line_style,
                        const double&         style_value,
                        const bool&      draw_arrows)
  {
    // Check if we should draw arrows.
    bool    forward_arrow = false;
    bool    backward_arrow = false;

    if (draw_arrows)
    {
      forward_arrow = (_arrow_mode == FIG_FORWARD_ARROW) ||
                      (_arrow_mode == FIG_BOTH_ARROWS);
      backward_arrow = (_arrow_mode == FIG_BACKWARD_ARROW) ||
                       (_arrow_mode == FIG_BOTH_ARROWS);
    }

    // Count the number of points in the spline.
    int     n_points = std::distance (begin, end);

    // Write the segment properties.
    _ofile << "2 1 "                      // Desginate a polyline.
           << line_style << ' '
           << line_width << ' '
           << line_color << ' '
           << FIG_WHITE << ' '            // Fill color (dummy).
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << FIG_NOT_FILLED << ' '
           << style_value << ' '
           << "0 "                        // Join style (always 0).
           << "0 "                        // Cap style (always 0).
           << "-1 "                       // Radius (not in use for lines).
           << forward_arrow << ' '
           << backward_arrow << ' '
           << n_points << std::endl;

    // Write the points defining the polyline.
    bool             is_first = true;
    int              ix, iy;

    while (begin != end)
    {
      if (is_first)
      {
        _ofile << '\t';
        is_first = false;
      }
      else
      {
        _ofile << ' ';
      }

      _convert_point (*begin, ix, iy);
      _ofile << ix << ' ' << iy;

      begin++;
    }
    _ofile << std::endl;

    // Write the arrows, if necessary.
    if (forward_arrow)
        _write_arrow_line ();

    if (backward_arrow)
        _write_arrow_line ();

    return;
  }

  /*!
   * Write a polygon, represented as a range of points.
   */
  template <class Input_iterator>
  void _write_polygon (const int n_points,
                       Input_iterator begin, Input_iterator end,
                       const Fig_color&      line_color,
                       const int             line_width,
                       const Fig_line_style& line_style,
                       const double&         style_value,
                       const Fig_color&      fill_color,
                       const Fig_fill_style& fill_style)
  {
    // Write the polyline properties.
    _ofile << "2 3 "                      // Desginate a polygon.
           << line_style << ' '
           << line_width << ' '
           << line_color << ' '
           << fill_color << ' '
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << fill_style << ' '
           << style_value << ' '
           << "0 "                        // Join style (always 0).
           << "0 "                        // Cap style (always 0).
           << "-1 "                       // Radius (not in use for lines).
           << "0 "                        // No forward arrow.
           << "0 "                        // No backward arrow.
           << n_points + 1 << std::endl;

    // Write the points.
    bool             is_first = true;
    Point_2          first = *begin;
    int              ix, iy;

    while (begin != end)
    {
      if (is_first)
      {
        _ofile << '\t';
        is_first = false;
      }
      else
      {
        _ofile << ' ';
      }

      _convert_point (*begin, ix, iy);
      _ofile << ix << ' ' << iy;

      begin++;
    }

    // Write the first point again.
    _convert_point (first, ix, iy);
    _ofile << ' ' << ix << ' ' << iy << std::endl;

    return;
  }

  /*!
   * Write an ellipse.
   */
  void _write_ellipse (const Point_2& center,
                       const NT& squared_radius_x,
                       const NT& squared_radius_y,
                       const Fig_color&      line_color,
                       const int&            line_width,
                       const Fig_line_style& line_style,
                       const double&         style_value,
                       const Fig_color&      fill_color,
                       const Fig_fill_style& fill_style)
  {


    // Write the ellipse properties.
    _ofile << "1 1 "                      // Desginate an ellipse.
           << line_style << ' '
           << line_width << ' '
           << line_color << ' '
           << fill_color << ' '
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << fill_style << ' '
           << style_value << ' '
           << "1 "                        // Direction (always 1).
           << "0.000 ";                   // Angle (in radians).

    // Write the center point.
    int     ix, iy;

    _convert_point (center, ix, iy);
    _ofile << ' ' << ix << ' ' << iy;

    // Write the radii.
    int  rx = static_cast<int> (_scale *
                                std::sqrt(CGAL::to_double(squared_radius_x)));
    int  ry = static_cast<int> (_scale *
                                std::sqrt(CGAL::to_double(squared_radius_y)));

    _ofile << ' ' << rx << ' ' << ry;

    // Write the start point (the center) and the end point (one corner of
    // the rectangles bounding the ellipse).
    _ofile << ' ' << ix << ' ' << iy
           << ' ' << (ix + rx) << ' ' << (iy +ry) << std::endl;

    return;
  }

  /*!
   * Write an arc.
   */
  void _write_arc (const Point_2& p1, const Point_2& p2, const Point_2& p3,
                   const Fig_color&      line_color,
                   const int&            line_width,
                   const Fig_line_style& line_style,
                   const double&         style_value)
  {
    // Check if we should draw arrows.
    bool    forward_arrow;
    bool    backward_arrow;

    forward_arrow = (_arrow_mode == FIG_FORWARD_ARROW) ||
                  (_arrow_mode == FIG_BOTH_ARROWS);
    backward_arrow = (_arrow_mode == FIG_BACKWARD_ARROW) ||
                   (_arrow_mode == FIG_BOTH_ARROWS);

    // Construct the supporting circle of the arc and use its center and
    // orientation.
    Circle_2   circ (p1, p2, p3);
    int        orient = (circ.orientation() == CGAL::CLOCKWISE) ? 0 : 1;

    // Write the arc properties.
    _ofile << "5 1 "                      // Desginate an open arc.
           << line_style << ' '
           << line_width << ' '
           << line_color << ' '
           << FIG_WHITE << ' '            // Fill color (dummy).
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << FIG_NOT_FILLED << ' '
           << style_value << ' '
           << "0 "                        // Cap style (always 0).
           << orient << ' '
           << forward_arrow << ' '
           << backward_arrow << ' ';

    // Write the center of the circle.
    int     ix, iy;

    _convert_point (circ.center(), ix, iy);
    _ofile << ix << ' ' << iy;

    // Write the three points defining the arc.
    _convert_point (p1, ix, iy);
    _ofile << ' ' << ix << ' ' << iy;

    _convert_point (p2, ix, iy);
    _ofile << ' ' << ix << ' ' << iy;

    _convert_point (p3, ix, iy);
    _ofile << ' ' << ix << ' ' << iy << std::endl;

    // Write the arrows, if necessary.
    if (forward_arrow)
        _write_arrow_line ();

    if (backward_arrow)
        _write_arrow_line ();

    return;
  }

  /*!
   * Write a spline
   */
  template <class Input_iterator>
  void _write_spline (Input_iterator begin, Input_iterator end,
                      const float& factor,
                      const Fig_color&      line_color,
                      const int&            line_width,
                      const Fig_line_style& line_style,
                      const double&         style_value)
  {
    // Check if we should draw arrows.
    bool    forward_arrow;
    bool    backward_arrow;

    forward_arrow = (_arrow_mode == FIG_FORWARD_ARROW) ||
                  (_arrow_mode == FIG_BOTH_ARROWS);
    backward_arrow = (_arrow_mode == FIG_BACKWARD_ARROW) ||
                   (_arrow_mode == FIG_BOTH_ARROWS);

    // Count the number of points in the spline.
    int     n_points = std::distance (begin, end);

    // Write the spline properties.
    _ofile << "3 0 "                      // Desginate an open spline.
           << line_style << ' '
           << line_width << ' '
           << line_color << ' '
           << FIG_WHITE << ' '            // Fill color (dummy).
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << FIG_NOT_FILLED << ' '
           << style_value << ' '
           << "0 "                        // Cap style (always 0).
           << forward_arrow << ' '
           << backward_arrow << ' '
           << n_points << std::endl;

    // Write the points defining the spline.
    bool             is_first = true;
    int              ix, iy;

    while (begin != end)
    {
      if (is_first)
      {
        _ofile << '\t';
        is_first = false;
      }
      else
      {
        _ofile << ' ';
      }

      _convert_point (*begin, ix, iy);
      _ofile << ix << ' ' << iy;

      begin++;
    }
    _ofile << std::endl;

    // Write the shape factors: 0 for the endpoints and (factor) for each
    // of the midpoints.
    int     i;

    _ofile << '\t' << "0.000";
    for (i = 0; i < n_points - 2; i++)
      _ofile << ' ' << factor;
    _ofile << '\t' << "0.000" << std::endl;

    // Write the arrows, if necessary.
    if (forward_arrow)
        _write_arrow_line ();

    if (backward_arrow)
        _write_arrow_line ();

    return;
  }

  /*!
   * Write an arrow line.
   */
  void _write_arrow_line ()
  {
    int  width  = static_cast<int> (_scale * CGAL::to_double(_arrow_width));
    int  height = static_cast<int> (_scale * CGAL::to_double(_arrow_height));

    _ofile << _arrow_type << ' '
           << "0 "                        // Arrow style (always 0).
           << _line_width << ' '
           << width << ' '
           << height << std::endl;
    return;
  }

  /*!
   * Write a text box.
   */
  void _write_text (const Point_2& pos,
                    const unsigned char *text,
                    const int&          len_text,
                    const double& angle,
                    const Fig_color& font_color,
                    const Fig_font&  font,
                    const int&       font_size)
  {
    // Compute the text-box dimensions.
    const int    text_height = font_size * 1200 / 80;
    const int    text_width = len_text * font_size * 1200 / 160;

    // Write the text properties.
    _ofile << "4 0 "                      // Desginate left-justified text.
           << font_color << ' '
           << _depth << ' '
           << "0 "                        // Pen style (not in use, always 0).
           << font << ' '
           << font_size << ' '
           << angle << ' '
           << "2 "                        // Indicates a special LaTeX font.
           << text_height << ' '
           << text_width;

    // Write the position coordinates.
    int     ix, iy;

    _convert_point (pos, ix, iy);
    _ofile << ' ' << ix << ' ' << iy << ' ';

    // Write the text.
    int     i;

    for (i = 0; i < len_text; i++)
    {
      if (text[i] >= ' ' && text[i] < 128 && text[i] != '\\')
      {
        // If the current character is printable, just write it.
        _ofile << static_cast<char>(text[i]);
      }
      else
      {
        // Convert the current character to an octal string and write
        // it.
        std::stringstream out;
        out << "\\" << std::setfill('0') << std::setw(3) << std::oct << text[i];
        _ofile << out.str();
      }
    }

    // Write the end-of-string sequence.
    _ofile << "\\001" << std::endl;

    return;
  }

  /*!
   * Reset all user-defined colors.
   */
  void _reset_colors ()
  {
    int     i;

    for (i = 0; i < FIG_FIRST_USER_DEFINED_COLOR; i++)
        colors[i] = true;

    for (i = FIG_FIRST_USER_DEFINED_COLOR;
         i < FIG_LAST_USER_DEFINED_COLOR; i++)
    {
        colors[i] = false;
    }

    return;
  }
};

} //namespace CGAL

#endif
