#include <colors.h>
#include <image.h>
#include <tree.h>
#include <urldefid.h>

///////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////

HIDDEN bool callback_compare_node(TTree& node, void* jolly, word when)
{
  const TString& id = *((TString*)jolly);
  TString cur_id; node.curr_id(cur_id);
  return id == cur_id;
}

HIDDEN bool callback_expand_node(TTree& node, void* jolly, word when)
{
  if (when == SCAN_PRE_ORDER)
    node.expand();
  return false;
}

HIDDEN bool callback_find_father(TTree& node, void* jolly, word when)
{
  TString_array& father_and_son = *(TString_array*)jolly;
  TString myself; node.curr_id(myself);

  if (when == SCAN_PRE_ORDER)
  {
    if (myself == father_and_son.row(1))
      father_and_son.add(myself, 0);
  }
  else
  {
    if (father_and_son.objptr(0))
    {
      father_and_son.add(myself, 0);
      return true;
    }
  }

  return false;
}

HIDDEN bool callback_find_brother(TTree& node, void* jolly, word when)
{
  TString_array& brother_and_sister = *(TString_array*)jolly;
  TString myself; node.curr_id(myself);

  if (when == SCAN_PRE_ORDER)
  {
    if (myself == brother_and_sister.row(1))
      brother_and_sister.add(myself, 0);
  }
  else
  {
    if (brother_and_sister.objptr(0))
    {
      brother_and_sister.add(myself, 0);
      return true;
    }
  }
  return false;
}

///////////////////////////////////////////////////////////
//  TTtree
///////////////////////////////////////////////////////////

bool TTree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags)
{
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;

  TString myself;
  curr_id(myself);

  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))
      return true;

    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_depth_first(nh, jolly, flags))
        return true;
      goto_node(myself);
    }

    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))
      return true;
  }

  if (goto_rbrother())
  {
    if (scan_depth_first(nh, jolly, flags))
      return true;
    goto_node(myself);
  }

  if (test_myself)
  {
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))
      return true;
  }

  return false;
}

bool TTree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags)
{
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;

  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))
      return true;
  }

  TString myself;
  curr_id(myself);

  if (goto_rbrother())
  {
    if (scan_breadth_first(nh, jolly, flags))
      return true;
    goto_node(myself);
  }

  if (test_myself)
  {
    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))
      return true;

    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_breadth_first(nh, jolly, flags))
        return true;
      goto_node(myself);
    }
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))
      return true;
  }

  return false;
}

bool TTree::has_root() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_root();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::has_father() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_father();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::goto_father()
{
  TString myself; curr_id(myself);
  bool ok = goto_root();
  if (ok)
  {
    TString_array father_and_son;
    father_and_son.add(myself, 1);
    ok = scan_breadth_first(callback_find_father, &father_and_son,
                            SCAN_PRE_ORDER | SCAN_IN_ORDER);
  }
  if (!ok)
    goto_node(myself);
  return ok;
}

bool TTree::has_lbrother() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_lbrother();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::goto_lbrother()
{
  TString myself; curr_id(myself);
  bool ok = goto_root();
  if (ok)
  {
    TString_array brother_and_sister;
    brother_and_sister.add(myself, 1);
    ok = scan_depth_first(callback_find_brother, &brother_and_sister,
                          SCAN_PRE_ORDER | SCAN_POST_ORDER);
  }
  if (!ok)
    goto_node(myself);
  return ok;
}

bool TTree::has_rbrother() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_rbrother();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::could_have_son() const
{ return has_son(); }

bool TTree::has_son() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_firstson();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::expanded() const
{
  TString str; curr_id(str);
  bool yes = _expanded.is_key(str);
  return yes;
}

bool TTree::expand()
{
  bool ok = enabled() && has_son();
  if (ok)
  {
    TString str; curr_id(str);
    ok = !_expanded.is_key(str);
    if (ok)
      _expanded.add(str);
  }
  return ok;
}

bool TTree::shrink()
{
  TString str; curr_id(str);
  bool ok = _expanded.is_key(str);
  if (ok)
    _expanded.remove(str);
  return ok;
}

bool TTree::expand_all()
{
  bool ok = goto_root();
  if (ok)
    scan_breadth_first(callback_expand_node, NULL);
  return ok;
}

bool TTree::shrink_all()
{
  _expanded.destroy();
  return goto_root();
}

TImage* TTree::get_res_image(short bmp_id) const
{
  TImage* bmp = (TImage*)_image.objptr(bmp_id);
  if (bmp == NULL)
  {
    bmp = new TImage(bmp_id);
    if (bmp->ok())
    {
      bmp->convert_transparent_color(NORMAL_BACK_COLOR);
      ((TTree*)this)->_image.add(bmp, bmp_id);
    }
    else
    {
      delete bmp;
      bmp = NULL;
    }
  }
  return bmp;
}

TImage* TTree::get_res_icon(short icon_id) const
{
  const int id = 100000 + icon_id;
  TImage* bmp = (TImage*)_image.objptr(id);
  if (bmp == NULL)
  {
    TImage ico(icon_id, true);
    if (ico.ok())
    {
      const TImage* def = get_res_image(BMP_FILE);
      int w = 16, h = 16;
      if (def != NULL) 
      {
        w = def->width();
        h = def->height();
      }
      ico.convert_transparent_color(NORMAL_BACK_COLOR);
      bmp = new TImage(ico, w, h);
      ((TTree*)this)->_image.add(bmp, id);
    }
  }
  return bmp;
}

TImage* TTree::image(bool selected) const
{
  short bmp_id = BMP_FILE;
  if (could_have_son())
    bmp_id = selected ? BMP_DIRDN : BMP_DIR;
  return get_res_image(bmp_id);
}

///////////////////////////////////////////////////////////
//  TBidirectional_tree
///////////////////////////////////////////////////////////

bool TBidirectional_tree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags)
{
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;

  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))
      return true;

    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_depth_first(nh, jolly, flags))
        return true;
      goto_father();
    }

    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))
      return true;
  }

  if (goto_rbrother())
  {
    if (scan_depth_first(nh, jolly, flags))
      return true;
    goto_lbrother();
  }

  if (test_myself)
  {
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))
      return true;
  }

  return false;
}

bool TBidirectional_tree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags)
{
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;

  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))
      return true;
  }

  if (goto_rbrother())
  {
    if (scan_breadth_first(nh, jolly, flags))
      return true;
    goto_lbrother();
  }

  if (test_myself)
  {
    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))
      return true;
    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_breadth_first(nh, jolly, flags))
        return true;
      goto_father();
    }
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))
      return true;
  }

  return false;
}

bool TBidirectional_tree::goto_node(const TString &id)
{
  bool ok = goto_root();
  if (ok && id.not_empty())
    ok = scan_breadth_first(callback_compare_node, (void *)&id);
  return ok;
}

///////////////////////////////////////////////////////////
// TObject tree
///////////////////////////////////////////////////////////

bool TObject_tree::expanded() const
{
  bool ok = _current && _current->_expanded;
  return ok;
}

bool TObject_tree::expand()
{
  bool ok = _current && _current->_son && !_current->_expanded && enabled();
  if (ok)
    _current->_expanded = true;
  return ok;
}

bool TObject_tree::shrink()
{
  bool ok = _current && _current->_expanded;
  if (ok)
    _current->_expanded = false;
  return ok;
}

void TObject_tree::node2id(const TObject* node, TString& id) const
{
  id.format("%p", (const void*)node);
}

bool TObject_tree::goto_node(const TString& node)
{
  void* p = NULL;
  if (node.not_empty())
  {
#ifdef DBG_TREE
    if (!_expanded.is_key(node)) // Usa l'assoc array per testare i nodi validi!
    {
      NFCHECK("Invalid node: %s", (const char*)node);
      return goto_root();
    }
#endif
    sscanf(node, "%p", &p);
  }

  bool ok = true;
  if (p == NULL)
    ok = goto_root();
  else
    _current = (TTree_node*)p;
  return ok;
}

bool TObject_tree::goto_root()
{
  _current = _root;
  return _root != NULL;
}

bool TObject_tree::goto_firstson()
{
  TTree_node* n = _current ? _current->_son : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_rbrother()
{
  TTree_node* n = _current ? _current->_rbrother : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_father()
{
  TTree_node* n = _current ? _current->_father : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_lbrother()
{
  TTree_node* n = _current ? _current->_lbrother : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::set_object(TObject* obj)
{
  if (_current)
  {
#ifdef DBG_TREE
    TString str; curr_id(str);
    _expanded.add(str); // Usa l'assoc array per memorizzare i nodi validi!
#endif
    if (_current->_obj)
      delete _current->_obj;
    _current->_obj = obj;
  }
  return _current != NULL;
}

bool TObject_tree::set_object(const TObject& obj)
{
  bool ok = false;
  if (_current)
  {
    TObject* ptr = obj.dup();
    ok = set_object(ptr);
    if (!ok)
      delete ptr;
  }
  return ok;
}

bool TObject_tree::create_root()
{
  if (!has_root())
    _root = _current = new TTree_node;
  return goto_root();
}

bool TObject_tree::add_son(TObject* obj)
{
  bool ok = false;
  if (_root)
  {
    TTree_node*& curson = _current->_son;
    TTree_node* newson = new TTree_node;
    newson->_father = _current;
    if (curson != NULL)
    {
      curson->_lbrother = newson;
      newson->_rbrother = curson;
    }
    curson = newson;
    ok = goto_firstson();
  }
  else
    ok = create_root();

  if (ok)
    ok = set_object(obj);
  return ok;
}

bool TObject_tree::add_son(const TObject& obj)
{
  TObject* ptr = obj.dup();
  bool ok = add_son(ptr);
  if (!ok) delete ptr;
  return ok;
}

bool TObject_tree::add_brother(TObject* obj)
{
  bool ok = false;
  if (goto_father())
    ok = add_son(obj);
  else
    ok = create_root() && set_object(obj);
  return ok;
}

bool TObject_tree::add_brother(const TObject& obj)
{
  TObject* ptr = obj.dup();
  bool ok = add_brother(ptr);
  if (!ok) delete ptr;
  return ok;
}

bool TObject_tree::add_rbrother(TObject* obj)
{
  bool ok = false;
  if (has_father())
  {
    TTree_node* newbrother = new TTree_node;
    newbrother->_father    = _current->_father;
    newbrother->_lbrother  = _current;
    newbrother->_rbrother  = _current->_rbrother;
    _current->_rbrother    = newbrother;
    ok = goto_rbrother();
  }
  else
    ok = create_root();

  if (ok)
    set_object(obj);

  return ok;
}

bool TObject_tree::add_rbrother(const TObject& obj)
{
  TObject* ptr = obj.dup();
  bool ok = add_rbrother(ptr);
  if (!ok) delete ptr;
  return ok;
}

bool TObject_tree::add_lbrother(TObject* obj)
{
  bool ok = false;
  if (has_father())
  {
    if (_current->_lbrother == NULL)
      ok = add_brother(obj);
    else
    {
      TTree_node* newbrother = new TTree_node;
      newbrother->_father    = _current->_father;
      newbrother->_rbrother  = _current;
      newbrother->_lbrother  = _current->_lbrother;
      _current->_lbrother    = newbrother;
      ok = goto_lbrother();
    }
  }
  else
    ok = create_root();
  if (ok)
    set_object(obj);
  return ok;
}

bool TObject_tree::add_lbrother(const TObject& obj)
{
  TObject* ptr = obj.dup();
  bool ok = add_lbrother(ptr);
  if (!ok) delete ptr;
  return ok;
}

bool TObject_tree::kill_node()
{
  bool ok = false;
  if (_current)
  {
    TTree_node* cur = _current;
    while (goto_firstson())
    {
      kill_node();
      _current = cur;
    }
    if (_current->_lbrother)
    {
      _current->_lbrother->_rbrother = _current->_rbrother;
    }
    else
    {
      if (_current->_father)
        _current->_father->_son = _current->_rbrother;
    }
    if (_current->_rbrother)
      _current->_rbrother->_lbrother = _current->_lbrother;

#ifdef DBG_TREE
    TString id; curr_id(id);
    _expanded.remove(id);
#endif

    TTree_node* to_be_killed = _current;

    if (_current == _root)
       _root = NULL;

    if (_current->_rbrother)
      _current = _current->_rbrother;
    else
    {
      if (_current->_lbrother)
        _current = _current->_lbrother;
      else
        _current = _current->_father;
    }

    delete to_be_killed;

    ok = true;
  }
  return ok;
}

bool TObject_tree::get_description(TString& str) const
{
  TObject* obj = curr_node();
  if (obj)
    str << *obj;
  return obj != NULL;
}

TObject_tree::TObject_tree()
{
  _root = _current = NULL;
}

TObject_tree::~TObject_tree()
{
  if (goto_root())
    kill_node();
}

///////////////////////////////////////////////////////////
// TString_tree
///////////////////////////////////////////////////////////

bool TString_tree::get_description(TString& str) const
{
  const TString* obj = (const TString*)curr_node();
  if (obj)
    str = *obj;
  else
    str.cut(0);
  return obj != NULL;
}