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

#pragma once

using namespace System;
using namespace System::Windows::Forms;
using namespace System::Windows::Forms::Layout;
using namespace System::Collections::Generic;

namespace MySQL {
  namespace Forms {

    ref class Box;

    public ref class GtkBoxLayout abstract : public LayoutEngine
    {
    public:
      virtual System:: Drawing::Size GetPreferredSize(Box^ container, System::Drawing::Size proposedSize) = 0;
    };

    // Implements an extended flow layout behavior which mimics the horizontal GTK.box layout.
    public ref class HorizontalGtkBoxLayout : public GtkBoxLayout
    {
    public:
      System::Drawing::Size ComputeLayout(Box^ box, System::Drawing::Size proposedSize, bool resizeChildren);
      virtual bool Layout(Object^ container, LayoutEventArgs^ arguments) override;
      virtual System::Drawing::Size GetPreferredSize(Box^ container, System::Drawing::Size proposedSize) override;
    };

    // Implements an extended flow layout behavior which mimics the vertical GTK.box layout.
    public ref class VerticalGtkBoxLayout : public GtkBoxLayout
    {
    public:
      System::Drawing::Size ComputeLayout(Box^ box, System::Drawing::Size proposedSize, bool resizeChildren);
      virtual bool Layout(Object^ container, LayoutEventArgs^ arguments) override;
      virtual System::Drawing::Size GetPreferredSize(Box^ container, System::Drawing::Size proposedSize) override;
    };

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

      GtkBoxLayout^ layoutEngine;
      bool horizontal;
      bool homogeneous;
      int spacing;

    protected:
      virtual void OnPaintBackground(PaintEventArgs^ args) override;
    public:
      Box();

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

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

      virtual System::Drawing::Size GetPreferredSize(System::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)
      {
        Controls->Remove(ctl);
        expandInfo.Remove(ctl);
        fillInfo.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);
      static void add(mforms::Box *self, mforms::View *child, bool expand, bool fill);
      static void add_end(mforms::Box *self, mforms::View *child, bool expand, bool fill);
      static void remove(mforms::Box *self, mforms::View *child);
      static void set_spacing(mforms::Box *self, int space);
      static void set_homogeneous(mforms::Box *self, bool 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);

      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*, bool, mgr, f->_box_impl, BoxImpl, set_homogeneous);
      }
    };

  };
};
