/* 
 * Copyright 2000 Karl Nelson
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * UIInfo "huffs goat choad" but we will try to make the best of it.
 */

#ifndef GNOMEMM_APP_HELPER_H
#define GNOMEMM_APP_HELPER_H

#include <new>
#include <gtkmm/menushell.h>
#include <gtkmm/toolbar.h>
#include <gtkmm/accelgroup.h>
#include <libgnomeuimm/ui-items-icon.h>
#include <libgnomeui/gnome-app.h>
#include <libgnomeui/gnome-app-helper.h>
#include <vector>

namespace Gnome
{

namespace UI
{

namespace Items
{

class Icon;

/*******************************************************/
// Info is the base of the UI item tree it represents broad types
// of GUI items for construction later.

template<class T_Info>
class Array;

class InfoData;

/*** Derived GnomeUIInfo
 * Note: When deriving this, you must not add any fields or add any virtuals
 */
class Info : public GnomeUIInfo
{
  friend class InfoData;
  friend class Array<Info>;

  // must not be dynamic
  void* operator new(size_t s);
public:
  void* operator new(size_t s, void* v) {return ::operator new(s, v);}

  Info();
  Info(const Info& src);
  ~Info();
  Info& operator=(const Info& src);

  Gtk::Widget* get_widget();
  const Gtk::Widget* get_widget() const;

  enum Type
  {
    END = GNOME_APP_UI_ENDOFINFO,
    ITEM = GNOME_APP_UI_ITEM,
    TOGGLEITEM = GNOME_APP_UI_TOGGLEITEM,
    RADIOITEMS = GNOME_APP_UI_RADIOITEMS,
    SUBTREE = GNOME_APP_UI_SUBTREE,
    SEPARATOR = GNOME_APP_UI_SEPARATOR,
    HELP = GNOME_APP_UI_HELP,
    BUILDER = GNOME_APP_UI_BUILDER_DATA,
    CONFIGURABLE = GNOME_APP_UI_ITEM_CONFIGURABLE,
    SUBTREE_STOCK = GNOME_APP_UI_SUBTREE_STOCK
  };
  
  Type type() const;

  const gchar* debug_get_icon_info() const;

  void set_accel(const Gtk::AccelKey& ak = Gtk::AccelKey());

  typedef sigc::slot<void> Callback;
  
protected:
  void init(Type type_);
  void init_cb(Type type_, const Icon& icon,
            const Glib::ustring& label, const Callback& cb,
            const Glib::ustring& tooltip);
  void init_sub(Type type_, const Icon& icon,
            const Glib::ustring& label, const Array<Info>& sub,
            const Glib::ustring& tooltip);
  InfoData* init_common(Type type_, const Icon& icon_,
            const Glib::ustring& label_, const Glib::ustring& hint_);

  InfoData* get_data_();
  const InfoData* get_data_() const;
  
  void set_data_(InfoData* infodata);
};






// subtree or submenu
class SubTree : public Info
{
protected:
  SubTree();
public:
  SubTree(const Glib::ustring& label, const Array<Info>& uitree,
          const Glib::ustring& tip = Glib::ustring());
  SubTree(const Icon& icon, const Glib::ustring& label,
          const Array<Info>& uitree, const Glib::ustring& tip = Glib::ustring());
  ~SubTree();

  Array<Info>& get_uitree();
};
typedef SubTree Menu;



#ifndef DOXYGEN_SHOULD_SKIP_THIS
// begin marker for C structures (not really for user use)
class Begin : public Info
{
public:
  Begin();

private:
  static GnomeUIBuilderData build_data_;
};

// end marker for C structures (not really for user use)
class End : public Info
{
public:
  End();
};
#endif /* DOXYGEN_SHOULD_SKIP_THIS */

namespace Array_Helpers
{

template<class T>
struct Traits
{
  typedef typename T::const_iterator iterator;
  static iterator begin(const T& t) {return t.begin();}
  static iterator end(const T& t) {return t.end();}
};

// You must place an End() to use this type
template<class T_Info>
struct Traits<T_Info*>
{
  typedef const T_Info* iterator;
  static iterator begin(const T_Info* t) {return t;}
  static iterator end( T_Info* t) {return t+64;} //64?
};

template<size_t N, class T_Info>
struct Traits<T_Info[N]>
{
  typedef const T_Info* iterator;
  static iterator begin(const T_Info* t) {return t;}
  static iterator end(const T_Info* t) {return &t[N];}
};

} /* namespace Array_Helpers */



// Array converter class
template<class T_Info>
class Array 
{
  //void* operator new (size_t s);  // not implemented
  Info* data_;
  Info* begin_;
  int size_;

  template <class I> void create(I b, I e);
public:
  typedef T_Info value_type;
  typedef T_Info& reference_type;
  typedef T_Info* iterator;
  typedef T_Info* const const_iterator;
  typedef T_Info* const pointer;
  typedef T_Info* const const_pointer;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;

  Array& operator=(const Array& a)
  {
    if (this == &a)
      return *this;

    delete [] data_;
    data_ = 0;
    size_ = 0;
    
    create(a.begin(), a.end());
    return *this;
  }

  Array()
  : data_(0), begin_(0), size_(0)
  { create((T_Info*)0, (T_Info*)0); }

  Array(Array& a)
  : data_(0), begin_(0), size_(0)
  { create(a.begin(), a.end()); }

  template <class T>
  Array(const T& t)
  : data_(0), begin_(0), size_(0)
  { create(Array_Helpers::Traits<T>::begin(t),
           Array_Helpers::Traits<T>::end(t));
  }

  template <class I>
  Array(I b, I e)
  : data_(0), begin_(0), size_(0)
  { create(b, e); }

  ~Array()
  {
    delete [] data_;
    data_ = 0;
    size_ = 0;
  }

  size_t size() const { return size_; }

  iterator begin() const
  { return static_cast<T_Info*>(begin_); }
  
  iterator end() const
  { return static_cast<T_Info*>(begin_ + size_); }
  
  reference_type operator[](size_t n) const
  { return static_cast<T_Info&>(begin_[n]); }
  

  GnomeUIInfo* gobj() const
  { return static_cast<GnomeUIInfo*>(data_); }
  
};


template <class T_Info>
template <class I>
void
Array<T_Info>::create(I b, I e)
{

  // NULL list
  if (b == e)
  {
    data_ = new End[1];
    return;
  }

  // determine number of Items
  for (I b2 = b ; b2 != e; ++b2, ++size_)
  {
    if (b2->type() == Info::END)
      break;
  }

  // must have a builder data on head
  if (b->type() != Info::BUILDER)
  {
    begin_ = data_ = new Info[size_+2]; //plus space for BEGIN and END.
    new (begin_) Begin(); //constructor without allocation.
    begin_++; //Hide Begin() element from outside, by keeping it before begin().
  }
  else
    begin_ = data_ = new Info[size_+1]; //plus space for END.

  // Copy data
  for (int i = 0; b != e; ++b, ++i)
  {
    new (&begin_[i]) T_Info(*b); //constructor without allocation.
  }

  new (&begin_[size_]) End(); //constructor without allocation.

  //At this point _size excludes the Begin() and End() GNOME internals elements,
  //so users of begin() and end() will never see them.
}


#ifndef DOXYGEN_SHOULD_SKIP_THIS
// This is a refcounted holder to deal with C interface badness
// users don't need to deal with these unless they are making new Info types.
//   This is probably refcounted because that's easier than copying in copy constructors, given that we can't
//   add member data to the Info class.
class InfoData
{
public:
  InfoData();
  InfoData(const Glib::ustring& label, const Glib::ustring& hint, const Icon& icon = Icon());

private:
  InfoData(const InfoData&); //Prevent use of copy constructor.
protected:
  virtual ~InfoData();

public:

  void ref();
  void unref();

  virtual void connect(Info&);

  typedef sigc::slot<void> Callback;

  void set_callback(const Callback& callback);
  void set_subtree(const Array<Info>& subtree);
  Array<Info>& get_subtree();


  Callback callback_;
  Array<Info> subtree_;
  Glib::ustring label_;
  Glib::ustring hint_;
  Icon icon_;

private:
  int ref_count_;
};
#endif /* DOXYGEN_SHOULD_SKIP_THIS */


/** Fill a menu with Items::Info items.
 * 'wrapper' for gnome_app_fill_menu()
 */
Items::Array<Info> fill (Gtk::MenuShell                      &menu_shell,
			 const Items::Array<Info>            &info,
			 const Glib::RefPtr<Gtk::AccelGroup> &accel_group,
			 bool                                 uline_accels = true,
			 int                                  pos = 0);


/** Fill a toolbar with Items::Info items.
 * 'wrapper' for gnome_app_fill_toolbar()
 */
Items::Array<Info> fill (Gtk::Toolbar                        &toolbar, 
			 const Items::Array<Info>            &info,
			 const Glib::RefPtr<Gtk::AccelGroup> &accel_group);



//: Utility functions for Gtk::MenuShell.
//- These should really be member methods of Gtk::MenuShell,
//- but they're part of gnomeui, not gtk, and they didn't subclass GtkMenuShell.
//static Gtk::MenuShell* MenuShell_find_menu_pos(const Gtk::MenuShell& parent,
//                                               const Glib::ustring &path,
//                                               int& pos);

//IGNORED gnome_app_fill_menu_with_data()
//IGNORED gnome_app_fill_menu_custom()
//IGNORED gnome_app_fill_toolbar_with_data()
//IGNORED gnome_app_fill_toolbar_custom()

} /* namespace Items */
} /* namespace UI */
} /* namespace Gnome */

#endif //GNOMEMM_APP_HELPER_H
