/* 
 * © 2008-2009 Sun Microsystems, Inc.
 *
 * 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 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef _WF_BOX_H_
#define _WF_BOX_H_


#include "mforms/box.h"

#include "wf_view.h"

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Windows::Forms::Layout;
using namespace System::Runtime::InteropServices;

namespace MySQL {
  namespace Forms {

    // Implements an extended flow layout behavior which mimics the horizontal GTK.box layout.
    public ref class HorizontalGtkBoxLayout : public LayoutEngine
    {
    public:
      virtual bool Layout(Object^ container, LayoutEventArgs^ arguments) override;
    };

    // Implements an extended flow layout behavior which mimics the vertical GTK.box layout.
    public ref class VerticalGtkBoxLayout : public LayoutEngine
    {
    public:
      virtual bool Layout(Object^ container, LayoutEventArgs^ arguments) override;
    };

    // Implements a GTK.box like control.
    public ref class Box : public Panel
    {
    private:
      Dictionary<Control^, bool> expandInfo;
      Dictionary<Control^, bool> fillInfo;

      Windows::Forms::Layout::LayoutEngine^ layoutEngine;
      bool horizontal;
      bool homogeneous;
      int spacing;
      Drawing::Size preferredSize;
    public:
      Box()
      {
        horizontal= false;
        homogeneous= false;
        spacing= 0;
        preferredSize= Drawing::Size(0, 0);
      }

      bool GetControlExpands(Control ^ctl)
      {
        return expandInfo[ctl];
      }

      bool GetControlFills(Control ^ctl)
      {
        return fillInfo[ctl];
      }

      virtual Drawing::Size GetPreferredSize(Drawing::Size proposedSize) override;

      void Add(Control ^ctl, bool expands, bool fills)
      {
        ViewImpl::set_layout_dirty(this, true);
        expandInfo[ctl]= expands;
        fillInfo[ctl]= fills;
        Controls->Add(ctl);
      }

      void Remove(Control ^ctl)
      {
        expandInfo.Remove(ctl);
        fillInfo.Remove(ctl);
        Controls->Remove(ctl);
      }

      virtual property Windows::Forms::Layout::LayoutEngine^ LayoutEngine
      {
        Windows::Forms::Layout::LayoutEngine^ get() override
        {
          if (layoutEngine == nullptr)
            if (horizontal)
              layoutEngine = gcnew HorizontalGtkBoxLayout();
            else
              layoutEngine = gcnew VerticalGtkBoxLayout();

          return layoutEngine;
        }
      }

      virtual property bool Homogeneous
      {
        bool get()
        {
          return homogeneous;
        }

        void set(bool value)
        {
          if (homogeneous != value)
          {
            ViewImpl::set_layout_dirty(this, true);
            homogeneous= value;
            Refresh();
          }
        }

      }

      virtual property int Spacing
      {
        int get()
        {
          return spacing;
        }

        void set(int value)
        {
          if (spacing != value)
          {
            ViewImpl::set_layout_dirty(this, true);
            spacing= value;
            Refresh();
          }
        }

      }

      virtual property bool Horizontal
      {
        bool get() 
        { 
          return horizontal; 
        }

        void set(bool value)
        {
          if (horizontal != value)
          {
            horizontal= value;
            layoutEngine= nullptr;
            ViewImpl::set_layout_dirty(this, true);
            Refresh();
          }
        }
      }
    };

    // Implements a wrapper class that connects the backend's box class with a .NET box control.
    public ref class BoxImpl : public ViewImpl
    {
    protected:
      static bool create(::mforms::Box *self, bool horizontal)
      {
        BoxImpl ^box= gcnew BoxImpl(self);

        if (box != nullptr)
        {
          Box^ native_box= ViewImpl::create<Box>(self, box);
          native_box->Horizontal= horizontal;
          return true;
        }
        return false;
      }

      static void add(mforms::Box *self, mforms::View *child, bool expand, bool fill)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->add(((ViewImpl^)ObjectImpl::FromUnmanaged(child))->get_control<Control>(), expand, fill);
        }
      }

      static void add_end(mforms::Box *self, mforms::View *child, bool expand, bool fill)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->add_end(((ViewImpl^)ObjectImpl::FromUnmanaged(child))->get_control<Control>(), expand, fill);
        }
      }

      static void remove(mforms::Box *self, mforms::View *child)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->remove(((ViewImpl^)ObjectImpl::FromUnmanaged(child))->get_control<Control>());
        }
      }

      static void set_spacing(mforms::Box *self, int space)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->set_spacing(space);
        }
      }

      static void set_padding(mforms::Box *self, int padding)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->set_padding(padding);
        }
      }

      static void set_back_color(mforms::Box *self, const std::string &color)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->set_back_color(CppStringToNative(color));
        }
      }

      static void set_homogeneous(mforms::Box *self, bool value)
      {
        BoxImpl^ box= (BoxImpl^)ObjectImpl::FromUnmanaged(self);

        if (box != nullptr)
        {
          box->set_homogeneous(value);
        }
      }
    public:
      BoxImpl(::mforms::View *view)
        : ViewImpl(view)
      {
      }
      void add(Windows::Forms::Control ^control, bool expand, bool fill);
      void add_end(Windows::Forms::Control ^control, bool expand, bool fill);
      void remove(Windows::Forms::Control ^control);

      void set_homogeneous(bool flag);
      void set_spacing(int space);
      void set_padding(int padding);
      void set_back_color(String ^color);

      static void init(Manager ^mgr)
      {
        ::mforms::ControlFactory *f= ::mforms::ControlFactory::get_instance();

        DEF_CALLBACK2(bool, ::mforms::Box*, bool, mgr, f->_box_impl, BoxImpl, create);
        DEF_CALLBACK4(void, ::mforms::Box*, ::mforms::View*, bool, bool, mgr, f->_box_impl, BoxImpl, add);
        DEF_CALLBACK4(void, ::mforms::Box*, ::mforms::View*, bool, bool, mgr, f->_box_impl, BoxImpl, add_end);
        DEF_CALLBACK2(void, ::mforms::Box*, ::mforms::View*, mgr, f->_box_impl, BoxImpl, remove);
        DEF_CALLBACK2(void, ::mforms::Box*, int, mgr, f->_box_impl, BoxImpl, set_spacing);
        DEF_CALLBACK2(void, ::mforms::Box*, int, mgr, f->_box_impl, BoxImpl, set_padding);
        DEF_CALLBACK2(void, ::mforms::Box*, const std::string&, mgr, f->_box_impl, BoxImpl, set_back_color);
        DEF_CALLBACK2(void, ::mforms::Box*, bool, mgr, f->_box_impl, BoxImpl, set_homogeneous);
      }
    };

  };
};

#endif
