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

#include "stdafx.h"

#include "mforms/mforms.h"
#include "wf_view.h"
#include "wf_button.h"
#include "wf_form.h"
#include "wf_utilities.h"

using namespace System;
using namespace System::IO;
using namespace Drawing;
using namespace Windows::Forms;

using namespace MySQL;
using namespace MySQL::Forms;

//--------------------------------------------------------------------------------------------------

bool FormFillLayout::Layout(Object^ container, LayoutEventArgs^ arguments)
{
  // Not using the can_layout check here, as a form is a bit special.
  String ^reason = arguments->AffectedProperty;
  if (reason != "Bounds" && reason != "Padding" && reason != "Visible")
    return false;

  FillForm^ form = (FillForm^) container;
  if (!form->IsHandleCreated)
    return false;

  mforms::View *backend = ViewImpl::get_backend_control<mforms::View>(form);
  if (backend->is_destroying())
    return false;

  ViewImpl::adjust_auto_resize_from_docking(form);
  System::Drawing::Size newSize = form->ComputeLayout(form->Size, true);

  if (newSize.Width < form->MinimumSize.Width)
    newSize.Width= form->MinimumSize.Width;
  if (newSize.Height < form->MinimumSize.Height)
    newSize.Height= form->MinimumSize.Height;

  // Finally adjust the container.
  bool parentLayoutNeeded= !form->Size.Equals(newSize);
  if (parentLayoutNeeded)
    ViewImpl::resize_with_docking(form, newSize);

  return parentLayoutNeeded;
}

//-------------------------------------------------------------------------------------------------

System::Drawing::Size FormFillLayout::GetPreferredSize(Control^ container, System::Drawing::Size proposedSize)
{
  FillForm^ form = (FillForm^) container;
  return form->ComputeLayout(proposedSize, false);
}

//----------------- FillForm -----------------------------------------------------------------------

FillForm::FillForm()
{
}

//--------------------------------------------------------------------------------------------------

/**
 * Computes the entire layout of the form.
 * 
 * @param proposedSize The size to start from layouting. Since super ordinated controls may impose
 *                     a layout size we need to honor that (especially important for auto wrapping
 *                     labels).
 * @param resizeChildren Tells the function whether the computed client control bounds should be applied
 *                      (when doing a relayout) or not (when computing the preferred size).
 * @return The resulting size of the table.
 */
System::Drawing::Size FillForm::ComputeLayout(System::Drawing::Size proposedSize, bool resizeChildren)
{
  // This layout is actually very simple. Resize the first (and only) child control so that
  // it fills the entire client area of the container. If enabled resize the container to fit the
  // (preferred) size of the content.

  // Exclude any space needed to draw decoration (e.g. border) from layout processing.
  System::Drawing::Rectangle inner= DisplayRectangle;
  System::Drawing::Size current_size= Size;
  int horizontal_padding = current_size.Width - inner.Width;
  int vertical_padding = current_size.Height - inner.Height;

  if (Controls->Count > 0)
  {
    Control^ content= Controls[0];
    proposedSize.Width -= horizontal_padding;
    proposedSize.Height -= vertical_padding;

    ViewImpl::set_full_auto_resize(content);
    System::Drawing::Size contentSize = content->GetPreferredSize(proposedSize);

    if (ViewImpl::use_min_width_for_layout(content))
      contentSize.Width = content->MinimumSize.Width;
    if (ViewImpl::use_min_height_for_layout(content))
      contentSize.Height = content->MinimumSize.Height;

    // Adjust width of the container if it is too small or auto resizing is enabled.
    // Don't auto size if this is a standalone window (has no parent).
    bool auto_resize= /*(Parent != nullptr) &&*/ ViewImpl::can_auto_resize_horizontally(this);
    if (proposedSize.Width < contentSize.Width || auto_resize)
      proposedSize.Width = contentSize.Width;

    // Adjust height of the container if it is too small or auto resizing is enabled.
    auto_resize= /*(Parent != nullptr) &&*/ ViewImpl::can_auto_resize_vertically(this);
    if (proposedSize.Height < contentSize.Height || auto_resize)
      proposedSize.Height = contentSize.Height;

    if (resizeChildren)
    {
      // Now stretch the client control to fill the entire display area.
      ViewImpl::remove_auto_resize(content, AutoResizeMode::ResizeBoth);
      content->Bounds= System::Drawing::Rectangle(inner.Location, proposedSize);
    }

    proposedSize.Width += horizontal_padding;
    proposedSize.Height += vertical_padding;
  }

  return proposedSize;
}

//--------------------------------------------------------------------------------------------------

System::Drawing::Size FillForm::GetPreferredSize(System::Drawing::Size proposedSize)
{
  return layoutEngine->GetPreferredSize(this, proposedSize);
}

//-------------------------------------------------------------------------------------------------

bool FormImpl::create(mforms::Form *self, mforms::Form *owner, mforms::FormFlag flag)
{
  FormImpl ^form= gcnew FormImpl(self, owner, flag);

  if (form != nullptr)
  {
    Windows::Forms::Form ^f = ViewImpl::create<FillForm>(self, form);
    if (owner != NULL)
      f->StartPosition = FormStartPosition::CenterParent;
    else
      f->StartPosition= FormStartPosition::Manual;
    f->Disposed += gcnew System::EventHandler(&FormImpl::disposed);
    f->FormClosing += gcnew FormClosingEventHandler(&FormImpl::closing);

    if (File::Exists("images/icons/MySQLWorkbench.ico"))
      f->Icon= gcnew Icon("images/icons/MySQLWorkbench.ico", Size(16, 16));

    if ((flag & mforms::FormToolWindow) != 0)
    {
      if ((flag & mforms::FormResizable) != 0)
        f->FormBorderStyle= FormBorderStyle::SizableToolWindow;
      else
        f->FormBorderStyle= FormBorderStyle::FixedToolWindow;
    }
    else
      if ((flag & mforms::FormSingleFrame) != 0)
        f->FormBorderStyle= FormBorderStyle::FixedSingle;
      else
        if ((flag & mforms::FormDialogFrame) != 0)
          f->FormBorderStyle= FormBorderStyle::FixedDialog;
        else
          if ((flag & mforms::FormResizable) != 0)
            f->FormBorderStyle= FormBorderStyle::Sizable;
          else
            f->FormBorderStyle= FormBorderStyle::None;

    f->MinimizeBox= (flag & mforms::FormMinimizable) != 0;
    f->MaximizeBox= (flag & mforms::FormMinimizable) != 0;
    //f->TopMost=
    if ((flag & mforms::FormStayOnTop) != 0)
      f->Owner = UtilitiesImpl::get_mainform();

    form->_hide_on_close= (flag & mforms::FormHideOnClose) != 0;
    ViewImpl::remove_auto_resize(f, AutoResizeMode::ResizeBoth);

    return true;
  }
  return false;
}

//--------------------------------------------------------------------------------------------------

void FormImpl::set_title(mforms::Form *self, const std::string &title)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);

  if (form != nullptr)
    form->get_control<Windows::Forms::Form>()->Text= gcnew String(title.c_str());
}

//--------------------------------------------------------------------------------------------------

void FormImpl::show_modal(mforms::Form *self, mforms::Button *accept,mforms::Button *cancel)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);

  if (form != nullptr)
  {
    Windows::Forms::Form ^f= form->get_control<Windows::Forms::Form>();

    if (accept)
    {
      f->AcceptButton= ((ViewImpl^)ObjectImpl::FromUnmanaged(accept))->get_control<Windows::Forms::Button>();
      f->AcceptButton->DialogResult = Windows::Forms::DialogResult::OK;
    }
    if (cancel)
    {
      f->CancelButton= ((ViewImpl^)ObjectImpl::FromUnmanaged(cancel))->get_control<Windows::Forms::Button>();
      f->CancelButton->DialogResult = Windows::Forms::DialogResult::Cancel;
    }
    // Make the window top most (so it stays above all others), but non-blocking.
    //f->TopMost= true;
    f->Owner = UtilitiesImpl::get_mainform();
    if (f->InvokeRequired)
      f->BeginInvoke(gcnew MethodInvoker(f, &Control::Show));
    else
      f->Show();
  }
}

//--------------------------------------------------------------------------------------------------

bool FormImpl::run_modal(mforms::Form *self, mforms::Button *accept,mforms::Button *cancel)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);

  if (form != nullptr)
  {
    Windows::Forms::Form ^f= form->get_control<Windows::Forms::Form>();
    Windows::Forms::Button^ acceptButton = accept == NULL ? nullptr :
      ((ViewImpl^)ObjectImpl::FromUnmanaged(accept))->get_control<Windows::Forms::Button>();
    if (accept)
    {
      f->AcceptButton = acceptButton;
      f->AcceptButton->DialogResult = Windows::Forms::DialogResult::OK;
    }
    if (cancel)
    {
      f->CancelButton= ((ViewImpl^)ObjectImpl::FromUnmanaged(cancel))->get_control<Windows::Forms::Button>();
      f->CancelButton->DialogResult = Windows::Forms::DialogResult::Cancel;
    }

    Windows::Forms::DialogResult dialog_result;
    if (f->InvokeRequired)
    {
      Object^ invocation_result= f->Invoke(gcnew FillForm::ShowModalDelegate(f, &Windows::Forms::Form::ShowDialog),
        gcnew array<Object^> {form->_owner});
      dialog_result= *(Windows::Forms::DialogResult^) (invocation_result);
    }
    else
    {
      // If there is currently no active form then we are being called while the application
      // is not active. Focus the accept button if possible in that case to avoid having any
      // text field with keyboard focus, which might appear as if they were active (but are not in fact).
      Windows::Forms::Form^ active_form = Windows::Forms::Form::ActiveForm;
      if (active_form == nullptr && acceptButton != nullptr)
        f->ActiveControl = acceptButton;

      mforms::Utilities::enter_modal_loop();
      dialog_result= f->ShowDialog(form->_owner);
      mforms::Utilities::leave_modal_loop();
    }

    bool result = (dialog_result == Windows::Forms::DialogResult::OK);

    f->Hide();

    return result;
  }
  return false;
}

//--------------------------------------------------------------------------------------------------

void FormImpl::end_modal(mforms::Form *self, bool result)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);
  if (form != nullptr)
  {
    Windows::Forms::Form ^f= form->get_control<Windows::Forms::Form>();
    f->DialogResult = result ? Windows::Forms::DialogResult::OK : Windows::Forms::DialogResult::Cancel;
  }
}

//--------------------------------------------------------------------------------------------------

/**
 * Called by the backend when a form is closed from the application side.
 */
void FormImpl::close(mforms::Form *self)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);

  if (form != nullptr)
    form->get_control<Windows::Forms::Form>()->Close();
}

//--------------------------------------------------------------------------------------------------

void FormImpl::set_content(mforms::Form *self, mforms::View *view)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);

  if (form != nullptr)
  {
    ViewImpl^ child= (ViewImpl^)ObjectImpl::FromUnmanaged(view);
    Control^ ctl= child->get_control<Control>();
    form->get_control<Windows::Forms::Form>()->Controls->Add(ctl);
  }
}

//--------------------------------------------------------------------------------------------------


void FormImpl::center(mforms::Form *self)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);
  if (form != nullptr)
    form->center();
}

//--------------------------------------------------------------------------------------------------

void FormImpl::flush_events(mforms::Form *self)
{
  FormImpl^ form= (FormImpl^)ObjectImpl::FromUnmanaged(self);
  if (form != nullptr)
    form->flush_events();
}

//--------------------------------------------------------------------------------------------------

FormImpl::FormImpl(mforms::Form *form, mforms::Form *owner, mforms::FormFlag flag)
  : ViewImpl(form)
{
  if (owner != NULL)
  {
    // Meant is the window parent here, not the real owner.
    if (owner == mforms::Form::main_form())
      _owner = Application::OpenForms[0];
    else
    {
      FormImpl^ parent_form= (FormImpl^)ObjectImpl::FromUnmanaged(owner);
      _owner = parent_form->get_control<Windows::Forms::Form>();
    }
  }
  else
    _owner = nullptr;
}

//--------------------------------------------------------------------------------------------------

/**
 * Sets the startup position of the form so that it is centered over its parent when displayed.
 * If the form has no parent the desktop is used. This is also the default setting.
 */
void FormImpl::center()
{
  Windows::Forms::Form^ form= get_control<Windows::Forms::Form>();
  form->StartPosition= FormStartPosition::CenterParent;
}

//--------------------------------------------------------------------------------------------------

void FormImpl::flush_events()
{
  Application::DoEvents();
}

//--------------------------------------------------------------------------------------------------

void FormImpl::disposed(System::Object ^sender, System::EventArgs ^e)
{
  Windows::Forms::Form^ native_form= (Windows::Forms::Form^)sender;

  if (native_form->Tag != nullptr)
  {
    mforms::Form* form=ViewImpl::get_backend_control<mforms::Form>(native_form);
    form->release();
  }
}

//--------------------------------------------------------------------------------------------------

/**
 * Called by the OS if the form is closed by the user (e.g. via red cross close button).
 */
void FormImpl::closing(System::Object ^sender, FormClosingEventArgs  ^e)
{
  Windows::Forms::Form^ native_form= (Windows::Forms::Form^)sender;
  mforms::Form* be_form= ViewImpl::get_backend_control<mforms::Form>(native_form);
  FormImpl^ wrapper_form= (FormImpl^)ObjectImpl::FromUnmanaged(be_form);
  if (wrapper_form->hide_on_close())
  {
    e->Cancel= true;
    native_form->Hide();
  }

  be_form->was_closed();

  // Do nothing if hiding is not requested. In this case Windows will dispose of the form implicitly.
}

//--------------------------------------------------------------------------------------------------

bool FormImpl::hide_on_close()
{
  return _hide_on_close;
}

//--------------------------------------------------------------------------------------------------

