/* 
 * © 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.
 */
/**
 * Implementation of the treeview wrapper for the mforms library. We use a third-party tree view control
 * here (TreeViewAdv) which supports columns.
 */

#include "stdafx.h"
#include "wf_treeview.h"

using namespace MySQL;
using namespace MySQL::Forms;

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

MySQL::Forms::TreeNode::TreeNode()
{
}

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

/**
 * Returns the text for a column given by index.
 *
 * @param index The index of the column for which the text is to be returned. If this index is not in the range of
 *              stored values then an empty string is returned (sparse list).
 * @return The requested string or "" if the index is out of range.
 */
String^ MySQL::Forms::TreeNode::GetText(int index)
{
  if (index < 0 || index >= _text.Count)
    return "";
  return _text[index] == nullptr ? "" : _text[index];
}

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

/**
 * Stores the given text at the given index for later retrieval.
 *
 * @param index The index at which to store the text (corresponds to a column in the tree). Must be >= 0. 
 *              The index can larger than the current count in the list. If so a number of empty entries are added.
 */
void MySQL::Forms::TreeNode::SetText(int index, String^ text)
{
  while (index >= _text.Count)
    _text.Add(nullptr);
  _text[index]= text;
}

//----------------- TreeViewImpl static interface functions ----------------------------------------

bool TreeViewImpl::create(::mforms::TreeView *self, ::mforms::TreeOptions options)
{
  TreeViewImpl ^treeview= gcnew TreeViewImpl(self);

  if (treeview != nullptr)
  {
    TreeViewAdv^ t= ViewImpl::create<TreeViewAdv>(self, treeview);
    t->Model= treeview->_model;
    t->SelectionChanged += gcnew System::EventHandler(&TreeViewImpl::SelectionChanged);
    t->UseColumns= options & ::mforms::TreeNoColumns ? false : true;
    t->ShowLines= options & ::mforms::TreeShowLines ? true : false;
    t->ShowPlusMinus= options & ::mforms::TreeShowPlusMinus ? true : false;
    return true;
  }
  return false;
}

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

int TreeViewImpl::add_column(::mforms::TreeView *self, ::mforms::TreeColumnType type, const string &name, 
                             int initial_width, bool editable)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->add_column(type, name, initial_width, editable);
  return -1;
}

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

void TreeViewImpl::end_columns(::mforms::TreeView *self)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->end_columns();
}

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

void TreeViewImpl::clear_rows(::mforms::TreeView *self)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->clear_rows();
}

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

void TreeViewImpl::delete_row(::mforms::TreeView *self, int row)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->delete_row(row);
}

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

int TreeViewImpl::add_row(::mforms::TreeView *self)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->add_row();
  return -1;
}

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

int TreeViewImpl::get_selected(::mforms::TreeView *self)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->get_selected();
  return -1;
}

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

void TreeViewImpl::set_selected(::mforms::TreeView *self, const int idx)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->set_selected(idx);
}

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

int TreeViewImpl::count(::mforms::TreeView *self)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->count();
  return 0;
}

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

void TreeViewImpl::set_row_tag(::mforms::TreeView *self, int row, const string &value)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->set_row_tag(row, value);
}

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

void TreeViewImpl::set_string(::mforms::TreeView *self, int row, int column, const string &value)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->set_string(row, column, value);
}

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

void TreeViewImpl::set_int(::mforms::TreeView *self, int row, int column, int value)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->set_int(row, column, value);
}

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

void TreeViewImpl::set_check(::mforms::TreeView *self, int row, int column, bool value)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    treeview->set_check(row, column, value);
}

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

string TreeViewImpl::get_row_tag(::mforms::TreeView *self, int row)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->get_row_tag(row);
  return "";
}

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

string TreeViewImpl::get_string(::mforms::TreeView *self, int row, int column)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->get_string(row, column);
  return "";
}

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

int TreeViewImpl::get_int(::mforms::TreeView *self, int row, int column)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->get_int(row, column);
  return -1;
}

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

bool TreeViewImpl::get_check(::mforms::TreeView *self, int row, int column)
{
  TreeViewImpl^ treeview= (TreeViewImpl^)ObjectImpl::FromUnmanaged(self);
  if (treeview != nullptr)
    return treeview->get_check(row, column);
  return false;
}

//----------------- Actual implementation ----------------------------------------------------------

TreeViewImpl::TreeViewImpl(::mforms::TreeView *self)
  : ViewImpl(self)
{
  _model= gcnew Aga::Controls::Tree::TreeModel();
}

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

int TreeViewImpl::add_column(::mforms::TreeColumnType type, const string &name, int initial_width, bool editable)
{
  TreeViewAdv^ treeview= get_control<TreeViewAdv>();
  
  BindableControl^ nodeControl;
  switch (type)
  {
  case ::mforms::CheckColumnType:
    nodeControl= gcnew NodeCheckBox();
    break;
  case ::mforms::IntegerColumnType:
    {
      NodeTextBox^ box= gcnew NodeTextBox(); // Does an integer column work differently than a normal text column (editor-wise)?
      box->EditEnabled= editable;
      nodeControl= box;
      break;
    }
  default: // ::mforms::StringColumnType
    {
      NodeTextBox^ box= gcnew NodeTextBox();
      box->EditEnabled= editable;
      nodeControl= box;
    }
  };

  TreeColumn^ column= gcnew TreeColumn(CppStringToNative(name), (initial_width < 0) ? 50 : initial_width);
  treeview->Columns->Add(column);

  nodeControl->VirtualMode= true; // Means: get value for that column via event.
  nodeControl->ValueNeeded += gcnew System::EventHandler<NodeControlValueEventArgs^>(TreeValueNeeded);
  nodeControl->ValuePushed += gcnew System::EventHandler<NodeControlValueEventArgs^>(TreeValuePushed);
  nodeControl->LeftMargin= 3;
  nodeControl->ParentColumn= column;
  treeview->NodeControls->Add(nodeControl);

  return column->Index;
}

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

/**
 * Returns the text for the given node control (passed in as sender, representing the column) and for
 * the given node (passed in the event arguments).
 */
void TreeViewImpl::TreeValueNeeded(System::Object^ sender, NodeControlValueEventArgs^ e)
{
  BindableControl^ control= (BindableControl^) sender;
  TreeNodeAdv^ treeNode= e->Node;
  TreeNode^ ourNode= (TreeNode^) treeNode->Tag; // Implicit knowledge, what a design...
  
  // The passed in TreeNodeAdv can be the hidden root node, so better check it.
  if (ourNode != nullptr)
  {
    String^ value= ourNode->GetText(control->ParentColumn->Index);
    if (control->GetType() == NodeCheckBox::typeid)
      e->Value= value == "1" ? true : false;
    e->Value= value;
  }
}

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

/**
 * Sets new text for the given node control (passed in as sender, representing the column) and for
 * the given node (passed in the event arguments).
 */
void TreeViewImpl::TreeValuePushed(System::Object^ sender, NodeControlValueEventArgs^ e)
{
  BindableControl^ control= (BindableControl^) sender;
  TreeNodeAdv^ treeNode= e->Node;
  TreeNode^ ourNode= (TreeNode^) treeNode->Tag;

  if (ourNode != nullptr)
    ourNode->SetText(control->ParentColumn->Index, e->Value->ToString());
}

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

void TreeViewImpl::SelectionChanged(System::Object^ sender, EventArgs^ e)
{
   TreeViewAdv^ tree= (TreeViewAdv^)sender;

  if (tree->Tag != nullptr)
  {
    ::mforms::TreeView* t= ViewImpl::get_backend_control<::mforms::TreeView>(tree);
    if (t != 0)
      t->emit_changed();
  }
}

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

void TreeViewImpl::end_columns()
{
}

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

void TreeViewImpl::clear_rows()
{
  _model->Nodes->Clear();
}

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

void TreeViewImpl::delete_row(int row)
{
  _model->Nodes->RemoveAt(row);
}

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

int TreeViewImpl::add_row()
{
  Node^ node= gcnew TreeNode();
  _model->Nodes->Add(node);
  return _model->Nodes->Count - 1;
}

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

int TreeViewImpl::get_selected()
{
  TreeNodeAdv^ node= get_control<TreeViewAdv>()->CurrentNode;
  if (node == nullptr)
    return -1;
  return node->Index;
}

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

void TreeViewImpl::set_selected(const int idx)
{
  TreeViewAdv^ tree= get_control<TreeViewAdv>();
  Node^ node= _model->Nodes[idx];
  // TODO: CurrentNode is not accessible, find another way.
  //get_control<TreeViewAdv>()->SelectedNode= ; how to get TreeNodeAdv from Node?
  //get_control<TreeViewAdv>()->CurrentNode= _model->Nodes[idx];
}

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

int TreeViewImpl::count()
{
  return _model->Nodes->Count;
}

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

void TreeViewImpl::set_row_tag(int row, const string &value)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  node->tag= CppStringToNative(value);
}

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

void TreeViewImpl::set_string(int row, int column, const string &value)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  node->SetText(column, CppStringToNative(value));

  get_control<TreeViewAdv>()->Invalidate(); // Might lead to flickering, investigate if necessary.
}

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

void TreeViewImpl::set_int(int row, int column, int value)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  node->SetText(column, Convert::ToString(value));

  get_control<TreeViewAdv>()->Invalidate();
}

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

void TreeViewImpl::set_check(int row, int column, bool value)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  node->SetText(column , value ? "1" : "0");

  get_control<TreeViewAdv>()->Invalidate();
}

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

string TreeViewImpl::get_row_tag(int row)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  return NativeToCppString(node->tag);
}

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

string TreeViewImpl::get_string(int row, int column)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  return NativeToCppString(node->GetText(column));
}

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

int TreeViewImpl::get_int(int row, int column)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  return Convert::ToInt32(node->GetText(column));
}

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

bool TreeViewImpl::get_check(int row, int column)
{
  MySQL::Forms::TreeNode^ node= (MySQL::Forms::TreeNode^) _model->Nodes[row];
  return node->GetText(column) == "1" ? true : false;
}

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

