/*
 * SceneTreeView.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * 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 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 (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include "stdafx.h"

#include "SceneTreeView.h"
#include "IconSize.h"

#include "swt.h"
#ifndef INT_MAX
# include <limits.h>
#endif

#include "Scene.h"
#include "DuneApp.h"
#include "Node.h"
#include "Proto.h"
#include "Field.h"
#include "MFNode.h"
#include "SFNode.h"
#include "Path.h"
#include "resource.h"

class TreeNode {
public:
                TreeNode (int f, Node *n)
                { field = f; node = n; }
    int         field;  // which field of parent is this node in
    Node       *node;  // the node, or NULL if a field
};

static void
treeCallback(void *data, int type, STREEITEM item)
{
    switch (type) {
      case SW_TREE_SELECT:
        ((SceneTreeView *) data)->OnSelectionChanged(item);
        break;
      case SW_TREE_BEGIN_DRAG:
        ((SceneTreeView *) data)->OnBeginDrag(item);
        break;
    }
}

SceneTreeView::SceneTreeView(Scene *scene, SWND parent)
  : SceneView(scene, parent)
{
    int width, height;

    swGetSize(parent, &width, &height);
    _tree = swCreateTree(0, 0, width, height, parent);
    swTreeSetClientData(_tree, this);
    swTreeSetCallback(_tree, treeCallback);
    int idb_node_icons = IDB_NODE_ICONS;
    if (TheApp->getBlackAndWhiteIcons())
        idb_node_icons = IDB_NODE_ICONS_BW;
    _bitmap = swLoadBitmap(parent, idb_node_icons);
    _bitmapItems = scene->getNumberBuildinProtos() + 4;
    _mask = swCreateMask(_bitmap, ICON_SIZE_X * _bitmapItems, ICON_SIZE_Y, 
                         0x808000);
    swTreeSetImageList(_tree, _bitmap, _mask, ICON_SIZE_X, ICON_SIZE_Y,
                       _bitmapItems);
    swTreeSetOverlayImage(_tree, _bitmapItems - 2);
    _currentDragSource = NULL;
    _currentDragParent = NULL;
    _currentDragField = -1;
    RegisterDropTarget();
}

SceneTreeView::~SceneTreeView()
{
    UnregisterDropTarget();
    swTreeSetCurrentItem(_tree, NULL);
    DeleteItemRec(swTreeGetRootItem(_tree));
    swDestroyTree(_tree);
    swDestroyBitmap(_bitmap);
    swDestroyBitmap(_mask);
}

void SceneTreeView::renameNode(STREEITEM item, Node *node)
{
    STREEITEM child;
    for (child = swTreeGetFirstChild(_tree,item); child != NULL;
         child = swTreeGetNextItem(_tree, child)) {
        TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
        if (cinfo->node == node) {
            if (TheApp->is4Catt())
                node->generateTreeLabel();
            const char *name;            
            if (node->hasName() || TheApp->is4Catt()) 
                name = node->getTreeLabel();
            else {
                bool x3d = _scene->isX3d() && !_scene->converts2VRML();
                x3d = x3d || _scene->converts2X3d();
                name = node->getProto()->getName(x3d);
            }
            swTreeSetItemName(_tree, child, name);
        }        
        renameNode(child, node);
    }        
}

void SceneTreeView::renameNode(STREEITEM item, RouteUpdate *routeUpdate)
{
    STREEITEM child;
    for (child = swTreeGetFirstChild(_tree,item); child != NULL;
         child = swTreeGetNextItem(_tree, child)) {
        TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
        if (cinfo->node == routeUpdate->src) {
            Node *node = routeUpdate->src;
            node->getName();
            swTreeSetItemName(_tree, child, (const char*)node->getTreeLabel());
        }
        if (cinfo->node == routeUpdate->dest) {
            Node *node = routeUpdate->dest;
            node->getName();
            swTreeSetItemName(_tree, child, (const char*)node->getTreeLabel());
        }
        renameNode(child,routeUpdate);
    }        
}


void SceneTreeView::OnUpdate(SceneView* sender, int type, Hint *hint) 
{
    Node *root = _scene->getRoot();
    NodeUpdate *nodeUpdate;
    RouteUpdate *routeUpdate;
    STREEITEM item;

    switch (type) {
      CASE_UPDATE(UPDATE_ADD_ROUTE)
        routeUpdate = (RouteUpdate *) hint;
        for (item = swTreeGetRootItem(_tree); item != NULL;
             item = swTreeGetNextItem(_tree, item)) 
            renameNode(item,routeUpdate);
        break;
      CASE_UPDATE(UPDATE_ALL)
        swTreeSetCurrentItem(_tree, NULL);
        DeleteItemRec(swTreeGetRootItem(_tree));
        _scene->getNodes()->clearFlag(NODE_FLAG_TOUCHED);
        if (root != NULL) InsertNodeRec(root, -1, SW_INSERT_ROOT, NULL);
        UpdateOverlay();
        // open always the first level of scenegraph on UPDATE_ALL
        item = swTreeGetFirstChild(_tree, swTreeGetRootItem(_tree));
        if (item) {
            swTreeSetCurrentItem(_tree, item);
            swTreeSetCurrentItem(_tree, swTreeGetRootItem(_tree));
        }
        UpdateSelection();
        break;
      CASE_UPDATE(UPDATE_SELECTION_NAME)
        UpdateNodeName(_scene->getSelection()->getNode());
        break;
      CASE_UPDATE(UPDATE_SELECTION)
        UpdateSelection();
        break;
      CASE_UPDATE(UPDATE_ADD_NODE)
        nodeUpdate = (NodeUpdate *) hint;
        UpdateAddNode(swTreeGetRootItem(_tree), nodeUpdate->node, 
                      nodeUpdate->parent, nodeUpdate->field);
        UpdateOverlay();
        break;
      CASE_UPDATE(UPDATE_REMOVE_NODE)
        nodeUpdate = (NodeUpdate *) hint;
        UpdateRemoveNode(swTreeGetRootItem(_tree), nodeUpdate->node,
                         nodeUpdate->parent, nodeUpdate->field, NULL);
        UpdateOverlay();
        break;
      CASE_UPDATE(UPDATE_SOLID_CHANGED)
      CASE_UPDATE(UPDATE_NODE_NAME)
        nodeUpdate = (NodeUpdate *) hint;
        UpdateNodeName(nodeUpdate->node);
        break;
    }
}

void SceneTreeView::OnSize(int width, int height)
{
    swSetSize(swTreeGetWindow(_tree), width, height);
}

bool SceneTreeView::UpdateAddNode(STREEITEM item, Node *node,
                                  Node *parent, int field)
{
    STREEITEM child, next, sib;
    bool found = false;

    TreeNode *info = (TreeNode *) swTreeGetItemData(_tree, item);
    if (info->node == parent) {
        int pos = parent->findChild(node, field);
        if (TheApp->GetShowAllFields() || parent->showFields()) {
            for (child = swTreeGetFirstChild(_tree, item); child != NULL;
                 child = swTreeGetNextItem(_tree, child)) {
                TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
                if (!cinfo->node && cinfo->field == field) {
                    item = child;
                    break;
                }
            }
        } else {
            for (child = swTreeGetFirstChild(_tree, item); child != NULL;
                 child = swTreeGetNextItem(_tree, child)) {
                TreeNode *cinfo = (TreeNode *) swTreeGetItemData(_tree, child);
                if (cinfo->field >= field) break;
                pos++;
            }
        }
        int vpos;
        if (pos == 0) {
            vpos = SW_INSERT_FIRST_CHILD;
            sib = item;
        } else {
            vpos = SW_INSERT_AFTER;
            sib = swTreeGetFirstChild(_tree, item);
            while (--pos > 0) {
                sib = swTreeGetNextItem(_tree, sib);
            }
        }
        item = InsertNodeRec(node, field, vpos, sib);
        swTreeDeselectAll(_tree);
        swTreeSetCurrentItem(_tree, item);
        swTreeSelectItem(_tree, item);
        found = true;
    } else {
        for (child = swTreeGetFirstChild(_tree, item); child != NULL;
             child = next) {
            next = swTreeGetNextItem(_tree, child);
            found = UpdateAddNode(child, node, parent, field);
        }
    }
    return found;
}

bool SceneTreeView::UpdateRemoveNode(STREEITEM item,
                                     Node *node, Node *parent, int field,
                                     Node *curParent)
{
    STREEITEM child, next;

    TreeNode *info = (TreeNode *) swTreeGetItemData(_tree, item);
    if (info->node == node && info->field == field && curParent == parent) {
        // remove a node only once (only one USE'd Node)
        DeleteItemRec(item);
        // Fixme: delete also nodes inside a USE'd Node
        return true;
    } else {
        if (info->node != NULL)
            curParent = info->node;
        for (child = swTreeGetFirstChild(_tree, item); child != NULL;
             child = next) {
            next = swTreeGetNextItem(_tree, child);
            if (UpdateRemoveNode(child, node, parent, field, curParent))
                return true;
        }
    }
    return false;
}

void SceneTreeView::DeleteItemRec(STREEITEM item)
{
    if (!item) return;

    STREEITEM child, next;

    for (child = swTreeGetFirstChild(_tree, item); child != NULL;
         child = next) {
        TreeNode *info = (TreeNode *) swTreeGetItemData(_tree, child);
        next = swTreeGetNextItem(_tree, child);
        DeleteItemRec(child);
    }

    delete (TreeNode *) swTreeGetItemData(_tree, item);
    swTreeDeleteItem(_tree, item);
}

void SceneTreeView::UpdateNode(Node *updateNode)
{
    const Path *updatePath = updateNode->getPath();
    Node *node = _scene->getRoot();
    STREEITEM item = swTreeGetRootItem(_tree);

    if (updatePath == NULL) return;

    int len = updatePath->getPathLen();

    const int *path = updatePath->getPath();

    for (int i = 0; i < len;) {
        int field = path[i++];
        FieldValue *value = NULL;
        if (field != INT_MAX) {
            if (node->isPROTO()) {
                FieldIndex fieldIndex(field);
                value = node->getField(fieldIndex);
            } else     
                value = node->getField(field);
        }
        if (TheApp->GetShowAllFields() || node->showFields()) {
            item = swTreeGetFirstChild(_tree, item);
            Proto *proto = node->getProto();
            if (item != NULL) {
                for (int j = 0; j < field; j++) {
                    if ((j == proto->metadata_Field()) && (!GetScene()->isX3d()))
                        continue;
                    FieldValue *value2 = NULL;
                    if (node->isPROTO()) {
                        FieldIndex fieldIndex2(j);
                        value2 = node->getField(fieldIndex2);
                    } else
                        value2 = node->getField(j);
                    if (value2 == NULL)
                        return;
                    if ((value2->getType() == SFNODE) || (value2->getType() == MFNODE))
                        item = swTreeGetNextItem(_tree, item);
                }
            }
        }
        if (value == NULL)
            return;
        if (i == len) break;
        int pos = path[i++];
        if (value->getType() == SFNODE) {
            node = ((SFNode *) value)->getValue();
            item = swTreeGetFirstChild(_tree, item);
        } else if (value->getType() == MFNODE) {
            node = ((MFNode *) value)->getValue(pos);
            item = swTreeGetFirstChild(_tree, item);
            while (pos--) item = swTreeGetNextItem(_tree, item);
        }
        for (;item != NULL; item = swTreeGetNextItem(_tree, item)) {
             TreeNode *t = (TreeNode *) swTreeGetItemData(_tree, item);
             if (t->field == field) break;
        }
    }
    if (item != NULL && item != swTreeGetCurrentItem(_tree)) {
        swTreeDeselectAll(_tree);
        swTreeSetCurrentItem(_tree, item);
        swTreeSelectItem(_tree, item);
    }
}

static bool updateNode(Node *node, void *data)
{
    SceneTreeView *treeView = (SceneTreeView *)data;
    treeView->UpdateNode(node);
    return true;
}

void SceneTreeView::UpdateSelection()
{
    Node *selection = _scene->getSelection()->getNode();
//    selection->doWithUses(updateNode, this, true, false);
    if (selection->isInsideProto())
        selection = selection->getNodePROTO();
    UpdateNode(selection);
}

void SceneTreeView::InsertNodeListRec(NodeList *list, int field, STREEITEM parent)
{
    if (list == NULL) return;
    for (int i = 0; i < list->size(); i++) {
        InsertNodeRec(list->get(i), field, SW_INSERT_LAST_CHILD, parent);
    }
}

void SceneTreeView::InsertChildren(STREEITEM item, Node *node)
{
    assert(node != NULL);

    STREEITEM fieldItem;
    Proto *def = node->getProto();
    bool showAllFields = TheApp->GetShowAllFields() || node->showFields();
    bool x3d = node->getScene()->isX3d();

    for (int i = 0; i < def->getNumFields(); i++) {
        Field *field = def->getField(i);
        if (_scene->isInvalidElement(field))
            continue;
        if (field->getType() == MFNODE) {
            MFNode *value = (MFNode *) node->getField(i);
            const char *name = (const char *) field->getName(x3d);
            if (showAllFields) {
                fieldItem = swTreeInsertItem(_tree, SW_INSERT_LAST_CHILD,
                                             item, name);
                swTreeSetItemData(_tree, fieldItem, new TreeNode(i, NULL));
                swTreeSetItemImage(_tree, fieldItem, _bitmapItems - 2, _bitmapItems - 2);
                InsertNodeListRec(value->getValues(), i, fieldItem);
            } else {
                InsertNodeListRec(value->getValues(), i, item);
            }
        } else if (field->getType() == SFNODE) {
            SFNode *value = (SFNode *) node->getField(i);
            const char *name = (const char *) field->getName(x3d);
            if (showAllFields) {
                fieldItem = swTreeInsertItem(_tree, SW_INSERT_LAST_CHILD,
                                             item, name);
                swTreeSetItemData(_tree, fieldItem, new TreeNode(i, NULL));
                swTreeSetItemImage(_tree, fieldItem, _bitmapItems - 2, 
                                   _bitmapItems - 2);
                InsertNodeRec(value->getValue(), i, SW_INSERT_LAST_CHILD,
                              fieldItem);
            } else {
                InsertNodeRec(value->getValue(), i, SW_INSERT_LAST_CHILD,
                              item);
            }
        }
    }
}

STREEITEM SceneTreeView::InsertNodeRec(Node *node, int field, int position,
                                       STREEITEM relative)
{
    STREEITEM item;
    const char *name;

    if (node == NULL) return NULL;
    if (node == _scene->getRoot()) {
        name = "Scene";
    } else if (node->hasName() || TheApp->is4Catt()) {
        name = node->getTreeLabel();
    } else {
        name = node->getProto()->getName(_scene->isX3d());
    }
    item = swTreeInsertItem(_tree, position, relative, name);

    int img = node->getType();
    if (node == _scene->getRoot())
        img = _bitmapItems - 1;
    swTreeSetItemImage(_tree, item, img, img);

    swTreeSetItemData(_tree, item, new TreeNode(field, node));
    swTreeSetItemCollapsed(_tree, item, node->isCollapsed());

    bool insert = true;
    // check for cyclic scenegraph node
    STREEITEM parentItem = NULL;
    if (node != _scene->getRoot())
        parentItem = swTreeGetParentItem(_tree, item);
    while (parentItem != NULL) {
        if (node == ((TreeNode *)swTreeGetItemData(_tree, parentItem))->node) {
            insert = false;
            break;
        }
        parentItem = swTreeGetParentItem(_tree, parentItem);
    }
    if (insert) {
        // not a simple cyclic scenegraph
        InsertChildren(item, node);
        node->setFlag(NODE_FLAG_TOUCHED);
    }

/*
    if (!node->getFlag(NODE_FLAG_TOUCHED)) {
        node->setFlag(NODE_FLAG_TOUCHED);
        InsertChildren(item, node);
    }
*/
    return item;
}

void SceneTreeView::OnSelectionChanged(STREEITEM item) 
{
    if (item) {
        Path *path = MakePath(item);
        _scene->setSelection(path);
        _scene->UpdateViews(this, UPDATE_SELECTION); // bug ?
        _scene->removeSelectedHandles();
    }
}

void SceneTreeView::OnBeginDrag(STREEITEM item) 
{
    TreeNode *treeNode = (TreeNode *) swTreeGetItemData(_tree, item);
    Node *node = treeNode->node;

    if (treeNode->node) {
        STREEITEM parentItem = swTreeGetParentItem(_tree, item);

        if (parentItem) {
            Node *parent = ((TreeNode *) swTreeGetItemData(_tree, parentItem))->node;

            if (!parent) {
                parentItem = swTreeGetParentItem(_tree, parentItem);
                parent = ((TreeNode *) swTreeGetItemData(_tree, parentItem))->node;
            }
            _currentDragSource = node;
            _currentDragParent = parent;
            _currentDragField = treeNode->field;
            swDragDrop(_wnd, SW_DRAG_MOVE | SW_DRAG_LINK | SW_DRAG_COPY,
                       _bitmap, _mask, ICON_SIZE_X * node->getType(), 0, 
                       ICON_SIZE_X, ICON_SIZE_Y);
        }
    }
}

int SceneTreeView::OnDragEnter(int x, int y, int modifiers)
{
    return OnDragOver(x, y, modifiers);
}

int SceneTreeView::OnDragOver(int x, int y, int modifiers) 
{
    int rc = 0;

    STREEITEM target = swTreeHitTest(_tree, x, y);
    if (target) {
        TreeNode *treeNode = (TreeNode *) swTreeGetItemData(_tree, target);
        Node *node = treeNode->node;
        int field = -1;
        if (!node) {
            // dragging onto a field, so we know what field
            field = treeNode->field;
            treeNode = (TreeNode *) swTreeGetItemData(_tree, 
                            swTreeGetParentItem(_tree, target));
            node = treeNode->node;
        }
        if (_currentDragSource) {
            rc = _scene->OnDragOver(_currentDragSource, _currentDragParent,
                                    _currentDragField, node, field, modifiers);
            if (rc != 0) {
                swTreeSelectDropTarget(_tree, target);
            } else {
                swTreeSelectDropTarget(_tree, NULL);
            }
        } else {
            // the data came from another app
            // eventually, get the actual data object through drag & drop
        }
    }
    return rc;
}

void SceneTreeView::OnDragLeave()
{
    swTreeSelectDropTarget(_tree, NULL);
}

int SceneTreeView::OnDrop(int x, int y, int effect) 
{
    int rc = 0;

    swTreeSelectDropTarget(_tree, NULL);
    STREEITEM target = swTreeHitTest(_tree, x, y);
    if (target) {
        TreeNode *treeNode = (TreeNode *) swTreeGetItemData(_tree, target);
        Node *node = treeNode->node;
        int field = -1;
        if (!node) {
            // dragging onto a field, so we know what field
            field = treeNode->field;
            treeNode = (TreeNode *) swTreeGetItemData(_tree, swTreeGetParentItem(_tree, target));
            node = treeNode->node;
        }
        rc = _scene->OnDrop(_currentDragSource, _currentDragParent, _currentDragField,
                            node, field, effect);
        _currentDragSource = NULL;
        _currentDragParent = NULL;
        _currentDragField = -1;
    }
    return rc;
}

Path *SceneTreeView::MakePath(STREEITEM item)
{
    int len;
    STREEITEM p;
    TreeNode *t, *t1;
    Node *root = _scene->getRoot();

    t1 = (TreeNode *) swTreeGetItemData(_tree, item);
    len = t1->node ? 0 : 1;

    for (p = item; p != NULL; p = swTreeGetParentItem(_tree, p)) {
        t = (TreeNode *) swTreeGetItemData(_tree, p);
        if (t->node && t->node != root) {
            len += 2;
        }
    }
    int *list = new int[len];

    int i = len-1;
    if (!t1->node) {
        list[i--] = t1->field;
    }
    
    for (p = item; p != NULL; p = swTreeGetParentItem(_tree, p)) {
        t = (TreeNode *) swTreeGetItemData(_tree, p);
        if (t->node && t->node != root) {
            list[i--] = GetIndex(p);
            list[i--] = t->field;
        }
    }

    Path *path = new Path(list, len, _scene);
    delete [] list;
    return path;
}

int
SceneTreeView::GetIndex(STREEITEM item)
{
    assert(item != NULL);
    TreeNode *i = (TreeNode *) swTreeGetItemData(_tree, item);
    STREEITEM parent = swTreeGetParentItem(_tree, item);

    if (parent) {
        TreeNode *p = (TreeNode *) swTreeGetItemData(_tree, parent);

        if (!p->node) {
            // parent is a field, look up
            parent = swTreeGetParentItem(_tree, parent);
            p = (TreeNode *) swTreeGetItemData(_tree, parent);
        }
        assert(p->node != NULL);
        FieldValue *value = p->node->getField(i->field);
        if (value->getType() == MFNODE) {
            int pos = ((MFNode *) value)->getValues()->find(i->node);
            if (pos < 0)
                pos = -1;
            return pos;
        } else if (value->getType() == SFNODE)  {
            return 0;
        } else {
            assert(0);
            return -1;
        }
    } else {
        return 0;
    }
}

#if 0
void SceneTreeView::OnRButtonDown(UINT nFlags, CPoint point) 
{
    STREEITEM item = swTreeHitTest(point.x, point.y);

    if (item) {
        swTreeSetCurrentItem(_tree, item);
        CRect r;
        GetWindowRect(&r);

        FancyMenu menu, insertMenu;
        Path *path = MakePath(item);
        GetDocument()->ContextMenu(path, &menu, &insertMenu);
        menu.TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON, 
                            point.x + r.left, point.y + r.top, this);
        delete path;
    }
}
#endif

void SceneTreeView::UpdateOverlay()
{
    _scene->getNodes()->clearFlag(NODE_FLAG_TOUCHED);
    UpdateOverlayRec(swTreeGetRootItem(_tree));
}

void SceneTreeView::UpdateOverlayRec(STREEITEM item)
{
    Node *node = ((TreeNode *) swTreeGetItemData(_tree, item))->node;

    if (node) {
        if (node->getFlag(NODE_FLAG_TOUCHED)) {
            swTreeSetFlags(_tree, item, SW_TREE_ITEM_OVERLAY,
                           SW_TREE_ITEM_OVERLAY);
        } else {
            swTreeSetFlags(_tree, item, SW_TREE_ITEM_OVERLAY, 0);
            node->setFlag(NODE_FLAG_TOUCHED);
        }
    }

    for (STREEITEM child = swTreeGetFirstChild(_tree, item); child != NULL;
         child = swTreeGetNextItem(_tree, child)) {
        UpdateOverlayRec(child);
    }
}

void  SceneTreeView::UpdateNodeName(Node *node)
{
    for (STREEITEM item = swTreeGetRootItem(_tree); item != NULL;
         item = swTreeGetNextItem(_tree, item)) 
        renameNode(item, node);
}

#if 0
void SceneTreeView::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    STREEITEM item = pNMTreeView->itemNew.hItem;
    TreeNode *treeNode = (TreeNode *) GetTreeCtrl().GetItemData(item);

    if (treeNode->node) {
        if (pNMTreeView->action == TVE_COLLAPSE) {
            treeNode->node->setFlag(NODE_FLAG_COLLAPSED);
        } else if (pNMTreeView->action == TVE_EXPAND) {
            treeNode->node->clearFlag(NODE_FLAG_COLLAPSED);
        }
    }
    *pResult = 0;
}
#endif
