/*
 * Copyright (c) 2008  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.
 */

_DEFS(cluttermm,clutter)

#include <cluttermm/types.h>
#include <glibmm/object.h>
#include <cairomm/context.h>
#include <clutter/clutter.h>
_PINCLUDE(glibmm/private/object_p.h)

namespace Clutter
{

enum PathNodeType
{
  PATH_MOVE_TO,
  PATH_LINE_TO,
  PATH_CURVE_TO,
  PATH_CLOSE,
  PATH_REL_MOVE_TO = PATH_MOVE_TO | CLUTTER_PATH_RELATIVE,
  PATH_REL_LINE_TO = PATH_LINE_TO | CLUTTER_PATH_RELATIVE,
  PATH_REL_CURVE_TO = PATH_CURVE_TO | CLUTTER_PATH_RELATIVE
};

class PathNode
{
  _CLASS_BOXEDTYPE_STATIC(PathNode, ClutterPathNode)
public:
  typedef unsigned int  size_type;
  typedef int           difference_type;

  typedef Knot          value_type;
  typedef Knot&         reference;
  typedef const Knot&   const_reference;

  // Use plain pointers for simplicity.
  typedef Knot*         iterator;
  typedef const Knot*   const_iterator;

#ifndef GLIBMM_HAVE_SUN_REVERSE_ITERATOR
  typedef std::reverse_iterator<iterator>       reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#else
  typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
                                Knot, Knot&, Knot*, ptrdiff_t> reverse_iterator;

  typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
                                Knot, const Knot&, const Knot*, ptrdiff_t> const_reverse_iterator;
#endif
  explicit PathNode(PathNodeType type);

  size_type size() const;
  bool empty() const;

  iterator begin();
  iterator end();
  const_iterator begin() const;
  const_iterator end()   const;

  inline reverse_iterator       rbegin()       { return reverse_iterator(end()); }
  inline reverse_iterator       rend()         { return reverse_iterator(begin()); }
  inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
  inline const_reverse_iterator rend()   const { return const_reverse_iterator(begin()); }

  inline reference       front()       { return *begin();  }
  inline const_reference front() const { return *begin();  }
  inline reference       back()        { return *rbegin(); }
  inline const_reference back()  const { return *rbegin(); }

  inline reference       operator[](size_type i)       { return begin()[i]; }
  inline const_reference operator[](size_type i) const { return begin()[i]; }

#m4begin
  _WRAP_EQUAL(clutter_path_node_equal)
#m4end
};

class Path : public Glib::Object
{
  _CLASS_GOBJECT(Path, ClutterPath, CLUTTER_PATH, Glib::Object, GObject)
  _DERIVES_INITIALLY_UNOWNED()

protected:
  _CTOR_DEFAULT()
  _WRAP_CTOR(Path(const Glib::ustring& description),
             clutter_path_new_with_description)

public:
  class Nodes;

  _WRAP_CREATE()
  _WRAP_CREATE(const Glib::ustring& description)

  inline Nodes nodes();
  inline const Nodes nodes() const;

  _WRAP_METHOD(void add_move_to(int x, int y), clutter_path_add_move_to)
  _WRAP_METHOD(void add_rel_move_to(int x, int y), clutter_path_add_rel_move_to)
  _WRAP_METHOD(void add_line_to(int x, int y), clutter_path_add_line_to)
  _WRAP_METHOD(void add_rel_line_to(int x, int y), clutter_path_add_rel_line_to)
  _WRAP_METHOD(void add_curve_to(int x1, int y1, int x2, int y2, int x3, int y3),
               clutter_path_add_curve_to)
  _WRAP_METHOD(void add_rel_curve_to(int x1, int y1, int x2, int y2, int x3, int y3),
               clutter_path_add_rel_curve_to)
  _WRAP_METHOD(void add_close(), clutter_path_add_close)
  _WRAP_METHOD(bool add_string(const Glib::ustring& str), clutter_path_add_string)

#m4 dnl Functionality is available through the STL-style Path::Nodes interfaces
  _IGNORE(clutter_path_add_node, clutter_path_get_n_nodes, clutter_path_get_node,
          clutter_path_get_nodes, clutter_path_insert_node, clutter_path_remove_node,
          clutter_path_replace_node)

  typedef void (* ClutterPathCallback) (const ClutterPathNode *node,
                                      gpointer               data);
                     
  /** For instance,
   * void on_path_foreach(const Clutter::PathNode& node);
   */                 
  typedef sigc::slot<void, PathNode> ForEachSlot;
  
  //TODO: Documentation.
  void foreach(const ForEachSlot& slot);
  _IGNORE(clutter_path_foreach)
                                                
  _WRAP_METHOD(Glib::ustring get_description() const, clutter_path_get_description)
  _WRAP_METHOD(void set_description(const Glib::ustring& str), clutter_path_set_description)

  _WRAP_METHOD(void add_cairo_path(const Glib::RefPtr<Cairo::Path>& path), clutter_path_add_cairo_path)
  _WRAP_METHOD(void to_cairo_path(Glib::RefPtr<Cairo::Context>& context) const, clutter_path_to_cairo_path)

  _WRAP_METHOD(void clear(), clutter_path_clear)
  _WRAP_METHOD(guint get_position(double progress, Knot& position), clutter_path_get_position)
  _WRAP_METHOD(guint get_length() const, clutter_path_get_length)

  _WRAP_PROPERTY("description", Glib::ustring)
  _WRAP_PROPERTY("length", guint)
};

class Path::Nodes
{
private:
  friend class Clutter::Path;

  ClutterPath* path_;

  inline Nodes(const Path::Nodes& other) : path_ (other.path_) {}
  explicit inline Nodes(ClutterPath* path) : path_ (path) {}
  Nodes& operator=(const Path::Nodes&);

public:
  typedef unsigned int      size_type;
  typedef int               difference_type;
  typedef PathNode          value_type;
  typedef const value_type  const_reference;

  class const_iterator;
  class iterator;

  class reference
  {
  private:
    friend class Path::Nodes;
    friend class Path::Nodes::iterator;
    friend class Path::Nodes::const_iterator;

    ClutterPath* path_;
    unsigned int index_;

    inline reference(ClutterPath* path, unsigned int index)
      : path_ (path), index_ (index) {}

    // disallow the address-of operator
    reference* operator&();
    const reference* operator&() const;

  public:
    reference& operator=(const PathNode& node);
    operator PathNode() const;
  };

  class const_iterator
  {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
  protected:
    Path::Nodes::reference ref_;
#endif
  public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef PathNode                        value_type;
    typedef int                             difference_type;
    typedef const value_type                reference;
    typedef void                            pointer;

    inline const_iterator() : ref_ (0, 0) {}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    inline const_iterator(ClutterPath* path, unsigned int index)
      : ref_ (path, index) {}

    inline bool equal(const const_iterator& b) const
      { return (ref_.index_ == b.ref_.index_); }
#endif
    reference operator*() const;
    reference operator[](difference_type i) const;

    inline const_iterator& operator++()    { ++ref_.index_; return *this; }
    inline const_iterator  operator++(int) { return const_iterator(ref_.path_, ref_.index_++); }
    inline const_iterator& operator--()    { --ref_.index_; return *this; }
    inline const_iterator  operator--(int) { return const_iterator(ref_.path_, ref_.index_--); }
  };

  class iterator : public const_iterator
  {
  public:
    typedef Path::Nodes::reference reference;

    inline iterator() {}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    inline iterator(ClutterPath* path, unsigned int index)
      : const_iterator(path, index) {}
#endif
    inline reference operator*() const { return ref_; }
    inline reference operator[](difference_type i) const
      { return reference(ref_.path_, ref_.index_ + i); }

    inline iterator& operator++()    { ++ref_.index_; return *this; }
    inline iterator  operator++(int) { return iterator(ref_.path_, ref_.index_++); }
    inline iterator& operator--()    { --ref_.index_; return *this; }
    inline iterator  operator--(int) { return iterator(ref_.path_, ref_.index_--); }
  };

#ifndef GLIBMM_HAVE_SUN_REVERSE_ITERATOR
  typedef std::reverse_iterator<iterator>       reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#else
  typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
                                value_type, reference, void, ptrdiff_t> reverse_iterator;
  typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
                                value_type, const_reference, void, ptrdiff_t> const_reverse_iterator;
#endif
  size_type   size() const;
  inline bool empty() const { return (size() == 0); }

  inline iterator       begin()       { return iterator(path_, 0); }
  inline const_iterator begin() const { return const_iterator(path_, 0); }
  inline iterator       end()         { return iterator(path_, size()); }
  inline const_iterator end() const   { return const_iterator(path_, size()); }

  inline reverse_iterator       rbegin()       { return reverse_iterator(end()); }
  inline reverse_iterator       rend()         { return reverse_iterator(begin()); }
  inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
  inline const_reverse_iterator rend()   const { return const_reverse_iterator(begin()); }

  const_reference  operator[](size_type i) const;
  inline reference operator[](size_type i) { return reference(path_, i); }

  inline reference       front()       { return reference(path_, 0); }
  inline const_reference front() const { return (*this)[0]; }
  inline reference       back()        { return reference(path_, size() - 1); }
  inline const_reference back()  const { return (*this)[size() - 1]; }
};

/** @relates Path::Nodes::const_iterator */
inline bool operator==(const Path::Nodes::const_iterator& a, const Path::Nodes::const_iterator& b)
  { return a.equal(b); }

/** @relates Path::Nodes::const_iterator */
inline bool operator!=(const Path::Nodes::const_iterator& a, const Path::Nodes::const_iterator& b)
  { return !a.equal(b); }

#ifndef DOXYGEN_SHOULD_SKIP_THIS
inline Path::Nodes Path::nodes()
  { return Path::Nodes(gobj()); }

inline const Path::Nodes Path::nodes() const
  { return Path::Nodes(const_cast<ClutterPath*>(gobj())); }
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} // namespace Clutter
