/*****************************************************************************\
*                                                                             *
*  Name   : visual_tree_node                                                  *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*******************************************************************************
* Copyright (c) 1998-$now By Author.  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; either version 2 of   *
* the License or (at your option) any later version.  This is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

#include "bitmap_tree.h"
#include "tree_window.h"
#include "visual_tree_node.h"

#include <basis/function.h>
#include <basis/istring.h>
#include <basis/utility.h>

using namespace nodes;

//#define DEBUG_VISUAL_TREE_NODE
  // uncomment for noisy version.  the output needs to be piped into a file
  // when programs under test are being run.

#undef LOG
#ifdef DEBUG_VISUAL_TREE_NODE
  #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
#else
  #define LOG(to_print) {}
#endif

visual_tree_node::visual_tree_node(tree_window &container,
    visual_tree_node *parent, visual_tree_node *predecessor,
    const istring &text, int image_index, int select_index)
: tree(),
  _trewink(container),
  _closure(OPEN),
  _visible(VISIBLE),
  _os_node(NIL),
  _image_index(image_index),
  _selected_index(select_index),
  _text(new istring(text))
{
  HTREEITEM parent_item = NIL;
  HTREEITEM after_item = NIL;
  if (parent) {
    // if there's a parent, we attach to it.
    parent_item = parent->_os_node;
    // only insert in order if they didn't specify a predecessor.
    if (predecessor) {
      // they specified a predecessor, so we force insertion in that order.
      int index = parent->which(predecessor);
      if (non_negative(index)) {
        // we add if the predecessor was really a child of the parent.
        parent->insert(index + 1, this);
        after_item = predecessor->os_node();
      } else {
        LOG("constructor's predecessor is bunk!!");
      }
    }
    if (!after_item) {
      // if there was a predecessor, we couldn't figure out the item to insert
      // after based on it.  or there was none, so we do our normal ordered
      // insertion.
      visual_tree_node *before = parent->attach(this);
      // if we got a non-NIL before node and no explicit predecessor, then we
      // use it to construct the visual tree.
      if (before) after_item = before->os_node();
      else after_item = (HTREEITEM)TVI_FIRST;
    } 
  } else {
    // with no parent, this item becomes the root.
    after_item = (HTREEITEM)TVI_ROOT;
  }
  if (!predecessor) {
    // if there was no predecessor and we haven't computed an alternative item
    // to add after, we add the node last.
    if (!after_item) after_item = (HTREEITEM)TVI_LAST;
  } else {
    // there was a predecessor and we add it unless we already computed a
    // different one.
    if (!after_item) after_item = predecessor->_os_node;
  }
  // argh, finally add the visual form of the node.
  _os_node = add_item(parent_item, text, after_item);
  LOG(istring("created node for ") + *_text);
}

visual_tree_node::~visual_tree_node()
{
  while (branches()) {
    visual_tree_node *to_whack = (visual_tree_node *)branch(0);
    LOG(istring("whacking child ") + to_whack->text());
    WHACK(to_whack);
  }
  if (_os_node) {
    // if we have a tree item, whack it.
    _trewink.tree_control().DeleteItem(_os_node);
    _os_node = NIL;
  }
  LOG(istring("destroyed node for ") + *_text);
  WHACK(_text);
}

void visual_tree_node::activate()
{
//hmmm: store state in closure.
//hmmm: catch OnExpanded to track closure.
}

void visual_tree_node::context_menu() {}

visual_tree_node *visual_tree_node::attach(visual_tree_node *new_branch)
{
  for (int i = 0; i < branches(); i++) {
    visual_tree_node *curr = (visual_tree_node *)branch(i);
    if (curr->_text->lower() > new_branch->_text->lower()) {
      insert(i, new_branch);
      return (visual_tree_node *)branch(i - 1);
    }
  }
  // nobody is less than this branch, so append it to the branches.
  tree::attach(new_branch);
  if (branches() > 1) return (visual_tree_node *)branch(branches() - 1);
  return NIL;
}

visual_tree_node *visual_tree_node::find_child(const istring &to_find)
{
  for (int i = 0; i < branches(); i++) {
    visual_tree_node *child = (visual_tree_node *)branch(i);
    if ( child->text().iequals(to_find) || child->_text->iequals(to_find) )
      return child;
  }
  return NIL;
}

void visual_tree_node::closure(closures new_state)
{
  _closure = new_state;
  if (_closure == OPEN)
    _trewink.tree_control().Expand(_os_node, TVE_EXPAND);
  else
    _trewink.tree_control().Expand(_os_node, TVE_COLLAPSE);
}

istring visual_tree_node::text() const { return *_text; }

void visual_tree_node::set_text(const istring &to_set)
{
  *_text = to_set;
  _trewink.tree_control().SetItemText(_os_node, text().s());
}

void visual_tree_node::visible(visibility new_state)
{
  _visible = new_state;
//hmmm: fix the visual form.
}

HTREEITEM visual_tree_node::add_item(HTREEITEM parent, const istring &text,
    HTREEITEM insert_after)
{
  // this structure contains the item data for the new node.
  TV_ITEM item_data;
  istring nc_text = text;
  item_data.pszText = nc_text.s();
  item_data.cchTextMax = nc_text.length();
  item_data.mask = TVIF_TEXT;  // set tag for the text, so far.

  // if the image is not our special junk tag, we set it in the mask.
  item_data.iImage = _image_index;
  if (non_negative(_image_index)) item_data.mask |= TVIF_IMAGE;
  // assume they have set selected image, but patch if not...
  item_data.iSelectedImage = negative(_selected_index) ?
      _image_index : _selected_index;
  if (non_negative(item_data.iSelectedImage))
    item_data.mask |= TVIF_SELECTEDIMAGE;

  // this structure records where to insert the new node.
  TV_INSERTSTRUCT where;
  where.item = item_data;
  where.hInsertAfter = insert_after;
  where.hParent = parent;
  HTREEITEM to_return = _trewink.tree_control().InsertItem(&where);
  _trewink.tree_control().SetItemData(to_return, (DWORD)this);
  if (this->parent() && (this->parent()->branches() == 1)) {
    // we're a new first branch.  expand the node.
    visual_tree_node *parent = (visual_tree_node *)this->parent();
    _trewink.tree_control().Expand(parent->os_node(), TVE_EXPAND);
  }
  return to_return;
}

void visual_tree_node::reset_images(int image_index, int selected_image_index)
{
  TVITEM info;
  info.mask = TVIF_HANDLE;
  info.hItem = _os_node;
  _trewink.tree_control().GetItem(&info);
  info.mask = 0;  // reset all status.
  info.iImage = image_index;
  if (non_negative(image_index)) info.mask |= TVIF_IMAGE;
  info.iSelectedImage = negative(selected_image_index) ?
      image_index : selected_image_index;
  if (non_negative(info.iSelectedImage)) info.mask |= TVIF_SELECTEDIMAGE;
  _trewink.tree_control().SetItem(&info);
}

