// @(#)root/gui:$Id$
// Author: Fons Rademakers   20/5/2003

/*************************************************************************
 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TGFont
#define ROOT_TGFont


#include "TNamed.h"
#include "TGObject.h"
#include "TRefCnt.h"

class THashTable;
class TObjString;
class TGFont;

// Flags passed to TGFont::MeasureChars and TGFont::ComputeTextLayout

enum ETextLayoutFlags {
   kTextWholeWords = BIT(0),
   kTextAtLeastOne = BIT(1),
   kTextPartialOK  = BIT(2),
   kTextIgnoreTabs = BIT(3),
   kTextIgnoreNewlines = BIT(4)
};

enum EFontWeight {
   kFontWeightNormal = 0,
   kFontWeightMedium = 0,
   kFontWeightBold = 1,
   kFontWeightLight = 2,
   kFontWeightDemibold = 3,
   kFontWeightBlack = 4,
   kFontWeightUnknown = -1
};

enum EFontSlant {
   kFontSlantRoman = 0,
   kFontSlantItalic = 1,
   kFontSlantOblique = 2,
   kFontSlantUnknown = -1
};


struct FontMetrics_t {
   Int_t   fAscent;          // from baseline to top of font
   Int_t   fDescent;         // from baseline to bottom of font
   Int_t   fLinespace;       // the sum of the ascent and descent
   Int_t   fMaxWidth;        // width of widest character in font
   Bool_t  fFixed;           // true if monospace, false otherwise
};


struct FontAttributes_t {

   const char *fFamily; // Font family. The most important field.
   Int_t fPointsize;    // Pointsize of font, 0 for default size, or negative number meaning pixel size.
   Int_t fWeight;       // Weight flag; see below for def'n.
   Int_t fSlant;        // Slant flag; see below for def'n.
   Int_t fUnderline;    // Non-zero for underline font.
   Int_t fOverstrike;   // Non-zero for overstrike font.

   FontAttributes_t():  // default constructor
      fFamily    (nullptr),
      fPointsize (0),
      fWeight    (kFontWeightNormal),
      fSlant     (kFontSlantRoman),
      fUnderline (0),
      fOverstrike(0) { }

   FontAttributes_t(const FontAttributes_t& f): // copy constructor
      fFamily    (f.fFamily),
      fPointsize (f.fPointsize),
      fWeight    (f.fWeight),
      fSlant     (f.fSlant),
      fUnderline (f.fUnderline),
      fOverstrike(f.fOverstrike) { }

   FontAttributes_t& operator=(const FontAttributes_t& f) // assignment operator
   {
      if (this != &f) {
         fFamily     = f.fFamily;
         fPointsize  = f.fPointsize;
         fWeight     = f.fWeight;
         fSlant      = f.fSlant;
         fUnderline  = f.fUnderline;
         fOverstrike = f.fOverstrike;
      }
      return *this;
   }

};



struct LayoutChunk_t;


class TGTextLayout : public TObject {

friend class TGFont;

protected:
   const TGFont  *fFont;         ///< The font used when laying out the text.
   const char    *fString;       ///< The string that was laid out.
   Int_t          fWidth;        ///< The maximum width of all lines in the text layout.
   Int_t          fNumChunks;    ///< Number of chunks actually used in following array.
   LayoutChunk_t *fChunks;       ///< Array of chunks. The actual size will be maxChunks.

   TGTextLayout(const TGTextLayout &tlayout) = delete;
   void operator=(const TGTextLayout &tlayout) = delete;

public:
   TGTextLayout(): fFont(nullptr), fString(""), fWidth(0), fNumChunks(0), fChunks(nullptr) {}
   ~TGTextLayout() override;

   void   DrawText(Drawable_t dst, GContext_t gc, Int_t x, Int_t y,
                   Int_t firstChar, Int_t lastChar) const;
   void   UnderlineChar(Drawable_t dst, GContext_t gc,
                        Int_t x, Int_t y, Int_t underline) const;
   Int_t  PointToChar(Int_t x, Int_t y) const;
   Int_t  CharBbox(Int_t index, Int_t *x, Int_t *y, Int_t *w, Int_t *h) const;
   Int_t  DistanceToText(Int_t x, Int_t y) const;
   Int_t  IntersectText(Int_t x, Int_t y, Int_t w, Int_t h) const;
   void   ToPostscript(TString *dst) const;

   ClassDefOverride(TGTextLayout,0)   // Keep track of string  measurement information.
};


// The following class is used to keep track of the generic information about a font.

class TGFont : public TNamed, public TRefCnt {

friend class TGFontPool;
friend class TGTextLayout;

private:
   FontStruct_t     fFontStruct;      ///< Low level graphics fontstruct
   FontH_t          fFontH;           ///< Font handle (derived from fontstruct)
   FontMetrics_t    fFM;              ///< Cached font metrics
   FontAttributes_t fFA;              ///< Actual font attributes obtained when the font was created
   TObjString      *fNamedHash;       ///< Pointer to the named object TGFont was based on
   Int_t            fTabWidth;        ///< Width of tabs in this font (pixels).
   Int_t            fUnderlinePos;    ///< Offset from baseline to origin of underline bar
                                      ///< (used for drawing underlines on a non-underlined font).
   Int_t            fUnderlineHeight; ///< Height of underline bar (used for drawing
                                      ///< underlines on a non-underlined font).
   char             fTypes[256];      ///< Array giving types of all characters in
                                      ///< the font, used when displaying control characters.
   Int_t            fWidths[256];     ///< Array giving widths of all possible characters in the font.
   Int_t            fBarHeight;       ///< Height of underline or overstrike bar
                                      ///< (used for simulating a native underlined or strikeout font).

protected:
   TGFont(const char *name)
     : TNamed(name,""), TRefCnt(), fFontStruct(0), fFontH(0), fFM(),
        fFA(), fNamedHash(nullptr), fTabWidth(0), fUnderlinePos(0), fUnderlineHeight(0), fBarHeight(0)
   {
      SetRefCount(1);
      for (Int_t i=0; i<256; i++) {
         fWidths[i] = 0;
         fTypes[i]  = ' ';
      }
   }

   TGFont(const TGFont &) = delete;
   void operator=(const TGFont &) = delete;

   LayoutChunk_t *NewChunk(TGTextLayout *layout, int *maxPtr,
                           const char *start, int numChars,
                           int curX, int newX, int y) const;
public:
   ~TGFont() override;

   FontH_t      GetFontHandle() const { return fFontH; }
   FontStruct_t GetFontStruct() const { return fFontStruct; }
   FontStruct_t operator()() const;
   void         GetFontMetrics(FontMetrics_t *m) const;
   FontAttributes_t GetFontAttributes() const { return fFA; }

   Int_t  PostscriptFontName(TString *dst) const;
   Int_t  TextWidth(const char *string, Int_t numChars = -1) const;
   Int_t  XTextWidth(const char *string, Int_t numChars = -1) const;
   Int_t  TextHeight() const { return fFM.fLinespace; }
   void   UnderlineChars(Drawable_t dst, GContext_t gc,
                        const char *string, Int_t x, Int_t y,
                        Int_t firstChar, Int_t lastChar) const;
   TGTextLayout *ComputeTextLayout(const char *string, Int_t numChars,
                                  Int_t wrapLength, Int_t justify, Int_t flags,
                                  UInt_t *width, UInt_t *height) const;
   Int_t  MeasureChars(const char *source, Int_t numChars, Int_t maxLength,
                      Int_t flags, Int_t *length) const;
   void   DrawCharsExp(Drawable_t dst, GContext_t gc, const char *source,
                      Int_t numChars, Int_t x, Int_t y) const;
   void   DrawChars(Drawable_t dst, GContext_t gc, const char *source,
                   Int_t numChars, Int_t x, Int_t y) const;

   void Print(Option_t *option="") const override;
   void SavePrimitive(std::ostream &out, Option_t * = "") override;

   ClassDefOverride(TGFont,0)   // GUI font description
};


struct FontStateMap_t;
struct XLFDAttributes_t;


class TGFontPool : public TGObject {

private:
   THashTable    *fList;       // TGFont objects pool
   THashTable    *fUidTable;   // Hash table for some used string values like family names, etc.
   THashTable    *fNamedTable; // Map a name to a set of attributes for a font

   TGFontPool(const TGFontPool& fp) = delete;
   TGFontPool& operator=(const TGFontPool& fp) = delete;

protected:
   const char *GetUid(const char *string);
   Bool_t      ParseXLFD(const char *string, XLFDAttributes_t *xa);
   TGFont     *GetFontFromAttributes(FontAttributes_t *fa, TGFont *fontPtr);
   int         FindStateNum(const FontStateMap_t *map, const char *strKey);
   const char *FindStateString(const FontStateMap_t *map, int numKey);
   Bool_t      FieldSpecified(const char *field);
   TGFont     *GetNativeFont(const char *name, Bool_t fixedDefault = kTRUE);
   TGFont     *MakeFont(TGFont *font, FontStruct_t fontStruct, const char *fontName);

public:
   TGFontPool(TGClient *client);
   ~TGFontPool() override;

   TGFont  *GetFont(const char *font, Bool_t fixedDefault = kTRUE);
   TGFont  *GetFont(const TGFont *font);
   TGFont  *GetFont(FontStruct_t font);
   TGFont  *GetFont(const char *family, Int_t ptsize, Int_t weight, Int_t slant);

   void     FreeFont(const TGFont *font);

   TGFont  *FindFont(FontStruct_t font) const;
   TGFont  *FindFontByHandle(FontH_t font) const;

   char   **GetAttributeInfo(const FontAttributes_t *fa);
   void     FreeAttributeInfo(char **info);
   char   **GetFontFamilies();
   void     FreeFontFamilies(char **f);
   Bool_t   ParseFontName(const char *string, FontAttributes_t *fa);
   const char *NameOfFont(TGFont *font);

   void     Print(Option_t *option="") const override;

   ClassDefOverride(TGFontPool,0)  // Font pool
};

#endif
