/*
 * Copyright © 2012 Red Hat Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see .
 *
 * Authors: Benjamin Otte 
 */
#include "config.h"
#include "gtkprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkcssanimationprivate.h"
#include "gtkcssarrayvalueprivate.h"
#include "gtkcssenumvalueprivate.h"
#include "gtkcssinheritvalueprivate.h"
#include "gtkcssinitialvalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcsssectionprivate.h"
#include "gtkcssshorthandpropertyprivate.h"
#include "gtkcssstringvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkcsstransitionprivate.h"
#include "gtkstyleanimationprivate.h"
#include "gtkstylepropertyprivate.h"
#include "gtkstyleproviderprivate.h"
G_DEFINE_ABSTRACT_TYPE (GtkCssStyle, gtk_css_style, G_TYPE_OBJECT)
static GtkCssSection *
gtk_css_style_real_get_section (GtkCssStyle *style,
                                guint        id)
{
  return NULL;
}
static gboolean
gtk_css_style_real_is_static (GtkCssStyle *style)
{
  return TRUE;
}
static void
gtk_css_style_class_init (GtkCssStyleClass *klass)
{
  klass->get_section = gtk_css_style_real_get_section;
  klass->is_static = gtk_css_style_real_is_static;
}
static void
gtk_css_style_init (GtkCssStyle *style)
{
}
GtkCssValue *
gtk_css_style_get_value (GtkCssStyle *style,
                          guint        id)
{
  gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
  return GTK_CSS_STYLE_GET_CLASS (style)->get_value (style, id);
}
GtkCssSection *
gtk_css_style_get_section (GtkCssStyle *style,
                           guint        id)
{
  gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
  return GTK_CSS_STYLE_GET_CLASS (style)->get_section (style, id);
}
GtkBitmask *
gtk_css_style_add_difference (GtkBitmask  *accumulated,
                              GtkCssStyle *style,
                              GtkCssStyle *other)
{
  gint len, i;
  if (style == other)
    return accumulated;
  len = _gtk_css_style_property_get_n_properties ();
  for (i = 0; i < len; i++)
    {
      if (_gtk_bitmask_get (accumulated, i))
        continue;
      if (!_gtk_css_value_equal (gtk_css_style_get_value (style, i),
                                 gtk_css_style_get_value (other, i)))
        accumulated = _gtk_bitmask_set (accumulated, i, TRUE);
    }
  return accumulated;
}
gboolean
gtk_css_style_is_static (GtkCssStyle *style)
{
  gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), TRUE);
  return GTK_CSS_STYLE_GET_CLASS (style)->is_static (style);
}
/*
 * gtk_css_style_print:
 * @style: a #GtkCssStyle
 * @string: the #GString to print to
 * @indent: level of indentation to use
 * @skip_initial: %TRUE to skip properties that have their initial value
 *
 * Print the @style to @string, in CSS format. Every property is printed
 * on a line by itself, indented by @indent spaces. If @skip_initial is
 * %TRUE, properties are only printed if their value in @style is different
 * from the initial value of the property.
 *
 * Returns: %TRUE is any properties have been printed
 */
gboolean
gtk_css_style_print (GtkCssStyle *style,
                     GString     *string,
                     guint        indent,
                     gboolean     skip_initial)
{
  guint i;
  gboolean retval = FALSE;
  g_return_val_if_fail (GTK_IS_CSS_STYLE (style), FALSE);
  g_return_val_if_fail (string != NULL, FALSE);
  for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
    {
      GtkCssSection *section;
      GtkCssStyleProperty *prop;
      GtkCssValue *value;
      const char *name;
      section = gtk_css_style_get_section (style, i);
      if (!section && skip_initial)
        continue;
      prop = _gtk_css_style_property_lookup_by_id (i);
      name = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop));
      value = gtk_css_style_get_value (style, i);
      g_string_append_printf (string, "%*s%s: ", indent, "", name);
      _gtk_css_value_print (value, string);
      g_string_append_c (string, ';');
      if (section)
        {
          g_string_append (string, " /* ");
          _gtk_css_section_print (section, string);
          g_string_append (string, " */");
        }
      g_string_append_c (string, '\n');
      retval = TRUE;
    }
  return retval;
}
char *
gtk_css_style_to_string (GtkCssStyle *style)
{
  GString *string;
  g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
  string = g_string_new ("");
  gtk_css_style_print (style, string, 0, FALSE);
  return g_string_free (string, FALSE);
}
static PangoUnderline
get_pango_underline_from_style (GtkTextDecorationStyle style)
{
  switch (style)
    {
    case GTK_CSS_TEXT_DECORATION_STYLE_DOUBLE:
      return PANGO_UNDERLINE_DOUBLE;
    case GTK_CSS_TEXT_DECORATION_STYLE_WAVY:
      return PANGO_UNDERLINE_ERROR;
    case GTK_CSS_TEXT_DECORATION_STYLE_SOLID:
    default:
      return PANGO_UNDERLINE_SINGLE;
    }
  g_return_val_if_reached (PANGO_UNDERLINE_SINGLE);
}
static PangoAttrList *
add_pango_attr (PangoAttrList  *attrs,
                PangoAttribute *attr)
{
  if (attrs == NULL)
    attrs = pango_attr_list_new ();
  pango_attr_list_insert (attrs, attr);
  return attrs;
}
PangoAttrList *
gtk_css_style_get_pango_attributes (GtkCssStyle *style)
{
  PangoAttrList *attrs = NULL;
  GtkTextDecorationLine decoration_line;
  GtkTextDecorationStyle decoration_style;
  const GdkRGBA *color;
  const GdkRGBA *decoration_color;
  gint letter_spacing;
  const char *font_feature_settings;
  /* text-decoration */
  decoration_line = _gtk_css_text_decoration_line_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_LINE));
  decoration_style = _gtk_css_text_decoration_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE));
  color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR));
  decoration_color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR));
  switch (decoration_line)
    {
    case GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE:
      attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
      if (!gdk_rgba_equal (color, decoration_color))
        attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
                                                                       decoration_color->green * 65535. + 0.5,
                                                                       decoration_color->blue * 65535. + 0.5));
      break;
    case GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH:
      attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
      if (!gdk_rgba_equal (color, decoration_color))
        attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
                                                                           decoration_color->green * 65535. + 0.5,
                                                                           decoration_color->blue * 65535. + 0.5));
      break;
    case GTK_CSS_TEXT_DECORATION_LINE_NONE:
    default:
      break;
    }
  /* letter-spacing */
  letter_spacing = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_LETTER_SPACING), 100);
  if (letter_spacing != 0)
    {
      attrs = add_pango_attr (attrs, pango_attr_letter_spacing_new (letter_spacing * PANGO_SCALE));
    }
  /* font-feature-settings */
  font_feature_settings = _gtk_css_string_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS));
  if (font_feature_settings != NULL)
    {
      attrs = add_pango_attr (attrs, pango_attr_font_features_new (font_feature_settings));
    }
  return attrs;
}
static GtkCssValue *
query_func (guint    id,
            gpointer values)
{
  return gtk_css_style_get_value (values, id);
}
PangoFontDescription *
gtk_css_style_get_pango_font (GtkCssStyle *style)
{
  GtkStyleProperty *prop;
  GValue value = { 0, };
  prop = _gtk_style_property_lookup ("font");
  _gtk_style_property_query (prop, &value, query_func, style);
  return (PangoFontDescription *)g_value_get_boxed (&value);
}