/* 
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _MFORMS_MENUBAR_H_
#define _MFORMS_MENUBAR_H_

#include "base/trackable.h"
#include <mforms/box.h>
#include <mforms/app.h>
#include <vector>

namespace mforms {
  class MenuBase;
}

#ifdef _WIN32
  #pragma make_public(mforms::MenuBase) // To allow using MenuBase in interface functions in assemblies.
#endif

namespace mforms {
  class MenuItem;
  class MenuBar;
  class View;
  class ContextMenu;

  enum MenuItemType
  {
    NormalMenuItem = 0,
    CheckedMenuItem,
    SeparatorMenuItem
  };

#ifndef SWIG
  // This merges functionality of menus and menu items.
  struct MFORMS_EXPORT MenuItemImplPtrs
  {
    bool (__stdcall *create_menu_bar)(MenuBar *item);
    bool (__stdcall *create_context_menu)(ContextMenu *item);

    bool (__stdcall *create_menu_item)(MenuItem *item, const std::string &, const MenuItemType type);
    void (__stdcall *set_title)(MenuItem *item, const std::string&);
    std::string (__stdcall *get_title)(MenuItem *item);
    void (__stdcall *set_shortcut)(MenuItem *item, const std::string&);
    void (__stdcall *set_enabled)(MenuBase *item, bool);
    bool (__stdcall *get_enabled)(MenuBase *item);
    void (__stdcall *set_checked)(MenuItem *item, bool);
    bool (__stdcall *get_checked)(MenuItem *item);

    void (__stdcall *insert_item)(MenuBase *menu, int index, MenuItem *item);
    void (__stdcall *remove_item)(MenuBase *menu, MenuItem *item); // NULL item to remove all

    void (__stdcall *popup_menu)(ContextMenu *menu, int x, int y); // TODO: windows (not used, so this can go)
  };
#endif
  
  // base abstract class for menuitem and menubase
  class MFORMS_EXPORT MenuBase : public Object, public base::trackable
  {
  protected:
    MenuBase();

    MenuItemImplPtrs *_impl;
    MenuBase *_parent;
    std::vector<MenuItem*> _items;

  public:
    virtual ~MenuBase();

#ifndef SWIG
    std::vector<MenuItem*> &get_subitems() { return _items; }
#endif

    MenuItem *find_item(const std::string &name);
    MenuItem *get_item(int i);
    int get_item_index(MenuItem *item);
    int item_count();

#ifndef SWIG
    MenuItem *add_item_with_title(const std::string &title, boost::function<void ()> action, const std::string &name="");
#endif
    MenuItem *add_separator();

    void add_item(MenuItem *item);
    void add_submenu(MenuItem *item) { add_item(item); }

    void insert_item(int index, MenuItem *item);
    void remove_all();
    void remove_item(MenuItem *item);
    void set_enabled(bool flag);
    bool get_enabled();
    
    virtual void validate();
    
    MenuBase *get_parent() { return  _parent; }
    MenuBase *get_top_menu();
  };
  
  /** A menu item that can be added to the host application menus.
   
   */
  class MFORMS_EXPORT MenuItem : public MenuBase
  {
  public:
    /** Constructor
     
     @param title - passing an empty title in the constructor will make the item be a separator
     */
    MenuItem(const std::string &title, const MenuItemType type = NormalMenuItem); 
    
    void set_title(const std::string &title);
    std::string get_title();
    
    void set_shortcut(const std::string &shortcut);
        
    void set_checked(bool flag);
    bool get_checked();

#ifndef SWIG
    boost::signals2::signal<void ()>* signal_clicked() { return &_clicked_signal; }
#endif
    void set_name(const std::string &name);
    std::string get_name() { return _name; }

    MenuItemType get_type() { return _type; }
    
    void set_validator(const boost::function<bool ()> &slot);
    
  public:
    void callback();
    virtual void validate();

  private:
    std::string _name;
    boost::function<bool ()> _validate;
    boost::signals2::signal<void ()> _clicked_signal;
    MenuItemType _type;
  };

  
  
  /** A menu that can be added to the host application.
   
   */
  class MFORMS_EXPORT MenuBar : public MenuBase
  {
  public:
    /** Constructor
     
     @param 
     */
    MenuBar();

#ifndef SWIG
    boost::signals2::signal<void (MenuItem*)>* signal_will_show() { return &_signal_will_show; }
#endif
    void set_item_enabled(const std::string &item_name, bool flag);
    void set_item_checked(const std::string &item_name, bool flag);

    void will_show_submenu_from(MenuItem *item);
    
  private:
    boost::signals2::signal<void (MenuItem*)> _signal_will_show;
  };


  /** A menu that can be attached to other controls
   */
  class MFORMS_EXPORT ContextMenu : public MenuBase
  {
  public:
    /** Constructor

     @param
     */
    ContextMenu();

#ifndef SWIG
    boost::signals2::signal<void (MenuItem*)>* signal_will_show() { return &_signal_will_show; }
#endif
    void set_item_enabled(const std::string &item_name, bool flag);
    void set_item_checked(const std::string &item_name, bool flag);

    void will_show();
    void will_show_submenu_from(MenuItem *item);

    void popup_at(int x, int y);

  private:
    boost::signals2::signal<void (MenuItem*)> _signal_will_show;
  };
};

#endif
