/* 
 * Copyright (c) 2007, 2010, 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"

#ifndef _WIN32
#include <algorithm>
#endif

#include "stdafx.h"

#include <grtpp_util.h>
#include "grt_structs_tree.h"

using namespace grt;
using namespace bec;

StructsTreeBE::StructsTreeBE(GRT *grt)
  : _grt(grt)
{
  _mode= ByName;
}


static std::string format_type(MetaClass *meta, const MetaClass::Member *member)
{
  if (member)
    return fmt_type_spec(member->type);

  return "";
}




int StructsTreeBE::count_children(const NodeId &parent)
{
  Node *node= get_node_for_id(parent);
  
  if (node == NULL)
    return 0;

  return node->children.size();
}


NodeId StructsTreeBE::get_child(const NodeId &parent, int index)
{
  Node *node= get_node_for_id(parent);
  if ((node != NULL) && (index < (int)node->children.size()))
    return NodeId(parent).append(index);
  return NodeId();
}


bool StructsTreeBE::get_field(const NodeId &node, int column, std::string &value)
{
  Node *n= get_node_for_id(node);
  
  if (n == NULL)
    return false;
  
  switch (column)
  {
  case Name:
    value= n->name;
    return true;

  case Caption:
    switch (n->type)
    {
    case NPackage:
      value= "";
      break;
    case NStruct:
      value= n->gstruct->get_attribute("caption");
      break;
    case NMember:
      value= n->gstruct->get_member_attribute(n->name, "caption");
      break;
    case NFunction:
      value= "";
      break;
    case NSignal:
      value= "";
      break;
    }
    return true;

  case Type:
    switch (n->type)
    {
    case NPackage:
      value= "";
      break;
    case NStruct:
      value= "";
      break;
    case NMember:
      value= format_type(n->gstruct, n->gstruct->get_member_info(n->name));
      break;
    case NFunction:
      value= "";
      break;
    case NSignal:
      value= "";
      break;
    }
    return true;

  }
  return false;
}


Type StructsTreeBE::get_field_type(const NodeId &node, int column)
{
  return StringType;
}


std::string StructsTreeBE::get_field_description(const NodeId &node, int column)
{
  Node *n= get_node_for_id(node);
  
  if (n == NULL)
    return false;

  switch (n->type)
  {
  case NPackage:
    return "";

  case NStruct:
    return n->gstruct->get_attribute("desc");

  case NMember:
  case NFunction:
  case NSignal:
    return n->gstruct->get_member_attribute(n->name, "desc");
  }
  return "";
}


IconId StructsTreeBE::get_field_icon(const NodeId &node, int column, IconSize size)
{
  Node *n= get_node_for_id(node);
  const MetaClass::Member *mc;

  if (n == NULL)
    return 0;

  switch (n->type)
  {
  case NPackage:
    return IconManager::get_instance()->get_icon_id("");
  case NStruct:
    return IconManager::get_instance()->get_icon_id("grt_struct.png");
  case NMember:
    if ((mc= n->gstruct->get_member_info(n->name))!=0)
    {
      switch (mc->type.base.type)
      {
      case ListType:
        return IconManager::get_instance()->get_icon_id("grt_list.png");
      case DictType:
        return IconManager::get_instance()->get_icon_id("grt_dict.png");
      case ObjectType:
        return IconManager::get_instance()->get_icon_id("grt_object.png");
      default:
        return IconManager::get_instance()->get_icon_id("grt_simple_type.png");
      }
    }
    return IconManager::get_instance()->get_icon_id("grt_simple_type.png");
  case NFunction:
    return IconManager::get_instance()->get_icon_id("grt_function.png");
  case NSignal:
    return IconManager::get_instance()->get_icon_id("grt_object.png");
  }
  return 0;
}


bool StructsTreeBE::get_row(const NodeId &node,
                               IconId &icon,
                               std::string &name,
                               std::string &caption,
                               std::string &type)
{
  if (!node.is_valid())
    return false;

  Node *n= get_node_for_id(node);
  if (n == NULL)
    return false;

  icon= get_field_icon(node, 0, Icon16);

  switch (n->type)
  {
  case NPackage:
    name= n->name;
    caption= "";
    type= "";
    break;
  case NStruct:
    name= n->name;
    caption= n->gstruct->get_attribute("caption");
    type= "";
    break;
  case NMember:
    name= n->name;
    caption= n->gstruct->get_member_attribute(name, "caption");
    type= format_type(n->gstruct, n->gstruct->get_member_info(name));
    break;
  default:
    return false;
  }
  return true;
}


void StructsTreeBE::set_display_mode(DisplayMode mode)
{
  _mode= mode;

  refresh();
}


void StructsTreeBE::refresh()
{
  _root.name= "ROOT";
  std::for_each(_root.children.begin(), _root.children.end(), DeleteNode());
  _root.children.clear();

  switch (_mode)
  {
  case ByName:
    refresh_by_name();
    break;
  case ByHierarchy:
    refresh_by_hierarchy(_grt->get_metaclass(internal::Object::static_class_name()), &_root);
    break;
  case ByPackage:
    refresh_by_package();
    break;
  }
}


void StructsTreeBE::refresh_by_name()
{
  const std::list<MetaClass*> &metaclasses(_grt->get_metaclasses());
  
  for (std::list<MetaClass*>::const_iterator iter= metaclasses.begin(); 
       iter != metaclasses.end(); ++iter)
  {
    MetaClass *gstruct= *iter;

    Node *subnode= new Node(NStruct, gstruct->name(), gstruct);

    for (MetaClass::MethodList::const_iterator mem= gstruct->get_methods_partial().begin();
         mem != gstruct->get_methods_partial().end(); ++mem)
    {
      Node *mnode= new Node(NFunction, mem->second.name, gstruct);

      subnode->children.push_back(mnode);
    }

    for (MetaClass::MemberList::const_iterator mem= gstruct->get_members_partial().begin();
         mem != gstruct->get_members_partial().end(); ++mem)
    {
      Node *mnode= new Node(NMember, mem->second.name, gstruct);

      subnode->children.push_back(mnode);
    }
    std::sort(subnode->children.begin(), subnode->children.end(), NodeCompare(this));

    _root.children.push_back(subnode);
  }
  std::sort(_root.children.begin(), _root.children.end(), NodeCompare(this));
}


void StructsTreeBE::refresh_by_hierarchy(const MetaClass *parent, Node *parnode)
{
  const std::list<MetaClass*> &metaclasses(_grt->get_metaclasses());
  
  for (std::list<MetaClass*>::const_iterator iter= metaclasses.begin(); 
       iter != metaclasses.end(); ++iter)
  {
    if ((*iter)->parent() != parent)
      continue;

    Node *subnode= new Node(NStruct, (*iter)->name(), *iter);

    // add methods of the node
    for (MetaClass::MethodList::const_iterator mem= (*iter)->get_methods_partial().begin();
         mem != (*iter)->get_methods_partial().end(); ++mem)
    {
      Node *mnode= new Node(NFunction, mem->second.name, (*iter));

      subnode->children.push_back(mnode);
    }
    
    // add members of the node
    for (MetaClass::MemberList::const_iterator mem= (*iter)->get_members_partial().begin();
         mem != (*iter)->get_members_partial().end(); ++mem)
    {
      Node *mnode= new Node(NMember, mem->second.name, (*iter));

      subnode->children.push_back(mnode);
    }

    // add child structs
    refresh_by_hierarchy(*iter, subnode);

    parnode->children.push_back(subnode);
  }
  std::sort(parnode->children.begin(), parnode->children.end(), NodeCompare(this));
}


void StructsTreeBE::refresh_by_package()
{
  std::map<std::string, Node*> package_nodes;
  const std::list<MetaClass*> &metaclasses(_grt->get_metaclasses());
  
  for (std::list<MetaClass*>::const_iterator iter= metaclasses.begin(); 
       iter != metaclasses.end(); ++iter)
  {
    std::string pkgname= (*iter)->name();

    std::string::size_type p= pkgname.rfind('.');
    if (p != std::string::npos)
      pkgname= pkgname.substr(0, p);
    else
      pkgname= "";

    Node *pkgnode= package_nodes[pkgname];
    if (!pkgnode)
    {
      pkgnode= new Node(NPackage, pkgname);
      package_nodes[pkgname]= pkgnode;
      _root.children.push_back(pkgnode);
    }

    Node *node= new Node(NStruct, (*iter)->name(), *iter);
    pkgnode->children.push_back(node);

    // add methods of the node
    for (MetaClass::MethodList::const_iterator mem= (*iter)->get_methods_partial().begin();
         mem != (*iter)->get_methods_partial().end(); ++mem)
    {
      Node *mnode= new Node(NFunction, mem->second.name, *iter);

      node->children.push_back(mnode);
    }

    // add members of the node
    for (MetaClass::MemberList::const_iterator mem= (*iter)->get_members_partial().begin();
         mem != (*iter)->get_members_partial().end(); ++mem)
    {
      Node *mnode= new Node(NMember, mem->second.name, *iter);

      node->children.push_back(mnode);
    }
    std::sort(node->children.begin(), node->children.end(), NodeCompare(this));
  }
  
  for (std::map<std::string,Node*>::iterator iter= package_nodes.begin();
       iter != package_nodes.end(); ++iter)
  {
    std::sort(iter->second->children.begin(), iter->second->children.end(), NodeCompare(this));
  }

  // sort packages
  std::sort(_root.children.begin(), _root.children.end(), NodeCompare(this));
}


StructsTreeBE::Node *StructsTreeBE::get_node_for_id(const NodeId &id)
{  
  Node *node= &_root;

  for (int i= 0; i < get_node_depth(id); i++)
  {
    if ((int)node->children.size() <= id[i])
      return NULL;

    node= node->children[id[i]];
  }
  return node;
}
