#ifndef _GTKMM_STYLEPROPERTY_H
#define _GTKMM_STYLEPROPERTY_H

/* Copyright (C) 2014 The gtkmm Development Team
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

#include <gtkmm/widget.h>
#include <glibmm/ustring.h>
#include <glibmm/value.h>

namespace Gtk
{

/** Base class for widget style properties.
 *
 * This class manages the value type-agnostic bits of style properties.
 *
 * @newin{3,16}
 */
class GTKMM_API StylePropertyBase
{
public:

  /** Returns the name of the style property.
   */
  Glib::ustring get_name() const;

protected:
  Gtk::Widget*    widget_;
  GType           value_type_;
  GParamSpec*     param_spec_;

  /** Constructs a style property of type @a value_type for @a widget.
   * The property is not registered in Gtk, call install_style_property()
   * in order to do that. The properties are usually installed on the
   * initialization of the first instance of a widget.
   */
  StylePropertyBase(Gtk::Widget& widget, GType value_type);
  ~StylePropertyBase() noexcept;

  /** Checks if the style property has already been installed.
   */
  bool lookup_style_property(const Glib::ustring& name);

  /** Installs the style property specified by the given @a param_spec.
   */
  void install_style_property(GParamSpec* param_spec);

  /** Returns the name of the style property.
   */
  const char* get_name_internal() const;

private:
  // noncopyable
  StylePropertyBase(const StylePropertyBase&);
  StylePropertyBase& operator=(const StylePropertyBase&);
};

/** Represents a widget style property.
 *
 * This class maps one to one to a style property of a widget in Gtk. A style
 * property is similar to object properties (Glib::Property), but its value is
 * stored in the style object associated with the widget. The style property,
 * similarly to the object properties, has some global data for each property.
 * The global data includes the following information about the style property:
 *  * unique name, used to identify the style property
 *  * human-readable nickname
 *  * short description
 *  * default value, minimum and maximum bounds (applicable depending on the
 *      type of the style property)
 *  * flags, defining, among other things, whether the property can be read or
 *      written
 *
 * The StyleProperty class currently supports only the name and default value.
 * The minimum and maximum bounds are set to the full range of the value. The
 * nickname and the description are set to empty. The flags are set to indicate that
 * the property is read-only (this doesn't disallow a style provider to set the
 * value of the property).
 *
 * The global information must be installed into the widget once per property.
 * This is handled automatically. A reference to the widget must be passed on
 * the construction of the style property.
 *
 * @newin{3,16}
 */
template <class T>
class StyleProperty : public StylePropertyBase
{
public:
  typedef T PropertyType;
  typedef Glib::Value<T> ValueType;

  /** Constructs a style property for @a widget with @a name. For each instance
   * of the widget, the same property must be constructed with the same name.
   */
  StyleProperty(Gtk::Widget& widget, const Glib::ustring& name);

  /** Constructs a style property for @a widget with @a name and @a default_value.
   * For each instance of the widget, the same property must be constructed with
   * the same name and default value.
   */
  StyleProperty(Gtk::Widget& widget, const Glib::ustring& name, const PropertyType& default_value);

  /** Returns the value of the style property. Equivalent to
   * Gtk::Widget::get_style_property(get_name(), PropertyType& value).
   */
  inline PropertyType get_value() const;

  /** Returns the value of the style property. Equivalent to
   * Gtk::Widget::get_style_property(get_name(), PropertyType& value).
   */
  inline operator PropertyType() const;
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS

/**** Gtk::StyleProperty<T> ****************************************/

template <class T>
StyleProperty<T>::StyleProperty(Gtk::Widget& widget, const Glib::ustring& name)
:
  StylePropertyBase(widget, ValueType::value_type())
{
  if (!lookup_style_property(name))
  {
    ValueType value;
    value.init(value_type_);
    GParamSpec* param_spec = value.create_param_spec(name);
    param_spec->flags = (GParamFlags)(param_spec->flags & ~G_PARAM_WRITABLE); // value.create_param_spec() sets read-write flags
    install_style_property(param_spec);
  }
}

template <class T>
StyleProperty<T>::StyleProperty(Gtk::Widget& widget, const Glib::ustring& name,
                      const typename StyleProperty<T>::PropertyType& default_value)
:
  StylePropertyBase(widget, ValueType::value_type())
{
  if (!lookup_style_property(name))
  {
    ValueType value;
    value.init(value_type_);
    value.set(default_value);
    GParamSpec* param_spec = value.create_param_spec(name);
    param_spec->flags = (GParamFlags)(param_spec->flags & ~G_PARAM_WRITABLE); // value.create_param_spec() sets read-write flags
    install_style_property(param_spec);
  }
}

template <class T> inline
typename StyleProperty<T>::PropertyType StyleProperty<T>::get_value() const
{
  PropertyType value;
  widget_->get_style_property(get_name(), value);
  return value;
}

template <class T> inline
StyleProperty<T>::operator PropertyType() const
{
  return this->get_value();
}

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} // namespace Gtk

#endif /* _GTKMM_STYLEPROPERTY_H */
