/* Pango
 * pangocoretext.c
 *
 * Copyright (C) 2005-2007 Imendio AB
 * Copyright (C) 2010  Kristian Rietveld  <kris@gtk.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:coretext-fonts
 * @short_description:Font handling with CoreText fonts
 * @title:CoreText Fonts
 *
 * The macros and functions in this section are used to access fonts natively on
 * OS X using the CoreText text rendering subsystem.
 */
#include "config.h"

#include "pangocoretext.h"
#include "pangocoretext-private.h"

G_DEFINE_TYPE (PangoCoreTextFont, pango_core_text_font, PANGO_TYPE_FONT);

struct _PangoCoreTextFontPrivate
{
  PangoCoreTextFace *face;
  gpointer context_key;

  CTFontRef font_ref;
  PangoCoreTextFontKey *key;

  PangoCoverage *coverage;

  PangoFontMap *fontmap;
};

static void
pango_core_text_font_finalize (GObject *object)
{
  PangoCoreTextFont *ctfont = (PangoCoreTextFont *)object;
  PangoCoreTextFontPrivate *priv = ctfont->priv;
  PangoCoreTextFontMap* fontmap = g_weak_ref_get ((GWeakRef *)&priv->fontmap);
  if (fontmap)
    {
      g_weak_ref_clear ((GWeakRef *)&priv->fontmap);
      g_object_unref (fontmap);
    }

  if (priv->coverage)
    pango_coverage_unref (priv->coverage);

  G_OBJECT_CLASS (pango_core_text_font_parent_class)->finalize (object);
}

static PangoFontDescription *
pango_core_text_font_describe (PangoFont *font)
{
  PangoCoreTextFont *ctfont = (PangoCoreTextFont *)font;
  PangoCoreTextFontPrivate *priv = ctfont->priv;
  CTFontDescriptorRef ctfontdesc;

  ctfontdesc = pango_core_text_font_key_get_ctfontdescriptor (priv->key);

  return _pango_core_text_font_description_from_ct_font_descriptor (ctfontdesc);
}

static PangoCoverage *
ct_font_descriptor_get_coverage (CTFontDescriptorRef desc)
{
  CFCharacterSetRef charset;
  CFIndex i, length;
  CFDataRef bitmap;
  const UInt8 *ptr, *plane_ptr;
  const UInt32 plane_size = 8192;
  PangoCoverage *coverage;

  coverage = pango_coverage_new ();

  charset = CTFontDescriptorCopyAttribute (desc, kCTFontCharacterSetAttribute);
  if (!charset)
    /* Return an empty coverage */
    return coverage;

  bitmap = CFCharacterSetCreateBitmapRepresentation (kCFAllocatorDefault,
                                                     charset);
  ptr = CFDataGetBytePtr (bitmap);

  /* First handle the BMP plane. */
  length = MIN (CFDataGetLength (bitmap), plane_size);

  /* FIXME: can and should this be done more efficiently? */
  for (i = 0; i < length; i++)
    {
      int j;

      for (j = 0; j < 8; j++)
        if ((ptr[i] & (1 << j)) == (1 << j))
          pango_coverage_set (coverage, i * 8 + j, PANGO_COVERAGE_EXACT);
    }

  /* Next, handle the other planes. The plane number is encoded first as
   * a single byte. In the following 8192 bytes that plane's coverage bitmap
   * is stored.
   */
  plane_ptr = ptr + plane_size;
  while (plane_ptr - ptr < CFDataGetLength (bitmap))
    {
      const UInt8 plane_number = *plane_ptr;
      plane_ptr++;

      for (i = 0; i < plane_size; i++)
        {
          int j;

          for (j = 0; j < 8; j++)
            if ((plane_ptr[i] & (1 << j)) == (1 << j))
              pango_coverage_set (coverage, (plane_number * plane_size + i) * 8 + j,
                                  PANGO_COVERAGE_EXACT);
        }

      plane_ptr += plane_size;
    }

  CFRelease (bitmap);
  CFRelease (charset);

  return coverage;
}

static PangoCoverage *
pango_core_text_font_get_coverage (PangoFont     *font,
                                   PangoLanguage *language G_GNUC_UNUSED)
{
  PangoCoreTextFont *ctfont = (PangoCoreTextFont *)font;
  PangoCoreTextFontPrivate *priv = ctfont->priv;

  if (!priv->coverage)
    {
      CTFontDescriptorRef ctfontdesc;

      ctfontdesc = pango_core_text_font_key_get_ctfontdescriptor (priv->key);

      priv->coverage = ct_font_descriptor_get_coverage (ctfontdesc);
    }

  return pango_coverage_ref (priv->coverage);
}

/* Wrap shaper in PangoEngineShape to pass it through old API,
 * from times when there were modules and engines. */
typedef PangoEngineShape      PangoCoreTextShapeEngine;
typedef PangoEngineShapeClass PangoCoreTextShapeEngineClass;
static GType pango_core_text_shape_engine_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (PangoCoreTextShapeEngine, pango_core_text_shape_engine, PANGO_TYPE_ENGINE_SHAPE);
static void
_pango_core_text_shape_engine_shape (PangoEngineShape    *engine G_GNUC_UNUSED,
				 PangoFont           *font,
				 const char          *item_text,
				 unsigned int         item_length,
				 const PangoAnalysis *analysis,
				 PangoGlyphString    *glyphs,
				 const char          *paragraph_text,
				 unsigned int         paragraph_length)
{
  _pango_core_text_shape (font, item_text, item_length, analysis, glyphs,
		      paragraph_text, paragraph_length);
}
static void
pango_core_text_shape_engine_class_init (PangoEngineShapeClass *class)
{
  class->script_shape = _pango_core_text_shape_engine_shape;
}
static void
pango_core_text_shape_engine_init (PangoEngineShape *object)
{
}

static PangoEngineShape *
pango_core_text_font_find_shaper (PangoFont     *font,
                                  PangoLanguage *language G_GNUC_UNUSED,
                                  guint32        ch)
{
  static PangoEngineShape *shaper;
  if (g_once_init_enter (&shaper))
    g_once_init_leave (&shaper, g_object_new (pango_core_text_shape_engine_get_type(), NULL));
  return shaper;
}

static PangoFontMap *
pango_core_text_font_get_font_map (PangoFont *font)
{
  PangoCoreTextFont *ctfont = (PangoCoreTextFont *)font;
  /* FIXME: Not thread safe! */
  return ctfont->priv->fontmap;
}

static void
pango_core_text_font_init (PangoCoreTextFont *ctfont)
{
  ctfont->priv = G_TYPE_INSTANCE_GET_PRIVATE (ctfont,
                                              PANGO_TYPE_CORE_TEXT_FONT,
                                              PangoCoreTextFontPrivate);
}

static void
pango_core_text_font_class_init (PangoCoreTextFontClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);
  PangoFontClass *font_class = PANGO_FONT_CLASS (class);

  object_class->finalize = pango_core_text_font_finalize;

  font_class->describe = pango_core_text_font_describe;
  /* In CoreText the device unit is points so describe_absoulute and
   * describe are the same.
   */
  font_class->describe_absolute = pango_core_text_font_describe;
  font_class->get_coverage = pango_core_text_font_get_coverage;
  font_class->find_shaper = pango_core_text_font_find_shaper;
  font_class->get_font_map = pango_core_text_font_get_font_map;

  g_type_class_add_private (object_class, sizeof (PangoCoreTextFontPrivate));
}

void
_pango_core_text_font_set_font_map (PangoCoreTextFont    *font,
                                    PangoCoreTextFontMap *fontmap)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  g_return_if_fail (priv->fontmap == NULL);
  g_weak_ref_set((GWeakRef *) &priv->fontmap, fontmap);
}

void
_pango_core_text_font_set_face (PangoCoreTextFont *ctfont,
                                PangoCoreTextFace *ctface)
{
  PangoCoreTextFontPrivate *priv = ctfont->priv;

  priv->face = ctface;
}

PangoCoreTextFace *
_pango_core_text_font_get_face (PangoCoreTextFont *font)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  return priv->face;
}

gpointer
_pango_core_text_font_get_context_key (PangoCoreTextFont *font)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  return priv->context_key;
}

void
_pango_core_text_font_set_context_key (PangoCoreTextFont *font,
                                       gpointer        context_key)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  priv->context_key = context_key;
}

void
_pango_core_text_font_set_font_key (PangoCoreTextFont    *font,
                                    PangoCoreTextFontKey *key)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  priv->key = key;

  if (priv->coverage)
    {
      pango_coverage_unref (priv->coverage);
      priv->coverage = NULL;
    }
}

void
_pango_core_text_font_set_ctfont (PangoCoreTextFont *font,
                                  CTFontRef          font_ref)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  priv->font_ref = font_ref;
}

/**
 * pango_core_text_font_get_ctfont:
 * @font: A #PangoCoreTextFont
 *
 * Returns the CTFontRef of a font.
 *
 * Return value: the CTFontRef associated to @font.
 *
 * Since: 1.24
 */
CTFontRef
pango_core_text_font_get_ctfont (PangoCoreTextFont *font)
{
  PangoCoreTextFontPrivate *priv = font->priv;

  return priv->font_ref;
}
