// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2010 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WPDF_IMAGE_H_
#define WPDF_IMAGE_H_

#include <Wt/WPaintDevice>
#include <Wt/WResource>

#include <boost/filesystem/path.hpp>

/*
 * Forwards for libharu types
 */
struct _HPDF_Dict_Rec;
struct _HPDF_Doc_Rec;
typedef _HPDF_Dict_Rec *HPDF_Page;
typedef _HPDF_Dict_Rec *HPDF_Font;
typedef _HPDF_Doc_Rec *HPDF_Doc;
typedef float HPDF_REAL;
typedef long unsigned HPDF_STATUS;

namespace Wt {

  class WTransform;

/*! \class WPdfImage Wt/WPdfImage Wt/WPdfImage
 *  \brief A paint device for rendering to a PDF.
 *
 * A %WPdfImage paint device should be used in conjunction with a
 * WPainter, and can be used to make a PDF version of a
 * WPaintedWidget's contents.
 *
 * The PDF is generated using <a href="http://libharu.org/">The Haru
 * Free PDF Library</a>, and this class is included in the library
 * only if <tt>libharu</tt> was found during the build of the library.
 *
 * You can use the image as a resource and specialize handleRequest()
 * to paint the contents on the fly. Alternatively can also use
 * write() to serialize to a PDF file (std::ostream).
 *
 * The latter usage is illustrated by the code below:
 * \code
 * Wt::Chart::WCartesianChart *chart = ...
 *
 * Wt::WPdfImage pdfImage(Wt::WLength(4, Wt::WLength::Centimeter), Wt::WLength(3, Wt::WLength::Centimeter));
 * {
 *   Wt::WPainter p(&pdfImage);
 *   chart->paint(p);
 * }
 * std::ofstream f("chart.pdf", std::ios::out | std::ios::binary);
 * pdfImage.write(f);
 * \endcode
 *
 * A constructor is provided which allows the generated PDF image to
 * be embedded directly into a page of a larger <tt>libharu</tt>
 * document.
 *
 * This paint device has the following limitations:
 * - images (WPainter::drawImage()) can only be included from local
 * files, and only JPG and PNG images are supported.
 * - drop shadows are not supported.
 *
 * \ingroup painting
 */
class WT_API WPdfImage : public WResource, public WPaintDevice
{
public:
  /*! \brief Create a PDF resource that represents a single-page PDF document.
   *
   * The single page will have a size \p width x \p height.  The PDF
   * will be using the same DPI (72dpi) as is conventionally used for
   * the web. Thus, you can use both physical units for width and
   * height (such as 4 cm by 3 cm), as well as pixel units.
   *
   * \sa write()
   */
  WPdfImage(const WLength& width, const WLength& height, WObject *parent = 0);

  /*! \brief Create a PDF paint device to paint inside an existing page.
   *
   * The image will be drawn in the existing page, as an image with lower-left
   * point (\p x, \p y) and size (\p width x \p height).
   */
  WPdfImage(HPDF_Doc pdf, HPDF_Page page,
	    HPDF_REAL x, HPDF_REAL y, HPDF_REAL width, HPDF_REAL height,
	    WObject *parent = 0);

  /*! \brief Destructor.
   */
  ~WPdfImage();

  /*! \brief Adds a font collection.
   *
   * The provided \p directory will be searched for fonts (currently
   * only TrueType ".ttf" fonts). TrueType fonts are preferable over
   * Base-14 fonts (which are PDF's default fonts) since they provide
   * partial (or complete) unicode support.
   *
   * When using Base-14 fonts, WString::narrow() will be called on text
   * which may result in loss of information.
   *
   * \sa matchFont()
   */
  void addFontCollection(const std::string& directory, bool recursive=true);

  virtual void setChanged(WFlags<ChangeFlag> flags);
  virtual void drawArc(const WRectF& rect, double startAngle, double spanAngle);
  virtual void drawImage(const WRectF& rect, const std::string& imgUri,
			 int imgWidth, int imgHeight, const WRectF& sourceRect);
  virtual void drawLine(double x1, double y1, double x2, double y2);
  virtual void drawPath(const WPainterPath& path);
  virtual void drawText(const WRectF& rect, 
			WFlags<AlignmentFlag> alignmentFlags, TextFlag textFlag,
			const WString& text);
  virtual WTextItem measureText(const WString& text, double maxWidth = -1,
				bool wordWrap = false);
  virtual WFontMetrics fontMetrics();
  virtual void init();
  virtual void done();
  virtual bool paintActive() const { return painter_ != 0; }

  virtual WLength width() const { return width_; }
  virtual WLength height() const { return height_; }

  void errorHandler(HPDF_STATUS error_no, HPDF_STATUS detail_no);

  virtual void handleRequest(const Http::Request& request,
			     Http::Response& response);

protected:
  virtual WPainter *painter() const { return painter_; }
  virtual void setPainter(WPainter *painter) { painter_ = painter; }

  /*! \brief Describes a font match.
   */
  class FontMatch {
  public:
    /*! \brief Creates a non-matching font.
     */
    FontMatch();

    /*! \brief Creates a match with a font in the given file name.
     */
    FontMatch(const std::string& fileName, double quality);

    bool matched() const { return quality_ > 0; }

    /*! \brief Returns the font file.
     */
    std::string fileName() const { return file_; }

    /*! \brief Returns the match quality.
     *
     * This is a number between 0 and 1, where 1 indicates a perfect match
     * and 0 indicates no match.
     */
    double quality() const { return quality_; }

  private:
    std::string file_;
    double quality_;
  };

  /*! \brief Locates a matching font.
   *
   * This method locates a match for the \p font in the given \p directory.
   *
   * The default implementation provides a rather simple font matching
   * algorithm.
   *
   * \sa addFontCollection()
   */
  virtual FontMatch matchFont(const WFont& font, const std::string& directory,
			      bool recursive) const;

private:
  struct FontCollection {
    std::string directory;
    bool recursive;
  };

  std::vector<FontCollection> fontCollections_;
  WLength width_, height_;
  WPainter *painter_;

  HPDF_Doc pdf_;
  HPDF_Page page_;
  HPDF_Font font_;
  CharEncoding fontEncoding_;

  bool myPdf_;
  double x_, y_;

  double fontSize_;

  void paintPath();
  void drawPlainPath(const WPainterPath& path);
  void applyTransform(const WTransform& f);

  void matchFont(const WFont& font,
		 const std::vector<std::string>& fontNames,
		 const boost::filesystem::path& path,
		 bool recursive,
		 FontMatch& match) const;
  void matchFont(const WFont& font,
		 const std::vector<std::string>& fontNames,
		 const boost::filesystem::path& path,
		 FontMatch& match) const;
};

}

#endif // WPDF_IMAGE_H_
