/* 
 * 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
 */
#include "stdafx.h"
#include "wf_wizard.h"
#include "mforms/app.h"

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

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* parent)
{
  WizardImpl ^wizard= gcnew WizardImpl(self);

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

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

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

void WizardImpl::set_title(::mforms::Wizard* self, const 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 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 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 string& caption)
{
  WizardImpl^ wizard= (WizardImpl^)ObjectImpl::FromUnmanaged(self);
  if (wizard != nullptr)
    wizard->set_next_caption(caption);
}

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

WizardImpl::WizardImpl(::mforms::Wizard* self)
  : ObjectImpl(self)
{
  _main= gcnew Form;
  _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;

  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.
  _main->BackColor= Color::White;
  _main->FormBorderStyle = ::FormBorderStyle::SizableToolWindow;
  _main->MaximizeBox = false;      
  _main->MinimizeBox = false;
  _main->StartPosition = FormStartPosition::CenterScreen;
  _main->CancelButton= _cancel_button;
  _main->Resize += gcnew EventHandler(resize);
  _main->Tag= gcnew IntPtr(self);
  _main->Icon= gcnew Icon("images/icons/MySQLWorkbench.ico", Size(16, 16));

  // The footer with buttons.
  _main->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(self);
  _back_button->Text= "&Back";
  _back_button->UseMnemonic= true;
  _back_button->Click += gcnew EventHandler(backClick);
  _back_button->FlatStyle = FlatStyle::System;

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

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

  _extra_button->Tag= gcnew IntPtr(self);
  _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.
  _main->Controls->Add(_sidebar);
  _sidebar->Tag= gcnew IntPtr(self);
  _sidebar->Dock= DockStyle::Left;
  _sidebar->ForeColor= Color::White;
  _sidebar->Paint += gcnew PaintEventHandler(this, &WizardImpl::sidebarPaint);

  // Title
  _main->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.
  _main->Controls->Add(_content);

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

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

/**
 * 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::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)
{
  ::mforms::Wizard* wizard= ViewImpl::get_backend_control<::mforms::Wizard>((Form^) sender);
  WizardImpl^ wizard_impl= (WizardImpl^)ObjectImpl::FromUnmanaged(wizard);

  System::Drawing::Rectangle area= wizard_impl->_main->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 string& title)
{
  _main->Text= CppStringToNative(title);
}

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

void WizardImpl::run_modal()
{
  _main->ShowDialog(_parent);
}

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

void WizardImpl::close()
{
  _main->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 string& heading)
{
  _title->Text= CppStringToNative(heading);
}

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

void WizardImpl::set_step_list(const vector<string>& steps)
{
  // Remove old list.
  _main->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
  {
    _main->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 string& caption)
{
  _extra_button->Text= CppStringToNative(caption);
}

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

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

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

