/* 
 * 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;

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();

      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;
      }

      /**
       * 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(); void set(bool value); }

      /**
       * 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(); void set(int value); }

      /**
       * 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(); void set(int value); }

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

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

      /**
       * 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(); }

      /** Set to apply automatic padding on both sides to center the content instead stretching it. */
      property bool HorizontalCenter;
      property bool VerticalCenter;
    };

    /**
     * Implements a wrapper class that connects the backend's table class with a .NET table control.
     */
    public ref class TableImpl : public ViewImpl
    {
    protected:
      virtual void set_padding(int left, int top, int right, int bottom) override;

      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);
      }
    };

  };
};
