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

/**
 * Implementation of the mforms wizard form.
 */

#include "stdafx.h"

#include "mforms/mforms.h"
#include "wf_view.h"
#include "wf_form.h"
#include "wf_wizard.h"

#include "base/log.h"
#include "ConvUtils.h"

using namespace System::IO;
using namespace Drawing::Drawing2D;

using namespace std;
using namespace MySQL::Forms;

DEFAULT_LOG_DOMAIN(DOMAIN_MFORMS_WRAPPER)

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

BufferedPanel::BufferedPanel()
: FlowLayoutPanel()
{
  DoubleBuffered= true;
}

//----------------- WizardImpl static functions ----------------------------------------------------

bool WizardImpl::create(mforms::Wizard* self, mforms::Form* owner)
{
  WizardImpl^ wizard = gcnew WizardImpl(self, owner);

  ViewImpl::create<Form>(self, wizard);
  wizard->setupLayout(self);

  if (wizard != nullptr)
    return true;
  return false;
}

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

void WizardImpl::set_title(::mforms::Wizard* self, const std::string& title)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_title(title);
}

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

void WizardImpl::run_modal(::mforms::Wizard* self)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->run_modal();
}

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

void WizardImpl::close(::mforms::Wizard* self)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    return wizard->close();
}

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

void WizardImpl::flush_events(::mforms::Wizard* self)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->flush_events();
}

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

void WizardImpl::set_content(::mforms::Wizard* self, ::mforms::View* view)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_content(view);
}

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

void WizardImpl::set_heading(::mforms::Wizard* self, const std::string& heading)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_heading(heading);
}

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

void WizardImpl::set_step_list(::mforms::Wizard* self, const vector<string>& steps)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_step_list(steps);
}

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

void WizardImpl::set_allow_cancel(::mforms::Wizard* self, bool flag)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_allow_cancel(flag);
}

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

void WizardImpl::set_allow_back(::mforms::Wizard* self, bool flag)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_allow_back(flag);
}

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

void WizardImpl::set_allow_next(::mforms::Wizard* self, bool flag)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_allow_next(flag);
}

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

void WizardImpl::set_show_extra(::mforms::Wizard* self, bool flag)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_show_extra(flag);
}

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

void WizardImpl::set_extra_caption(::mforms::Wizard* self, const std::string& caption)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_extra_caption(caption);
}

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

void WizardImpl::set_next_caption(::mforms::Wizard* self, const std::string& caption)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_next_caption(caption);
}

//----------------- WizardImpl actual implementation -----------------------------------------------

WizardImpl::WizardImpl(mforms::Wizard* self, mforms::Form* owner)
  : FormImpl(self, owner, mforms::FormDialogFrame)
{
}

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

/**
 * Used to draw a gradient background for the side bar.
 *
 * @param sender The control for which the paint event is triggered (the side bar).
 * @param arguments Additional data needed to paint the background.
 */
void WizardImpl::sidebarPaint(Object^ sender, PaintEventArgs^ arguments)
{
  FlowLayoutPanel^ panel= (FlowLayoutPanel^) sender;
  Graphics^ g= arguments->Graphics;

  LinearGradientBrush gradientBrush(
    Point(10, 0),
    Point(10, panel->Height),
    Color::FromArgb(255, 66, 111, 166),
    Color::FromArgb(255, 103, 186, 104));

  g->FillRectangle(%gradientBrush, 0, 0, panel->Width, panel->Height);
  if (_sidebar_overlay != nullptr)
    g->DrawImage(_sidebar_overlay, 0, panel->Height - _sidebar_overlay->Height);
}

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

void WizardImpl::setupLayout(mforms::Wizard* wizard)
{
  _sidebar = gcnew BufferedPanel;
  _title = gcnew Label;
  _extra_button = gcnew Button;
  _footer = gcnew TableLayoutPanel;
  _button_bar = gcnew FlowLayoutPanel;
  _back_button = gcnew Button;
  _next_button = gcnew Button;
  _cancel_button = gcnew Button;
  _content = gcnew Panel;

  std::string icon_path = mforms::App::get()->get_resource_path("wb-wizard-vista-bg.png");
  String^ native_path = gcnew String(icon_path.c_str());
  if (File::Exists(native_path))
    _sidebar_overlay = Image::FromFile(native_path);
  else
    _sidebar_overlay = nullptr;

  // Main form, the dialog itself.
  Form^ form = get_control<Form>();
  form->BackColor = Color::White;
  form->FormBorderStyle = FormBorderStyle::SizableToolWindow;
  form->MaximizeBox = false;      
  form->MinimizeBox = false;
  form->StartPosition = FormStartPosition::CenterScreen;
  form->CancelButton = _cancel_button;
  form->Resize += gcnew EventHandler(resize);
  form->Icon = gcnew Icon("images/icons/MySQLWorkbench.ico", Size(16, 16));

  // The footer with buttons.
  form->Controls->Add(_footer);
  _footer->RowCount = 1;
  _footer->ColumnCount = 2;
  _footer->AutoSize = true;
  _footer->Dock = DockStyle::Bottom;
  _footer->Padding = Padding(10);
  _footer->Controls->Add(_extra_button, 0, 0);

  _button_bar->FlowDirection = FlowDirection::RightToLeft;
  _button_bar->Controls->Add(_cancel_button);
  _button_bar->Controls->Add(_next_button);
  _button_bar->Controls->Add(_back_button);
  _button_bar->Dock = DockStyle::Right;
  _button_bar->AutoSize = true;
  _button_bar->Margin = Padding(0);
  _footer->Controls->Add(_button_bar, 1, 0);

  // We need to keep a reference to the wizard in the buttons to be able
  // to notify it when the buttons are clicked.
  _back_button->Tag = gcnew IntPtr(wizard);
  _back_button->Text = "&Back";
  _back_button->UseMnemonic = true;
  _back_button->Click += gcnew EventHandler(backClick);
  _back_button->FlatStyle = FlatStyle::System;

  _next_button->Tag = gcnew IntPtr(wizard);
  _next_button->Text = "&Next";
  _next_button->UseMnemonic = true;
  _next_button->Click += gcnew EventHandler(nextClick);
  _next_button->FlatStyle = FlatStyle::System;

  _cancel_button->Tag = gcnew IntPtr(wizard);
  _cancel_button->Text = "Cancel";
  _cancel_button->Click += gcnew EventHandler(cancelClick);
  _cancel_button->FlatStyle = FlatStyle::System;

  _extra_button->Tag = gcnew IntPtr(wizard);
  _extra_button->Text = "&Advanced";
  _extra_button->UseMnemonic = true;
  _extra_button->Click += gcnew EventHandler(extraClick);
  _extra_button->FlatStyle = FlatStyle::System;

  // The side bar with background painting and list of strings.
  form->Controls->Add(_sidebar);
  _sidebar->Tag = gcnew IntPtr(wizard);
  _sidebar->Dock = DockStyle::Left;
  _sidebar->ForeColor = Color::White;
  _sidebar->Paint += gcnew PaintEventHandler(this, &WizardImpl::sidebarPaint);

  // Title
  form->Controls->Add(_title);
  _title->AutoSize= false;
  try
  {
    _title->Font = gcnew Font("Tahoma", 12.0, FontStyle::Bold, GraphicsUnit::Pixel);
  }
  catch (System::ArgumentException^ e)
  {
    // Argument exception pops up when the system cannot find the Regular font style (corrupt font).
    log_error("WizardImpl::c_tor setting title font failed. %s\n", e->Message);
  }

  _title->ForeColor = ColorTranslator::FromHtml("#003392");
  _title->Location = Point(_sidebar->Width, 0);
  _title->Padding = Padding(15);

  // Other initialization.
  form->Controls->Add(_content);

  // Resize as last action. Triggers layouting of the elements.
  form->Size = Size(800, 600); 
}

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

void WizardImpl::nextClick(Object^ sender, EventArgs^ arguments)
{
  Windows::Forms::Button^ btn= (Windows::Forms::Button^)sender;

  if (btn->Tag != nullptr)
  {
    ::mforms::Wizard* wizard = ViewImpl::get_backend_control<mforms::Wizard>(btn);
    if (wizard != NULL)
      wizard->next_clicked();
  }
}

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

void WizardImpl::backClick(Object^ sender, EventArgs^ arguments)
{
  Windows::Forms::Button^ btn = (Windows::Forms::Button^)sender;

  if (btn->Tag != nullptr)
  {
    ::mforms::Wizard* wizard = ViewImpl::get_backend_control<mforms::Wizard>(btn);
    if (wizard != NULL)
      wizard->back_clicked();
  }
}

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

void WizardImpl::cancelClick(Object^ sender, EventArgs^ arguments)
{
  Windows::Forms::Button^ btn = (Windows::Forms::Button^)sender;

  if (btn->Tag != nullptr)
  {
    ::mforms::Wizard* wizard = ViewImpl::get_backend_control<mforms::Wizard>(btn);
    if (wizard != NULL && wizard->_cancel_slot())
      ((Form^) btn->TopLevelControl)->Close();
  }
}

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

void WizardImpl::extraClick(Object^ sender, EventArgs^ arguments)
{
  Windows::Forms::Button^ btn= (Windows::Forms::Button^)sender;

  if (btn->Tag != nullptr)
  {
    ::mforms::Wizard* wizard= ViewImpl::get_backend_control<::mforms::Wizard>(btn);
    if (wizard != NULL)
      wizard->extra_clicked();
  }
}

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

void WizardImpl::resize(Object^ sender, EventArgs^ arguments)
{
  Form^ form = dynamic_cast<Form^>(sender);
  ::mforms::Wizard* wizard= ViewImpl::get_backend_control<::mforms::Wizard>(form);
  WizardImpl^ wizard_impl= (WizardImpl^)ObjectImpl::FromUnmanaged(wizard);

  System::Drawing::Rectangle area = form->DisplayRectangle;
  wizard_impl->_title->Size = Size(area.Width - wizard_impl->_sidebar->Width, 50);
  wizard_impl->_title->Location= Point(wizard_impl->_sidebar->Width, 0);

  wizard_impl->_content->Size= Size(area.Width - wizard_impl->_sidebar->Width, 
    area.Height - wizard_impl->_footer->Height - wizard_impl->_title->Height);
  wizard_impl->_content->Location= Point(wizard_impl->_sidebar->Width, wizard_impl->_title->Height);
}

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

void WizardImpl::set_title(const std::string& title)
{
  Form^ form = get_control<Form>();
  form->Text = CppStringToNative(title);
}

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

void WizardImpl::run_modal()
{
  Form^ form = get_control<Form>();
  form->ShowDialog(Owner);
}

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

void WizardImpl::close()
{
  Form^ form = get_control<Form>();
  form->Close();
}

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

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

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

void WizardImpl::set_content(::mforms::View *view)
{
  // Remove old stuff if there is some.
  if (_content->Controls->Count > 0)
  {
    _content->Controls[0]->Dock= DockStyle::None;
    _content->Controls->Clear();
  }

  if (view != NULL)
  {
    ViewImpl^ new_content_view= (ViewImpl^)ObjectImpl::FromUnmanaged(view);
    view->set_resize_mode(::mforms::ResizeNone);
    Control^ new_content= new_content_view->get_control<Control>();

    _content->Controls->Add(new_content);
    new_content->Dock = DockStyle::Fill;

    // Focus the new page to allow mnemonics to works consistently.
    new_content->Focus();
  }
}

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

void WizardImpl::set_heading(const std::string& heading)
{
  _title->Text= CppStringToNative(heading);
}

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

void WizardImpl::set_step_list(const vector<string>& steps)
{
  Form^ form = get_control<Form>();

  // Remove old list.
  form->SuspendLayout();
  try
  {
    _sidebar->Controls->Clear();

    for each (string entry in steps)
    {
      Label^ label= gcnew Label();
      label->Text= CppStringToNative(entry.substr(1));
      FontStyle style= FontStyle::Regular;
      switch (entry[0])
      {
      case '*': // current task
        label->ForeColor= Color::White;
        style= FontStyle::Bold;
        break;
      case '.': // executed task
        label->ForeColor= Color::White;
        break;
      case '-': // open task
        label->ForeColor= Color::FromArgb(255, 192, 192, 192);
        break;
      }

      try
      {
        label->Font=  gcnew Font("Tahoma", 11.0, style, GraphicsUnit::Pixel);
      }
      catch (System::ArgumentException^ e)
      {
        // Argument exception pops up when the system cannot find the Regular font style (corrupt font).
        log_error("WizardImpl::set_step_list setting label font failed. %s\n", e->Message);
      }

      label->BackColor= Color::Transparent;
      label->Padding= Padding(7);
      label->Size= Size(_sidebar->Width, label->PreferredHeight);
      _sidebar->Controls->Add(label);
    }
  }
  finally
  {
    form->ResumeLayout();
  }
}

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

void WizardImpl::set_allow_cancel(bool flag)
{
  _cancel_button->Enabled= flag;
}

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

void WizardImpl::set_allow_back(bool flag)
{
  _back_button->Enabled= flag;
}

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

void WizardImpl::set_allow_next(bool flag)
{
  _next_button->Enabled= flag;
}

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

void WizardImpl::set_show_extra(bool flag)
{
  _extra_button->Visible= flag;
}

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

void WizardImpl::set_extra_caption(const std::string& caption)
{
  _extra_button->Text= CppStringToNative(caption);
}

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

void WizardImpl::set_next_caption(const std::string& caption)
{
  if (caption.size() == 0)
    _next_button->Text= "&Next";
  else
    _next_button->Text= CppStringToNative(caption)->Replace("_", "&");
}

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

