/* 
 * © 2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/**
 * Windows front end implementation of the mforms Utilities APIs.
 */

#include "stdafx.h"
#include "wf_Utilities.h"

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

using namespace MySQL::Forms;

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

CustomMessageBox::CustomMessageBox()
{
  _picture= gcnew Windows::Forms::PictureBox();
  _button1= gcnew Windows::Forms::Button();
  _button2= gcnew Windows::Forms::Button();
  _button3= gcnew Windows::Forms::Button();
  _messageLabel= gcnew Windows::Forms::Label();

  SuspendLayout();

  Controls->Add(_picture);
  Controls->Add(_button2);
  Controls->Add(_button1);
  Controls->Add(_button3);
  Controls->Add(_messageLabel);

  // picture
  _picture->Name= "picture";
  _picture->TabIndex= 0;
  _picture->TabStop= false;

  // button1
  _button1->Name= "button1";
  _button1->TabIndex= 1;
  _button1->UseVisualStyleBackColor= true;
  _button1->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);

  // button2
  _button2->Name= "button2";
  _button2->TabIndex= 2;
  _button2->UseVisualStyleBackColor= true;
  _button2->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);

  // button3
  _button3->Name= "button3";
  _button3->TabIndex= 3;
  _button3->UseVisualStyleBackColor= true;
  _button3->Click += gcnew System::EventHandler(this, &CustomMessageBox::ButtonClick);

  // messageText
  _messageLabel->Name= "messageLabel";
  _messageLabel->Padding= Windows::Forms::Padding(2);
  _messageLabel->TabIndex= 4;
  _messageLabel->Text= "label1";

  // Form
  this->Padding= Windows::Forms::Padding(8);
  this->FormBorderStyle= ::FormBorderStyle::FixedDialog;
  this->AutoScaleDimensions= System::Drawing::SizeF(6, 13);
  this->AutoScaleMode= Windows::Forms::AutoScaleMode::Font;
  this->StartPosition = FormStartPosition::CenterScreen;
  this->Name= "MessageBoxEx";
  this->Text= "";
  this->ShowInTaskbar= false;
  this->HelpButton= false;
  this->MinimizeBox= false;
  this->MaximizeBox= false;

  ResumeLayout(false);
}

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

/// <summary>
/// Displays the CustomMessageBox and returns a dialog result, depending on the button clicked.
/// </summary>
mforms::DialogResult CustomMessageBox::Show(String^ title, String^ text, Image^ image, String^ button1, String^ button2, String^ button3)
{
  CustomMessageBox^ box= gcnew CustomMessageBox();
  
  box->_picture->Image= image; 
  box->_picture->Size= image->Size;
  box->Text= title;
  box->_messageLabel->Text= text;
  box->_button1->Text= button1;
  box->_button2->Text= button2;
  box->_button3->Text= button3;
  box->ComputeLayout();

  box->ShowDialog();

  return lastResult;
}

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

/**
 * Helper function to set up message box buttons.
 */
void AdjustButton(Windows::Forms::Button^ button)
{
  // Note: we need to set the Enabled property to as indicator if this button should be considered
  // when layouting the form. The Visible property cannot be used as long as the button's parent
  // is still hidden (which is the case when we do the layout process).
  if (button->Text == "")
  {
    button->Visible= false;
    button->Enabled= false;
  }
  else
  {
    Size size= button->GetPreferredSize(Drawing::Size::Empty);
    if (size.Width < 75)
      size.Width= 75;
    button->Size= size;
    button->Visible= true;
    button->Enabled= true;
  }
}

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

#define MESSAGE_BOX_BUTTON_SPACING 8

/**
 * As the name already says this function computes the layout of the message box depending on the 
 * content (image size, button text etc.).
 */
void CustomMessageBox::ComputeLayout()
{
  SuspendLayout();

  // Buttons have a minimum width of 75 px. Make them all same size and hide those which don't have
  // any text.
  AdjustButton(_button1);
  AdjustButton(_button2);
  AdjustButton(_button3);

  int visibleButtonCount= 0;
  Drawing::Size size= Drawing::Size::Empty;
  if (_button1->Enabled)
  {
    size= _button1->Size;
    visibleButtonCount++;
  }
  if (_button2->Enabled)
  {
    size.Height= _button2->Height;
    if (_button2->Width > size.Width)
      size.Width= _button2->Width;
    visibleButtonCount++;
  }
  if (_button3->Enabled)
  {
    size.Height= _button3->Height;
    if (_button3->Width > size.Width)
      size.Width= _button3->Width;
    visibleButtonCount++;
  }

  // For the common height we use the one from the last visible button we found.
  // Since the font is assumed to be the same for all buttons they also should compute
  // the same height when asked for their preferred size.
  // Compute the total size of the button area on the way (including padding of the container).
  Drawing::Size buttonSize= Drawing::Size::Empty;
  if (size.Width > 0)
  {
    // Compute only if there is at least one visible button.
    buttonSize.Height= size.Height;
    if (_button1->Enabled)
    {
      _button1->Size= size;
      buttonSize.Width= size.Width;
    }
    if (_button2->Enabled)
    {
      _button2->Size= size;
      buttonSize.Width += size.Width + MESSAGE_BOX_BUTTON_SPACING;
    }
    if (_button3->Enabled)
    {
      _button3->Size= size;
      buttonSize.Width += size.Width + MESSAGE_BOX_BUTTON_SPACING;
    }
    buttonSize.Width += Padding.Right;
    buttonSize.Height += Padding.Bottom;
  }

  // Compute message text layout. Start with a minimum width of 200 pixels.
  int minWidth= (buttonSize.Width > 200) ? buttonSize.Width : 200;
  Drawing::Size proposedSize= Drawing::Size(minWidth, 1);
  
  // We use the golden ratio to create an appealing layout.
  // Increase width in 10% steps until we found the proper ratio.
  int lastWidth= 0;
  while (true)
  {
    size= _messageLabel->GetPreferredSize(proposedSize);

    // Terminate loop if we get a height of 0, the same width as in the last run, beyond a maximum width
    // or reached the golden ratio. Getting the same width means we never can get to the golden
    // ratio as the text is too small.
    if ((size.Height == 0) || (size.Width == lastWidth) || 
      (size.Width >= 700) || (size.Width / size.Height >= 1.618))
      break;

    lastWidth= size.Width;
    proposedSize.Width += proposedSize.Width / 10;
  };

  // GetPreferredSize might return a value smaller than our minimum width. Account for that.
  if (size.Width < minWidth)
    size.Width= minWidth;
  _messageLabel->Size= size;

  // Now that we have the text size compute the overall size of the message box.
  // The image is vertically centered at the left side (with some padding and margin)
  // and the buttons are at the bottom right.
  int textHeight= _messageLabel->Margin.Vertical + _messageLabel->Height;
  if (textHeight < _picture->Height + _picture->Margin.Vertical)
    textHeight= _picture->Height + _picture->Margin.Vertical;
  size.Width= Padding.Horizontal + _picture->Margin.Horizontal + _picture->Width +
    _messageLabel->Margin.Horizontal + _messageLabel->Width;
  size.Height= Padding.Vertical + textHeight + buttonSize.Height;
  ClientSize= size;

  // Move picture to its final location (center vertically over the message text's height).
  Point location;
  location.X= Padding.Left;
  location.Y= (textHeight - _picture->Height - _picture->Margin.Vertical) / 2;
  _picture->Location= location;

  // Text location too.
  location.X= _picture->Right + _messageLabel->Margin.Left;
  location.Y= Padding.Top;
  _messageLabel->Location= location;

  // Move the buttons to their final locations (aligned to the lower right corner of the message box).
  // If there is only one button center it horizontally, otherwise align buttons to the right side.
  if (buttonSize.Width > 0)
  {
    location= Point(ClientSize.Width - buttonSize.Width, ClientSize.Height - buttonSize.Height);
    if (visibleButtonCount == 1)
      location.X= (ClientSize.Width -buttonSize.Width) / 2;
    if (_button1->Enabled)
    {
      _button1->Location= location;
      location.X += _button1->Width + MESSAGE_BOX_BUTTON_SPACING;
    }
    if (_button2->Enabled)
    {
      _button2->Location= location;
      location.X += _button2->Width + MESSAGE_BOX_BUTTON_SPACING;
    }
    if (_button3->Enabled)
      _button3->Location= location;
  }

  ResumeLayout(true);
}

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

void CustomMessageBox::ButtonClick(Object^ sender, EventArgs^ arguments)
{
  if (sender == _button1)
    lastResult= mforms::ResultOk;
  else
    if (sender == _button2)
      lastResult= mforms::ResultCancel;
    else
      lastResult= mforms::ResultOther;
  this->Close();
}

//----------------- UtilitiesImpl ------------------------------------------------------------------

UtilitiesImpl::UtilitiesImpl()
{
}

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

int UtilitiesImpl::show_message(const string &title, const string &text, const string &ok, 
  const string &cancel, const string &other)
{
  String^ boxTitle= CppStringToNative(title);
  String^ boxText= CppStringToNative(text);
  String^ button1= CppStringToNative(ok);
  String^ button2= CppStringToNative(cancel);
  String^ button3= CppStringToNative(other);
  Image^ image= Image::FromFile("images/ui/message_confirm.png");
  return CustomMessageBox::Show(boxTitle, boxText, image, button1, button2, button3);
}

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

int UtilitiesImpl::show_error(const string &title, const string &text, const string &ok, 
  const string &cancel, const string &other)
{
  String^ boxTitle= CppStringToNative(title);
  String^ boxText= CppStringToNative(text);
  String^ button1= CppStringToNative(ok);
  String^ button2= CppStringToNative(cancel);
  String^ button3= CppStringToNative(other);
  Image^ image= Image::FromFile("images/ui/message_error.png");
  return CustomMessageBox::Show(boxTitle, boxText, image, button1, button2, button3);
}

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

int UtilitiesImpl::show_warning(const string &title, const string &text, const string &ok, 
  const string &cancel, const string &other)
{
  String^ boxTitle= CppStringToNative(title);
  String^ boxText= CppStringToNative(text);
  String^ button1= CppStringToNative(ok);
  String^ button2= CppStringToNative(cancel);
  String^ button3= CppStringToNative(other);
  Image^ image= Image::FromFile("images/ui/message_warning.png");
  return CustomMessageBox::Show(boxTitle, boxText, image, button1, button2, button3);
}

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

/**
 * Places the given string on the clipboard.
 * 
 * @param content The text to be placed on the clipboard. It is assume its encoding is UTF-8.
 */
void UtilitiesImpl::set_clipboard_text(const string &content)
{
  Clipboard::SetText(CppStringToNative(content));
}

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

/**
 * Returns the current text on the clipboard, if there is any.
 * 
 * @result If there is text on the clipboard (ANSI or Unicode) it is returned as UTF-8 string.
 * @note The returned text gets all CRLF Windows line breaks converted to pure LF.
 */
string UtilitiesImpl::get_clipboard_text()
{
  String^ unicode= (String^) Clipboard::GetData(DataFormats::UnicodeText);
  if (unicode == nullptr)
    return "";

  return NativeToCppString(unicode);
}

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

/**
 * Returns platform specific user folders, e.g. for the desktop, the user's documents etc.
 */
string UtilitiesImpl::get_special_folder(FolderType type)
{
  return NativeToCppString(Environment::GetFolderPath(Environment::SpecialFolder(type)));
}

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

void UtilitiesImpl::open_url(const string &url)
{
  try
  {
    System::Diagnostics::Process::Start(CppStringToNative(url));
  }
  catch (Exception ^e)
  {
    MessageBox::Show(e->Message->ToString(), "Error Opening Browser", 
      MessageBoxButtons::OK, MessageBoxIcon::Error, MessageBoxDefaultButton::Button1);
  }
}

