/* Copyright (C) 2007 The cluttermm 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, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <glibmm/object.h>
#include <pangomm/context.h>
#include <pangomm/layout.h>
#include <cluttermm/animation.h>
#include <cluttermm/color.h>
#include <cluttermm/scriptable.h>
#include <cluttermm/shader.h>
#include <cluttermm/types.h> // For Event*
#include <clutter/clutter.h> // For ClutterActorBox

_DEFS(cluttermm,clutter)
_PINCLUDE(glibmm/private/object_p.h)


namespace Clutter
{

class Animation;

class ActorBox
{
  _CLASS_BOXEDTYPE_STATIC(ActorBox, ClutterActorBox)
public:
  ActorBox(float x1, float y1, float x2, float y2);

  _MEMBER_GET(x1, x1, float, float)
  _MEMBER_SET(x1, x1, float, float)
  _MEMBER_GET(y1, y1, float, float)
  _MEMBER_SET(y1, y1, float, float)
  _MEMBER_GET(x2, x2, float, float)
  _MEMBER_SET(x2, x2, float, float)
  _MEMBER_GET(y2, y2, float, float)
  _MEMBER_SET(y2, y2, float, float)
};

_WRAP_ENUM(ActorFlags, ClutterActorFlags)
_WRAP_ENUM(AllocationFlags, ClutterAllocationFlags)

//TODO: Write a version of the large description from here: http://clutter-project.org/docs/clutter/stable/ClutterActor.html#ClutterActor.description
class Actor : public Glib::Object, public Scriptable
{
  _CLASS_GOBJECT(Actor, ClutterActor, CLUTTER_ACTOR, Glib::Object, GObject)
  _DERIVES_INITIALLY_UNOWNED()
  _IMPLEMENTS_INTERFACE(Scriptable)

protected:
  _CTOR_DEFAULT()

public:
  //There is no create() because this is an abstract base class.
  //_WRAP_CREATE()

  _WRAP_METHOD(void set_flags(ActorFlags flags), clutter_actor_set_flags)
  _WRAP_METHOD(void unset_flags(ActorFlags flags), clutter_actor_unset_flags)
  _WRAP_METHOD(ActorFlags get_flags() const, clutter_actor_get_flags)

  //TODO: Remove these now that there are properties instead?
  bool is_mapped() const;
  bool is_realized() const;
  bool is_visible() const;
  bool is_reactive() const;

  _IGNORE(clutter_actor_destroy)

  _WRAP_METHOD(void show(), clutter_actor_show)
  _WRAP_METHOD(void show_all(), clutter_actor_show_all)
  _WRAP_METHOD(void hide(), clutter_actor_hide)
  _WRAP_METHOD(void hide_all(), clutter_actor_hide_all)
  _WRAP_METHOD(void realize(), clutter_actor_realize)
  _WRAP_METHOD(void unrealize(), clutter_actor_unrealize)
  _WRAP_METHOD(void map(), clutter_actor_map)
  _WRAP_METHOD(void unmap(), clutter_actor_unmap)
  _WRAP_METHOD(void paint(), clutter_actor_paint)
  _WRAP_METHOD(void queue_redraw(), clutter_actor_queue_redraw)
  _WRAP_METHOD(void queue_relayout(), clutter_actor_queue_relayout)

#m4 dnl // Those are varargs convenience functions for C coders
  _IGNORE(clutter_actor_animate, clutter_actor_animate_with_alpha, clutter_actor_animate_with_timeline)

  //TODO: Check that we don't want to wrap this: _WRAP_METHOD(void destroy(), clutter_actor_destroy)
  //_IGNORE(clutter_actor_destroy)

  _WRAP_METHOD(Glib::RefPtr<Pango::Context> get_pango_context(),
               clutter_actor_get_pango_context, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Pango::Context> get_pango_context() const,
               clutter_actor_get_pango_context, refreturn, constversion)
  _WRAP_METHOD(Glib::RefPtr<Pango::Context> create_pango_context(),
               clutter_actor_create_pango_context)

  _WRAP_METHOD(Glib::RefPtr<Pango::Layout> create_pango_layout(const Glib::ustring& text),
               clutter_actor_create_pango_layout)

  //TODO: Wrap CoglMatrix
  _WRAP_METHOD(void get_transformation_matrix(CoglMatrix* matrix), clutter_actor_get_transformation_matrix)

  _WRAP_METHOD(bool is_in_clone_paint() const, clutter_actor_is_in_clone_paint)
  _WRAP_METHOD(bool has_pointer() const, clutter_actor_has_pointer)
  _WRAP_METHOD(void set_text_direction(TextDirection text_dir), clutter_actor_set_text_direction)
  _WRAP_METHOD(TextDirection get_text_direction() const, clutter_actor_get_text_direction)


  _WRAP_METHOD(Glib::RefPtr<Actor> get_stage(), clutter_actor_get_stage, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Actor> get_stage() const, clutter_actor_get_stage, refreturn, constversion)

#m4 _CONVERSION(`const Geometry&', `const ClutterGeometry*', `($3).gobj()')
  _WRAP_METHOD(void set_geometry(const Geometry& geometry), clutter_actor_set_geometry)
  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_geometry)
  Geometry get_geometry() const;
  //_WRAP_METHOD(void get_coords(float *x_1, float *y_1, float *x_2, float *y_2) const, clutter_actor_get_coords)
  _WRAP_METHOD(void set_size(float width, float height), clutter_actor_set_size)
  _WRAP_METHOD(void get_position(float& x, float& y), clutter_actor_get_position)
  _WRAP_METHOD(void set_position(float x, float y), clutter_actor_set_position)
  //_WRAP_METHOD(void get_abs_position(float& x, float& y) const, clutter_actor_get_abs_position)
  _WRAP_METHOD(float get_width() const, clutter_actor_get_width)
  _WRAP_METHOD(float get_height() const, clutter_actor_get_height)
  _WRAP_METHOD(void set_width(float width), clutter_actor_set_width)
  _WRAP_METHOD(void set_height(float height), clutter_actor_set_height)

  _WRAP_METHOD(void set_x(float x), clutter_actor_set_x)
  _WRAP_METHOD(float get_x() const, clutter_actor_get_x)
  _WRAP_METHOD(void set_y(float y), clutter_actor_set_y)
  _WRAP_METHOD(float get_y() const, clutter_actor_get_y)
  _WRAP_METHOD(void set_reactive(bool reactive = true), clutter_actor_set_reactive)
  _WRAP_METHOD(bool get_reactive() const, clutter_actor_get_reactive)

  _WRAP_METHOD(void set_rotation(RotateAxis axis, double angle, float x, float y, float z), clutter_actor_set_rotation)
  _WRAP_METHOD(void set_z_rotation_from_gravity(double angle, Gravity gravity), clutter_actor_set_z_rotation_from_gravity)
  _WRAP_METHOD(double get_rotation(RotateAxis axis, float& x, float& y, float& z) const, clutter_actor_get_rotation)
  _WRAP_METHOD(Gravity get_z_rotation_gravity() const, clutter_actor_get_z_rotation_gravity)
  _WRAP_METHOD(void set_opacity(guint8 opacity), clutter_actor_set_opacity)
  _WRAP_METHOD(guint8 get_opacity() const, clutter_actor_get_opacity)
  _WRAP_METHOD(void set_name(const gchar *name), clutter_actor_set_name)
  _WRAP_METHOD(Glib::ustring get_name() const, clutter_actor_get_name)
  _WRAP_METHOD(guint32 get_gid() const, clutter_actor_get_gid)
  _WRAP_METHOD(void set_clip(float xoff, float yoff, float width, float height), clutter_actor_set_clip)
  _WRAP_METHOD(void get_clip(float& xoff, float& yoff, float& width, float& height), clutter_actor_get_clip)
  _WRAP_METHOD(void remove_clip(), clutter_actor_remove_clip)
  _WRAP_METHOD(bool has_clip() const, clutter_actor_has_clip)
  _WRAP_METHOD(void set_parent(const Glib::RefPtr<Actor>& parent), clutter_actor_set_parent)
  _WRAP_METHOD(Glib::RefPtr<Actor> get_parent(), clutter_actor_get_parent, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Actor> get_parent() const, clutter_actor_get_parent, refreturn, constversion)
  _WRAP_METHOD(void reparent(const Glib::RefPtr<Actor>& new_parent), clutter_actor_reparent)
  _WRAP_METHOD(void unparent(), clutter_actor_unparent)
  _WRAP_METHOD(void raise(const Glib::RefPtr<Actor>& below), clutter_actor_raise)
  _WRAP_METHOD(void lower(const Glib::RefPtr<Actor>& above), clutter_actor_lower)
  _WRAP_METHOD(void raise_top (), clutter_actor_raise_top)
  _WRAP_METHOD(void lower_bottom(), clutter_actor_lower_bottom)

  _WRAP_METHOD(Glib::RefPtr<Shader> get_shader(), clutter_actor_get_shader, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Shader> get_shader() const, clutter_actor_get_shader, refreturn, constversion)
  _WRAP_METHOD(void set_shader(const Glib::RefPtr<Shader>& shader), clutter_actor_set_shader)
  _WRAP_METHOD_DOCS_ONLY(clutter_actor_set_shader_param)
  template <class ParamType> void set_shader_param(const Glib::ustring& param, const ParamType& value);
  _IGNORE(clutter_actor_set_shader_param_int, clutter_actor_set_shader_param_float)
  //_WRAP_METHOD_DOCS_ONLY(clutter_actor_get_paint_area)
  //ActorBox get_paint_area() const;
  _WRAP_METHOD(bool is_rotated() const, clutter_actor_is_rotated)
  _WRAP_METHOD(bool is_scaled() const, clutter_actor_is_scaled)
  _WRAP_METHOD(bool should_pick_paint() const, clutter_actor_should_pick_paint)

  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_allocation_vertices)
  void get_allocation_vertices(Vertex vertices[4]) const;

  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_allocation_vertices)
  void get_allocation_vertices(const Glib::RefPtr<Actor>& ancestor, Vertex vertices[4]) const;

  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_abs_allocation_vertices)
  void get_abs_allocation_vertices(Vertex vertices[4]) const;

  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_allocation_box)
  ActorBox get_allocation_box() const;

#m4 _CONVERSION(`const ActorBox&', `const ClutterActorBox*',`($3).gobj()')
  _WRAP_METHOD(void allocate(const ActorBox& box, AllocationFlags absolute_origin_changed = ALLOCATION_NONE), clutter_actor_allocate)

  _WRAP_METHOD(void allocate_preferred_size(AllocationFlags absolute_origin_changed = ALLOCATION_NONE), clutter_actor_allocate_preferred_size)
    _WRAP_METHOD(void allocate_available_size(float x, float y, float available_width, float                 available_height, AllocationFlags flags = ALLOCATION_NONE), clutter_actor_allocate_available_size)

#m4 _CONVERSION(`const Vertex&',`ClutterVertex*',`const_cast<ClutterVertex*>(($3).gobj())')
#m4 _CONVERSION(`Vertex&',`ClutterVertex*',`($3).gobj()')
  _WRAP_METHOD(void apply_relative_transform_to_point(const Glib::RefPtr<Actor>& ancestor, const Vertex& point, Vertex& vertex) const, clutter_actor_apply_relative_transform_to_point)
  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_allocation_vertices)
  void apply_relative_transform_to_point(const Vertex& point, Vertex& vertex) const;

  _WRAP_METHOD(void set_request_mode(RequestMode mode), clutter_actor_set_request_mode)
  _WRAP_METHOD(RequestMode get_request_mode() const, clutter_actor_get_request_mode)

#m4 _CONVERSION(`float&',`float*',`&($3)')
  _WRAP_METHOD(void get_preferred_size(float& min_width_p, float& min_height_p, float& natural_width_p, float& natural_height_p) const, clutter_actor_get_preferred_size)
  _WRAP_METHOD(void get_preferred_width(float for_height, float& min_width_p, float& natural_width_p) const, clutter_actor_get_preferred_width)
  _WRAP_METHOD(void get_preferred_height(float for_width, float& min_height_p, float& natural_height_p) const, clutter_actor_get_preferred_height)
  _WRAP_METHOD(guint8 get_paint_opacity() const, clutter_actor_get_paint_opacity)
  _WRAP_METHOD(bool get_paint_visibility() const, clutter_actor_get_paint_visibility)
  _WRAP_METHOD(void move_anchor_point_from_gravity(Gravity gravity), clutter_actor_move_anchor_point_from_gravity)
  _WRAP_METHOD(void move_anchor_point(float anchor_x, float anchor_y), clutter_actor_move_anchor_point)


  _WRAP_METHOD(bool get_fixed_position_set() const, clutter_actor_get_fixed_position_set)
  _WRAP_METHOD(void set_fixed_position_set(bool is_set), clutter_actor_set_fixed_position_set)
  _WRAP_METHOD(void get_transformed_position(float& x, float& y) const, clutter_actor_get_transformed_position)
  _WRAP_METHOD(void get_transformed_size(float& width, float& height) const, clutter_actor_get_transformed_size)

  _WRAP_METHOD_DOCS_ONLY(clutter_actor_get_allocation_geometry)
  Geometry get_allocation_geometry() const;

  _WRAP_METHOD(void set_depth(float depth), clutter_actor_set_depth)
  _WRAP_METHOD(float get_depth() const, clutter_actor_get_depth)
  _WRAP_METHOD(void set_scale(double scale_x, double scale_y), clutter_actor_set_scale)
  _WRAP_METHOD(void set_scale_full(double scale_x, double scale_y, float center_x, float center_y), clutter_actor_set_scale_full)
  _WRAP_METHOD(void set_scale_with_gravity(double scale_x, double scale_y, Gravity gravity), clutter_actor_set_scale_with_gravity)
  _WRAP_METHOD(void get_scale(double& scale_x, double& scale_y) const, clutter_actor_get_scale)
  _WRAP_METHOD(void get_scale_center(float& center_x, float& center_y) const, clutter_actor_get_scale_center)
  _WRAP_METHOD(Gravity get_scale_gravity() const, clutter_actor_get_scale_gravity)

  //_WRAP_METHOD(void get_abs_size(float& width, float& height) const, clutter_actor_get_abs_size)
  _WRAP_METHOD(void get_size(float& width, float& height) const, clutter_actor_get_size)
  _WRAP_METHOD(void move_by(float dx, float dy), clutter_actor_move_by)

  _WRAP_METHOD(void apply_transform_to_point(const Vertex& point, Vertex& vertex) const, clutter_actor_apply_transform_to_point)
  _WRAP_METHOD(void transform_stage_point(float x, float y, float& x_out, float& y_out) const, clutter_actor_transform_stage_point)

  _WRAP_METHOD(bool event(ClutterEvent* event, bool capture), clutter_actor_event)
  _WRAP_METHOD(static Glib::RefPtr<Actor> get_actor_by_gid(guint32 id), clutter_get_actor_by_gid, refreturn)

  _WRAP_METHOD(void set_anchor_point(float anchor_x, float anchor_y), clutter_actor_set_anchor_point)
  _WRAP_METHOD(void get_anchor_point(float& anchor_x, float& anchor_y) const, clutter_actor_get_anchor_point)
  _WRAP_METHOD(void set_anchor_point_from_gravity(ClutterGravity gravity), clutter_actor_set_anchor_point_from_gravity)
  _WRAP_METHOD(Gravity get_anchor_point_gravity(), clutter_actor_get_anchor_point_gravity)

  _WRAP_METHOD(void grab_key_focus(), clutter_actor_grab_key_focus)

// Animation API
  // Hand coded to use std::map instead of the 3 arguments used by C API
  _IGNORE(clutter_actor_animatev, clutter_actor_animate_with_timelinev, clutter_actor_animate_with_alphav)

  //TODO: Documentation.
  Glib::RefPtr<Animation> animate(gulong mode, unsigned int duration, const std::map<std::string, Glib::ValueBase>& properties);
  Glib::RefPtr<Animation> animate(gulong mode, const Glib::RefPtr<Timeline>& timeline, const std::map<std::string, Glib::ValueBase>& properties);
  Glib::RefPtr<Animation> animate(const Glib::RefPtr<Alpha>& alpha, const std::map<std::string, Glib::ValueBase>& properties);

  _WRAP_METHOD(Glib::RefPtr<Animation> get_animation(), clutter_actor_get_animation, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Animation> get_animation() const, clutter_actor_get_animation, refreturn, constversion)

  _WRAP_METHOD(void push_internal(), clutter_actor_push_internal)
  _WRAP_METHOD(void pop_internal(), clutter_actor_pop_internal)


// signals
  _WRAP_SIGNAL(bool button_press_event(ButtonEvent* event), "button_press_event")
  _WRAP_SIGNAL(bool button_release_event(ButtonEvent* event), "button_release_event")
  _WRAP_SIGNAL(bool motion_event(MotionEvent* event), "motion_event")
  _WRAP_SIGNAL(bool leave_event(CrossingEvent* event), "leave_event")
  _WRAP_SIGNAL(void show(), "show")
  _WRAP_SIGNAL(void hide(), "hide")
  _WRAP_SIGNAL(void destroy(), "destroy")
  _WRAP_SIGNAL(bool key_release_event(KeyEvent* event), "key_release_event")
  _WRAP_SIGNAL(bool enter_event(CrossingEvent* event), "enter_event")
  _WRAP_SIGNAL(bool event(Event* event), "event")
  _WRAP_SIGNAL(void key_focus_out(), "key-focus_out")
  _WRAP_SIGNAL(void key_focus_in(), "key-focus_in")
#m4 _CONVERSION(`ClutterActor*', `const Glib::RefPtr<Actor>&', `Glib::wrap(($3),true)')
  _WRAP_SIGNAL(void parent_set(const Glib::RefPtr<Actor>& old_parent), "parent_set")
  _WRAP_SIGNAL(bool scroll_event(ScrollEvent* event), "scroll_event")
  _WRAP_SIGNAL(bool key_press_event(KeyEvent* event), "key_press_event")
  _WRAP_SIGNAL(bool captured_event(Event* event), "captured_event")
  _WRAP_SIGNAL(void paint(), "paint")
  _WRAP_SIGNAL(void queue_redaw(const Glib::RefPtr<Actor>& origin), "queue_redraw")
  _WRAP_SIGNAL(void queue_relayout(), "queue_relayout")
  _WRAP_SIGNAL(void realize(), "realize")
  _WRAP_SIGNAL(void unrealize(), "unrealize")

#m4 _CONVERSION(`const ClutterColor*', `const Color&', `Color(const_cast<ClutterColor*>($3), true)')
  _WRAP_SIGNAL(void pick(const Color& color), "pick")

#m4 _CONVERSION(`ClutterActorBox*', `const ActorBox&', `ActorBox(const_cast<ClutterActorBox*>($3))')
  _WRAP_SIGNAL(void allocation_changed(const ActorBox& box, AllocationFlags flags), "allocation-changed", no_default_handler)

  _WRAP_PROPERTY("allocation", ActorBox)
  _WRAP_PROPERTY("anchor-x", float)
  _WRAP_PROPERTY("anchor-y", float)
  _WRAP_PROPERTY("anchor-gravity", Gravity)
  _WRAP_PROPERTY("clip", Geometry)
  _WRAP_PROPERTY("clip-to-allocation", bool)
  _WRAP_PROPERTY("depth", float)
  _WRAP_PROPERTY("fixed-position-set", bool)
  _WRAP_PROPERTY("fixed-x", float)
  _WRAP_PROPERTY("fixed-y", float)
  _WRAP_PROPERTY("has-clip", bool)
  _WRAP_PROPERTY("has-pointer", bool)
  _WRAP_PROPERTY("height", float)
  _WRAP_PROPERTY("min-height", float)
  _WRAP_PROPERTY("min-height-set", bool)
  _WRAP_PROPERTY("min-width", float)
  _WRAP_PROPERTY("min-width-set", bool)
  _WRAP_PROPERTY("name", Glib::ustring)
  _WRAP_PROPERTY("opacity", guint8)
  _WRAP_PROPERTY("reactive", bool)
  _WRAP_PROPERTY("request-mode", RequestMode)
  _WRAP_PROPERTY("rotation-angle-x", double)
  _WRAP_PROPERTY("rotation-angle-y", double)
  _WRAP_PROPERTY("rotation-angle-z", double)
  _WRAP_PROPERTY("rotation-center-x", Vertex)
  _WRAP_PROPERTY("rotation-center-y", Vertex)
  _WRAP_PROPERTY("rotation-center-z", Vertex)
  _WRAP_PROPERTY("rotation-center-z-gravity", Gravity)
  _WRAP_PROPERTY("scale-x", double)
  _WRAP_PROPERTY("scale-y", double)
  _WRAP_PROPERTY("scale-center-x", double)
  _WRAP_PROPERTY("scale-center-y", double)
  _WRAP_PROPERTY("scale-gravity", Gravity)
  _WRAP_PROPERTY("show-on-set-parent", bool)
  _WRAP_PROPERTY("text-direction", TextDirection)
  _WRAP_PROPERTY("visible", bool)
  _WRAP_PROPERTY("width", float)
  _WRAP_PROPERTY("x", float)
  _WRAP_PROPERTY("y", float)

protected:
  _WRAP_METHOD(void set_shader_param_value(const Glib::ustring& param, const Glib::ValueBase& value),
               clutter_actor_set_shader_param)

  //TODO: Add new vfuncs.
  _WRAP_VFUNC(void show_all(), "show_all")
  _WRAP_VFUNC(void hide_all(), "hide_all")

  // TODO: I think we need to hand-code the vfuncs if we need to handle
  // the float* being NULL.
  #m4 _CONVERSION(`float*', `float&', `*($3)')
  #m4 _CONVERSION(`const ClutterActorBox*', `const ActorBox&', `Glib::wrap($3)')
  _WRAP_VFUNC(void pick(const Color& color), "pick")
  _WRAP_VFUNC(void get_preferred_width(float for_height, float& min_width_p, float& natural_width_p) const, "get_preferred_width")
  _WRAP_VFUNC(void get_preferred_height(float for_width, float& min_height_p, float& natural_height_p) const, "get_preferred_height")
  _WRAP_VFUNC(void allocate(const ActorBox& box, AllocationFlags absolute_origin_changed), "allocate")

};

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <class ParamType>
void Actor::set_shader_param(const Glib::ustring& param, const ParamType& value)
{
  Glib::Value<ParamType> param_value;
  param_value.init(Glib::Value<ParamType>::value_type());

  param_value.set(value);
  this->set_shader_param_value(param, param_value);
}
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} // namespace Clutter
