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


#include "mforms/table.h"

#include "wf_view.h"

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

namespace MySQL {
  namespace Forms {

    /**
     * Implements a special layout behavior which mimics the GTK.table layout.
     */
    ref class GtkTableLayout : public LayoutEngine
    {
    public:
      virtual bool Layout(Object^ container, LayoutEventArgs^ arguments) override;
    };

    /**
     * This class keeps all the information which is needed during the layout process in the table
     * layout engine.
     */
    public ref class CellEntry {
    public:
      Control^ control;
      bool isVisible;
      System::Drawing::Rectangle bounds;
      int leftAttachment;
      int rightAttachment;
      int topAttachment;
      int bottomAttachment;
      bool horizontalExpand;
      bool verticalExpand;
      bool horizontalFill;
      bool verticalFill;
    };

    /**
     * A list of CellEntry instances.
     */
    typedef List<CellEntry^> CellList;

    /**
      * Implements a GTK.table like control. In order to make the table layout working we have to derive
      * a new class which manages (and returns by request) an instance of the table layout engine.
      * It also manages some additional information which is used by the layout process.
      */
    public ref class Table : public Panel
    {
    private:
      GtkTableLayout^ layoutEngine;

      CellList content;
      bool homogeneous;
      int columnSpacing;
      int rowSpacing;
      int columnCount;
      int rowCount;
    public:
      Table()
      {
        homogeneous= false;
        columnSpacing= 0;
        rowSpacing= 0;
        columnCount= 0;
        rowCount= 0;
      }

      System::Drawing::Size ComputeLayout(System::Drawing::Size proposedSize, bool resizeChildren);
      virtual System::Drawing::Size GetPreferredSize(System::Drawing::Size proposedSize) override;

      void Add(Control ^control, int left, int right, int top, int bottom, int flags);
      void Remove(Control ^control);

      /**
       * Property to return our table layout engine instead the default one.
       */
      virtual property Windows::Forms::Layout::LayoutEngine^ LayoutEngine
      {
        Windows::Forms::Layout::LayoutEngine^ get() override
        {
          if (layoutEngine == nullptr)
            layoutEngine = gcnew GtkTableLayout();

          return layoutEngine;
        }
      };

      /**
       * Used get or set the homogeneous mode of the layouter. Homogeneous means all cells are resized to the same
       * size to fill up the entire table, based on the largest entry.
       */
      virtual property bool Homogeneous
      {
        bool get()
        {
          return homogeneous;
        }

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

      };

      /**
       * Used to get or set the row spacing value. This value is used as to define room not used 
       * by any cell between rows in a table.
       */
      virtual property int RowSpacing
      {
        int get()
        {
          return rowSpacing;
        }

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

      /**
       * Used to get or set the column spacing value. This value is used as to define room not used 
       * by any cell between columns in a table.
       */
      virtual property int ColumnSpacing
      {
        int get()
        {
          return columnSpacing;
        }

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

      /**
       * Used to get or set the number of rows to be used in the table.
       */
      virtual property int RowCount
      {
        int get() 
        { 
          return rowCount;
        }

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

      /**
       * Used to get or set the number of columns to be used in the table.
       */
      virtual property int ColumnCount
      {
        int get() 
        { 
          return columnCount;
        }

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

      /**
       * Returns the current content of the table in an unsorted fashion. The content list is filled
       * by calls to the function add.
       */
      virtual property CellList% Content
      {
        CellList% get()
        {
          return content;
        }
      }
    };

    /**
     * Implements a wrapper class that connects the backend's table class with a .NET table control.
     */
    public ref class TableImpl : public ViewImpl
    {
    protected:
      static bool create(::mforms::Table *self);
      static void add(mforms::Table *self, mforms::View *child, int left, int right, int top, int bottom, int flags);
      static void remove(mforms::Table *self, mforms::View* child);
      static void set_row_count(mforms::Table *self, int count);
      static void set_column_count(mforms::Table *self, int count);
      static void set_row_spacing(mforms::Table *self, int space);
      static void set_column_spacing(mforms::Table *self, int space);
      static void set_homogeneous(mforms::Table *self, bool value);
    public:
      TableImpl(::mforms::View *view)
        : ViewImpl(view)
      {
      }
      void add(Windows::Forms::Control ^control, int left, int right, int top, int bottom, int flags);
      void remove(Windows::Forms::Control ^control);

      void set_homogeneous(bool homogeneous);
      void set_row_count(int count);
      void set_column_count(int count);
      void set_row_spacing(int space);
      void set_column_spacing(int space);

      /**
       * Initializes the implementation structure which is used by the back end to send calls to this wrapper.
       */
      static void init(Manager ^mgr)
      {
        ::mforms::ControlFactory *f= ::mforms::ControlFactory::get_instance();

        DEF_CALLBACK1(bool, ::mforms::Table*, mgr, f->_table_impl, TableImpl, create);
        DEF_CALLBACK2(void, ::mforms::Table*, int, mgr, f->_table_impl, TableImpl, set_row_count);
        DEF_CALLBACK2(void, ::mforms::Table*, int, mgr, f->_table_impl, TableImpl, set_column_count);
        DEF_CALLBACK7(void, ::mforms::Table*, ::mforms::View*, int, int, int, int, int, mgr, f->_table_impl, TableImpl, add);
        DEF_CALLBACK2(void, ::mforms::Table*, ::mforms::View*, mgr, f->_table_impl, TableImpl, remove);
        DEF_CALLBACK2(void, ::mforms::Table*, int, mgr, f->_table_impl, TableImpl, set_row_spacing);
        DEF_CALLBACK2(void, ::mforms::Table*, int, mgr, f->_table_impl, TableImpl, set_column_spacing);
        DEF_CALLBACK2(void, ::mforms::Table*, bool, mgr, f->_table_impl, TableImpl, set_homogeneous);
      }
    };

  };
};

#endif
