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

/**
 * Implementation of the selector (combobox) Windows mforms wrapper.
 */

#include "stdafx.h"

#include "mforms/mforms.h"
#include "wf_view.h"
#include "wf_selector.h"

#include "ConvUtils.h"

using namespace MySQL::Forms;

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

SelectorImpl::SelectorImpl(mforms::Selector *self)
  : ViewImpl(self)
{
}

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

bool SelectorImpl::create(mforms::Selector *self, mforms::SelectorStyle style)
{
  SelectorImpl ^selector= gcnew SelectorImpl(self);

  if (selector != nullptr)
  {
    ComboBox ^combo= ViewImpl::create<ComboBox>(self, selector);
    switch (style)
    {
      case mforms::SelectorPopup:
        combo->DropDownStyle= ComboBoxStyle::DropDownList;
        combo->SelectedIndexChanged += gcnew System::EventHandler(&SelectorImpl::selection_changed);
        break;
      default:
        combo->DropDownStyle= ComboBoxStyle::DropDown;
        combo->TextChanged += gcnew System::EventHandler(&SelectorImpl::text_changed);
        break;
    }

    
    combo->MinimumSize= Size(50, combo->PreferredSize.Height);
    combo->DropDownHeight= 100;
    combo->DrawMode= DrawMode::OwnerDrawVariable;
    combo->MeasureItem += gcnew MeasureItemEventHandler(measure_item);
    combo->DrawItem += gcnew DrawItemEventHandler(draw_item);
    return true;
  }
  return false;
}

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

/**
 * Computes the width of the widest entry in the given combobox. Can be used to resize the combobox
 * or only its dropdown list.
 */
int compute_content_width(ComboBox^ box)
{
  int width= 0;
  Graphics^ g= box->CreateGraphics();
  Font^ font= box->Font;
  int vertScrollBarWidth= (box->Items->Count > box->MaxDropDownItems) ? SystemInformation::VerticalScrollBarWidth : 0;

  int newWidth;
  for each (Object^ item in box->Items)
  {
    if (item->GetType() == SeparatorItem::typeid)
      newWidth= 30;
    else
      newWidth = (int) g->MeasureString((String^) item, font).Width + vertScrollBarWidth;
    if (width < newWidth)
      width= newWidth;
  }
  return width;
}

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

const int separatorHeight= 5;
const int verticalItemPadding= 4;

void SelectorImpl::measure_item(Object^ sender, MeasureItemEventArgs^ args)
{
  // Fetch the current item we’re painting as specified by the index.
  ComboBox^ box= (ComboBox^) sender;
  Object^ comboBoxItem= box->Items[args->Index];

  // If we are a separator item, add in room for the separator.
  if (comboBoxItem->GetType() == SeparatorItem::typeid)
  {
    args->ItemHeight = separatorHeight;
    args->ItemWidth= 50;
  }
  else
  {
    // Measure the text of the item;
    String^ s= comboBoxItem->ToString();
    if (s == "")
      s= "Ag";
    Size textSize= args->Graphics->MeasureString(s, box->Font).ToSize();
    args->ItemHeight= textSize.Height + verticalItemPadding;
    args->ItemWidth= textSize.Width;
  }
}

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

void SelectorImpl::draw_item(Object^ sender, DrawItemEventArgs^ args)
{
  if (args->Index == -1) 
    return;
  else
  {
    ComboBox^ box= (ComboBox^) sender;
    Object^ comboBoxItem = box->Items[args->Index];

    bool isSeparatorItem= (comboBoxItem->GetType() == SeparatorItem::typeid);

    System::Drawing::Rectangle bounds= args->Bounds;

    // Let the box handle the default stuff.
    args->DrawBackground();
    args->DrawFocusRectangle();

    if (isSeparatorItem)
    {
      // Start with the background.
      Brush^ b = gcnew SolidBrush(box->BackColor);
      args->Graphics->FillRectangle(b, bounds);

      // Now the gradient line.
      System::Drawing::Drawing2D::LinearGradientBrush gradientBrush(
        Point(0, 1),
        Point(bounds.Width, 1),
        Color::FromArgb(255, 66, 111, 166),
        SystemColors::Window);

      Pen pen(%gradientBrush);

      int center= (bounds.Bottom + bounds.Top) / 2;
      args->Graphics->DrawLine(%pen, bounds.Left + 2, center, bounds.Right - 2, center);
    }
    else
    {
      // Draw the string vertically centered but on the left.
      Brush^ textBrush= gcnew SolidBrush(args->ForeColor);
      StringFormat^ format= gcnew StringFormat();
      format->LineAlignment= StringAlignment::Center;
      format->Alignment= StringAlignment::Near;
      format->FormatFlags= StringFormatFlags::NoWrap;

      args->Graphics->DrawString(comboBoxItem->ToString(), box->Font, textBrush, bounds, format);
    }
  }
}

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

void SelectorImpl::text_changed(System::Object ^sender, System::EventArgs ^e)
{
  Windows::Forms::ComboBox^ combo= (Windows::Forms::ComboBox^)sender;

  if (combo->Tag != nullptr)
  {
    mforms::Selector* sel= ViewImpl::get_backend_control<mforms::Selector>(combo);
    if (sel != NULL)
      sel->callback();
  }
}

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

void SelectorImpl::selection_changed(System::Object ^sender, System::EventArgs ^e)
{
  Windows::Forms::ComboBox^ combo= (Windows::Forms::ComboBox^)sender;

  if (combo->Tag != nullptr)
  {
    mforms::Selector* sel= ViewImpl::get_backend_control<mforms::Selector>(combo);
    if (sel != NULL)
      sel->callback();
  }
}

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


void SelectorImpl::clear(mforms::Selector *self)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
    selector->get_control<ComboBox>()->Items->Clear();
}

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

int SelectorImpl::add_item(mforms::Selector *self, const std::string &item)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
    ComboBox^ combobox= selector->get_control<ComboBox>();
    int i;
    if (item == "-")
      i = combobox->Items->Add(gcnew SeparatorItem(""));
    else
      i = combobox->Items->Add(CppStringToNativeRaw(item));
    if (i == 0) // select 1st item by default, instead of -1
      combobox->SelectedIndex = 0;
    return i;
  }
  return 0;
}

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

void SelectorImpl::add_items(mforms::Selector *self, const std::list<std::string> &items)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
    cli::array<Object^> ^strings= gcnew cli::array<Object^>(items.size());
    int i= 0;

    for (std::list<std::string>::const_iterator iter= items.begin(); iter != items.end(); ++iter)
    {
      if (*iter == "-")
        strings[i++]= gcnew SeparatorItem("");
      else
        strings[i++]= CppStringToNativeRaw(*iter);
    }

    ComboBox^ combobox= selector->get_control<ComboBox>();
    combobox->Items->AddRange(strings);

    if (i > 0)
      combobox->SelectedIndex = 0;

    int width= compute_content_width(combobox);

    // Do some sanity checks.
    if (width < 50)
      width= 50;
    else
      if (width > 500)
        width= 500;
    combobox->Width= width + 20; // Account for the dropdown arrow.
    combobox->MinimumSize= Size(width + 20, combobox->PreferredSize.Height);
  }
}

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

std::string SelectorImpl::get_text(mforms::Selector *self)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
    return NativeToCppStringRaw(selector->get_control<ComboBox>()->Text);
  return "";
}

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

std::string SelectorImpl::get_item(mforms::Selector *self, int index)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
      return NativeToCppStringRaw((String^)selector->get_control<ComboBox>()->Items[index]);
  }
  return "";
}

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

void SelectorImpl::set_index(mforms::Selector *self, int index)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr && selector->get_control<ComboBox>()->Items->Count > index)
  {
    selector->get_control<ComboBox>()->SelectedIndex= index;
  }
}

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

int SelectorImpl::get_index(mforms::Selector *self)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
    return selector->get_control<ComboBox>()->SelectedIndex;
  }
  return -1;
}

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

int SelectorImpl::get_item_count(mforms::Selector *self)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
    return selector->get_control<ComboBox>()->Items->Count;
  }
  return -1;
}

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

void SelectorImpl::set_value(mforms::Selector *self, const std::string& value)
{
  SelectorImpl^ selector= (SelectorImpl^)ObjectImpl::FromUnmanaged(self);

  if (selector != nullptr)
  {
    return selector->get_control<ComboBox>()->Text= CppStringToNativeRaw(value);
  }
}

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

