/*
 * pawstree.cpp - Author: Ondrej Hurt
 *
 * Copyright (C) 2003 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
 *
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

// CS INCLUDES
#include <psconfig.h>
#include <csgfx/rgbpixel.h>
#include <iutil/event.h>

// COMMON INCLUDES
#include "util/log.h"

// PAWS INCLUDES
#include "pawsborder.h"
#include "pawstree.h"
#include "pawsmanager.h"

#define SCROLLBAR_WIDTH 15




////////////////////////////////////////////////////////////
//        this should be in some library
////////////////////////////////////////////////////////////

// converts one hexadecimal digit to decimal value
int HexToDec4(char digit)
{
    if ((digit >= '0') && (digit <= '9'))
        return digit - '0';
    else if ((digit >= 'A') && (digit <= 'Z'))
        return digit - 'A' + 10;
    else if ((digit >= 'a') && (digit <= 'z'))
        return digit - 'a' + 10;
    else
        return 0;
}

// converts two hexadecimal digits to decimal value
int HexToDec8(char first, char second)
{
    return (HexToDec4(first) << 4) + HexToDec4(second);
}

int MakeColor(const csPixelFormat * fmt, int r, int g, int b)
{
  if (!fmt->PalEntries)
    return ((r >> (8 - fmt->RedBits)) << fmt->RedShift)
         | ((g >> (8 - fmt->GreenBits)) << fmt->GreenShift)
         | ((b >> (8 - fmt->BlueBits)) << fmt->BlueShift);

  // In paletted mode this is easy since we have a uniform 3-3-2 palette
  return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
}

// converts hexadecimal string to color in current pixel format
int ParseColor(const csString & str, iGraphics2D *g2d)
{
    int r, g, b;
    
    if (str.Length() != 6)
        return false;
        
    r = HexToDec8(str.GetAt(0), str.GetAt(1));
    g = HexToDec8(str.GetAt(2), str.GetAt(3));
    b = HexToDec8(str.GetAt(4), str.GetAt(5));

    return MakeColor(g2d->GetPixelFormat(), r, g, b);
}


////////////////////////////////////////////////////////////
//
//                    pawsTreeNode
//
////////////////////////////////////////////////////////////

pawsTreeNode::pawsTreeNode()
{
    tree = NULL;
    parent = NULL;
    firstChild = NULL;
    prevSibling = NULL;
    nextSibling = NULL;
    collapsable = true;
    collapsed = false;
}

pawsTreeNode::~pawsTreeNode()
{
    Clear();
}

void pawsTreeNode::SetTree(pawsITreeStruct * _tree)
{
    tree = _tree;
}

csString pawsTreeNode::GetAttr(const csString & name)
{    
    for ( size_t x = 0; x < attrList.Length(); x++ )    
    {
        if (attrList[x].name == name)
            return attrList[x].value;        
    }
    return "";
}

void pawsTreeNode::SetAttr(const csString & name, const csString & value)
{
    TreeNodeAttribute attr;
    
    attr.name  = name;
    attr.value = value;
    attrList.Push(attr);
    
    if (tree != NULL) tree->NodeChanged();
}

pawsTreeNode * pawsTreeNode::GetParent()
{
    return parent;
}

void pawsTreeNode::SetParent(pawsTreeNode * _parent)
{
    parent = _parent;
}

pawsTreeNode * pawsTreeNode::GetFirstChild()
{
    return firstChild;
}

pawsTreeNode * pawsTreeNode::FindNodeByPath(const csString & path)
{
    //not implemented yet
    return NULL;
}

pawsTreeNode * pawsTreeNode::FindChildByName(const csString & name, bool indirectToo)
{
    pawsTreeNode * child, * foundIndirect;

    child = firstChild;
    while (child != NULL)
    {
        if (name == child->name)
            return child;
        if (indirectToo)
        {
            foundIndirect = child->FindChildByName(name, true);
            if (foundIndirect != NULL)
                return foundIndirect;
        }

        child = child->GetNextSibling();
    }
    return NULL;
}

pawsTreeNode * pawsTreeNode::GetPrevSibling()
{
    return prevSibling;
}

pawsTreeNode * pawsTreeNode::GetNextSibling()
{
    return nextSibling;
}

void pawsTreeNode::SetPrevSibling(pawsTreeNode *node)
{
    prevSibling = node;
}

void pawsTreeNode::SetNextSibling(pawsTreeNode *node)
{
    nextSibling = node;
}

pawsTreeNode * pawsTreeNode::FindLastChild()
{
    return firstChild->FindLastSibling();
}

void pawsTreeNode::InsertChild(pawsTreeNode * node, pawsTreeNode * nextSibling)
{
    pawsTreeNode * prevSibling;

    if (nextSibling != NULL)
        prevSibling = nextSibling->GetPrevSibling();
    else
        prevSibling = FindLastChild();

    node->SetNextSibling(nextSibling);
    node->SetPrevSibling(prevSibling);
    
    if (nextSibling != NULL)
        nextSibling->SetPrevSibling(node);
    if (prevSibling != NULL)
        prevSibling->SetNextSibling(node);
    else
        firstChild = node;
    node->SetParent(this);
    node->SetTree(tree);

    tree->NewNode(node);
    
    if (tree != NULL) tree->NodeChanged();
}

void pawsTreeNode::MoveChild(pawsTreeNode * node, pawsTreeNode * nextSibling)
{
    RemoveChild(node);
    InsertChild(node, nextSibling);
    
    if (tree != NULL) tree->NodeChanged();
}

void pawsTreeNode::RemoveChild(pawsTreeNode * node)
{
    pawsTreeNode * prev = node->GetPrevSibling(),
                 * next = node->GetNextSibling();
    if (prev != NULL)
        prev->SetNextSibling(next);
    else
        firstChild = next;
    if (next != NULL)
        next->SetPrevSibling(prev);

    if (tree != NULL)
        tree->RemoveNode(node);
}

void pawsTreeNode::DeleteChild(pawsTreeNode * node)
{
    RemoveChild(node);
    delete node;
}

void pawsTreeNode::Clear()
{
    while (firstChild != NULL)
        DeleteChild(firstChild);
}

void pawsTreeNode::SetCollapsable(bool _collapsable)
{
    collapsable = _collapsable;
    if (!collapsable && collapsed)
        Expand();
}

void pawsTreeNode::Expand()
{
    collapsed = false;
    SetChildrenVisibAfterCollapseChange(true);
    
    if (tree != NULL) tree->NodeChanged();
}

void pawsTreeNode::ExpandAll()
{
    pawsTreeNode * child;
    
    Expand();
    child = firstChild;
    while (child != NULL)
    {
        child->ExpandAll();
        child = child->GetNextSibling();
    }
}

void pawsTreeNode::Collapse()
{
    collapsed = true;
    SetChildrenVisibAfterCollapseChange(false);
    
    if (tree != NULL) tree->NodeChanged();
}

void pawsTreeNode::CollapseAll()
{
    pawsTreeNode * child;
    
    Collapse();
    child = firstChild;
    while (child != NULL)
    {
        child->CollapseAll();
        child = child->GetNextSibling();
    }
}

bool pawsTreeNode::IsCollapsed()
{
    return collapsed;
}

bool pawsTreeNode::BuriedInRuins()
{
    if (parent == NULL)
        return false;
    return parent->IsCollapsed() || parent->BuriedInRuins();
}

void pawsTreeNode::SetChildrenVisibAfterCollapseChange(bool expanded)
{
    pawsTreeNode * child;
    
    child = firstChild;
    while (child != NULL)
    {
        if (expanded)
        {
            child->Show();
        }
        else
        {
            child->Hide();
        }
        if ( ! child->IsCollapsed() )
            child->SetChildrenVisibAfterCollapseChange(expanded);
        child = child->GetNextSibling();
    }
}    

int pawsTreeNode::GetRowNum()
{
    if (prevSibling != NULL)
        return prevSibling->FindLowestSubtreeNode()->GetRowNum() + 1;
    else if (parent != NULL)
        return parent->GetRowNum() + 1;
    else
        return 0;
}

pawsTreeNode * pawsTreeNode::FindLastSibling()
{
    pawsTreeNode *sibling;

    sibling = this;
    while ((sibling != NULL) && (sibling->GetNextSibling() != NULL))
        sibling = sibling->GetNextSibling();
    return sibling;
}

pawsTreeNode * pawsTreeNode::FindLowestSubtreeNode()
{
    pawsTreeNode * lastChild;

    lastChild = FindLastChild();
    if (lastChild == NULL)
        return this;
    else
        return lastChild->FindLowestSubtreeNode();
}

pawsTreeNode * pawsTreeNode::FindNodeAbove()
{
    if (prevSibling != NULL)
        return prevSibling->FindLowestSubtreeNode();
    else
        return parent;
}

pawsTreeNode * pawsTreeNode::FindNodeBelow()
{
    pawsTreeNode * node;

    if (firstChild != NULL)
        return firstChild;
    else if (nextSibling != NULL)
        return nextSibling;
    else
    {
        node = parent;
        while ((node != NULL) && (node->GetNextSibling() == NULL))
            node = node->GetParent();
        if (node != NULL)
            return node->GetNextSibling();
        else
            return NULL;
    }
}

bool pawsTreeNode::Load(iDocumentNode *node)
{
    csRef<iDocumentNodeIterator> xmlChildren, xmlAttrList;
    csRef<iDocumentNode> xmlChild, xmlAttr;
    csString factory;
    pawsWidget *childAsWidget;
    pawsTreeNode *childNode;

    Clear();

    name = node->GetAttributeValue("name");
    xmlChildren = node->GetNodes("widget");
    while (xmlChildren->HasNext())
    {
        xmlChild = xmlChildren->Next();

        factory = xmlChild->GetAttributeValue( "factory" );
        childAsWidget = PawsManager::GetSingleton().CreateWidget( factory );
        if ( !childAsWidget )
        {
            Error2("Could not create node from factory: %s", factory.GetData());
            return false;
        }
        childNode = dynamic_cast<pawsTreeNode*>(childAsWidget);
        if (childNode == NULL)
        {
            Error1("Created node is not pawsTreeNode");
            return false;
        }
        InsertChild(childNode);

        if (!childNode->Load(xmlChild))
        {
            Error1("Node failed to load");
            return false;
        }
    }


    attrList.DeleteAll();

    xmlAttrList = node->GetNodes("attr");
    while (xmlAttrList->HasNext())
    {
        xmlAttr = xmlAttrList->Next();
        SetAttr(xmlAttr->GetAttributeValue("name"), xmlAttr->GetAttributeValue("value"));
    }
    
    if (tree != NULL)
        tree->NodeChanged();

    csString collapsed = node->GetAttributeValue("collapsed");
    if (collapsed == "yes")
        CollapseAll();

    return true;
}

//////////////////////////////////////////////////////////////////////
//
//                        pawsTreeStruct
//
//////////////////////////////////////////////////////////////////////

pawsTreeStruct::pawsTreeStruct()
{
    root = NULL;
    version = 0;
}

pawsTreeStruct::~pawsTreeStruct()
{
    if (root != NULL)
        delete root;
}

void pawsTreeStruct::NodeChanged()
{
    version++;
}
    
void pawsTreeStruct::SetRoot(pawsTreeNode * _root)
{
    if (root != NULL)
        delete root;

    root = _root;
}

pawsTreeNode * pawsTreeStruct::FindNodeByName(const csString & name)
{
    if (root != NULL)
        return root->FindChildByName(name, true);
    else
        return NULL;
}

void pawsTreeStruct::InsertChild(pawsTreeNode * parent, pawsTreeNode * node, pawsTreeNode * nextSibling)
{
    parent->InsertChild(node, nextSibling);
}

void pawsTreeStruct::MoveChild(pawsTreeNode * node, pawsTreeNode * nextSibling)
{
    pawsTreeNode * parent;
    
    parent = node->GetParent();
    if (parent != NULL)
        parent->MoveChild(node, nextSibling);
}

void pawsTreeStruct::RemoveChild(pawsTreeNode * node)
{
    pawsTreeNode * parent;
    
    parent = node->GetParent();
    if (parent != NULL)
        parent->RemoveChild(node);
}

void pawsTreeStruct::DeleteChild(pawsTreeNode * node)
{
    pawsTreeNode * parent;
    
    parent = node->GetParent();
    if (parent != NULL)
        parent->DeleteChild(node);
}

void pawsTreeStruct::Clear()
{
    if (root != NULL)
        root->Clear();
}





void pawsTreeStruct::InsertChild(const csString & parent, pawsTreeNode * node, const csString & nextSibling)
{
    pawsTreeNode * parentNode, * nextSiblingNode;

    parentNode = FindNodeByName(parent);
    // Allows a root to be added automatically.
    if (parentNode == NULL)
        if (!root)
        {
            SetRoot(node);
            node->SetTree(this);
            return;
        }
        else
            parentNode = root;
    nextSiblingNode = FindNodeByName(nextSibling);
    parentNode->InsertChild(node, nextSiblingNode);
}

void pawsTreeStruct::InsertChild(const csString & parent, pawsTreeNode * node)
{
    pawsTreeNode * parentNode;

    parentNode = FindNodeByName(parent);
    if (parentNode == NULL) return;

    parentNode->InsertChild(node, NULL);
}

void pawsTreeStruct::MoveChild(const csString & name, const csString & nextSibling)
{
    pawsTreeNode * node, * parentNode, * nextSiblingNode;

    node = FindNodeByName(name);
    if (node == NULL) return;
    nextSiblingNode = FindNodeByName(nextSibling);
    if (nextSiblingNode == NULL) return;

    parentNode = node->GetParent();
    if (parentNode != NULL)
        parentNode->MoveChild(node, nextSiblingNode);
}

void pawsTreeStruct::DeleteChild(const csString & name)
{
    pawsTreeNode * node, * parentNode;

    node = FindNodeByName(name);
    if (node == NULL) return;

    parentNode = node->GetParent();
    if (parentNode != NULL)
        parentNode->DeleteChild(node);
    else
        Clear();
}

bool pawsTreeStruct::Load(iDocumentNode *node)
{
    csRef<iDocumentNode> xmlRoot;
    csString factory;
    pawsWidget *rootAsWidget;
    pawsTreeNode *newRoot;

    version++;

    xmlRoot = node->GetNode("widget");
    if (xmlRoot == NULL)
    {
        Error1("<widget> tag not found");
        return false;
    }

    factory = xmlRoot->GetAttributeValue("factory");
    rootAsWidget = PawsManager::GetSingleton().CreateWidget(factory);
    if (rootAsWidget == NULL)
    {
        Error2("Could not create root from factory: %s", factory.GetData());
        return false;
    }

    newRoot = dynamic_cast<pawsTreeNode*>(rootAsWidget);
    if (newRoot == NULL)
    {
        Error2("Root(%s) is not pawsTreeNode",rootAsWidget->GetType());
        delete rootAsWidget;
        return false;
    }
    SetRoot(newRoot);

    root->SetTree(this);
    return root->Load(xmlRoot);
}

pawsTreeNode * pawsTreeStruct::FindNodeAt(pawsTreeNode *parent, int x, int y)
{
    pawsTreeNode *child, *foundIndirect;
    csRect frame;

    if (parent->IsCollapsed())
        return false;

    child = parent->GetFirstChild();
    while (child != NULL)
    {
        frame = child->ScreenFrame();
        if (frame.Contains(x, y))
            return child;
        foundIndirect = FindNodeAt(child, x, y);
        if (foundIndirect != NULL)
            return foundIndirect;

        child = child->GetNextSibling();
    }
    return NULL;
}

pawsTreeNode * pawsTreeStruct::GetRoot()
{
    return root;
}

//////////////////////////////////////////////////////////////////////
//
//                    pawsStdTreeLayout
//
//////////////////////////////////////////////////////////////////////


pawsStdTreeLayout::pawsStdTreeLayout(pawsTree * _tree, int _rowSpacing, int _levelSpacing)
{
    tree           = _tree;
    rowSpacing     = _rowSpacing;
    levelSpacing   = _levelSpacing;
    horizScroll    = 0;
    vertScroll     = 0;
    lastVersion    = -1;
    width          = 0;
    height         = 0;
}

void pawsStdTreeLayout::SetLayout()
{
    pawsTreeNode *root;
    csRect treeFrame;
    int treeX, treeY, maxX, maxY;

    // if the tree hasn't changed, we have nothing to update
    if (lastVersion == tree->GetVersion())
        return;

    root = tree->GetRoot();
    if (root == NULL)
        return;

    treeFrame = tree->ScreenFrame();
    treeX = treeFrame.xmin - horizScroll;
    treeY = treeFrame.ymin - vertScroll;
    maxX  = treeX;
    maxY  = treeY;
    root->SetSize(0, 0);
    SetSubtreeLayout(root, treeX, treeY, maxX, maxY);
    width  = maxX - treeX + 1;
    height = maxY - treeY + 1;

    lastVersion = tree->GetVersion();
}

void pawsStdTreeLayout::SetSubtreeLayout(pawsTreeNode * subtreeRoot, int x, int y, int & maxX, int & maxY)
{
    pawsTreeNode *child;
    csRect frame;

    subtreeRoot->MoveTo(x, y);
    frame = subtreeRoot->ScreenFrame();
    if (frame.xmax > maxX)
        maxX = frame.xmax;
    maxY = frame.ymax;

    if ( ! subtreeRoot->IsCollapsed() )
    {
        child = subtreeRoot->GetFirstChild();
        while (child != NULL)
        {
            SetSubtreeLayout(child, x + levelSpacing, maxY + rowSpacing, maxX, maxY);
            child = child->GetNextSibling();
        }
    }
}

void pawsStdTreeLayout::SetHorizScroll(int _horizScroll)
{
    horizScroll = _horizScroll;
    SetLayout();
}
void pawsStdTreeLayout::SetVertScroll(int _vertScroll)
{
    vertScroll = _vertScroll;
    SetLayout();
}

void pawsStdTreeLayout::GetTreeSize(int & _width, int & _height)
{
    _width = width;
    _height = height;
}

//////////////////////////////////////////////////////////////////////
//
//                        pawsStdTreeDecorator
//
//////////////////////////////////////////////////////////////////////

pawsStdTreeDecorator::pawsStdTreeDecorator(pawsTree * _tree, iGraphics2D * _g2d, 
                                           int _selectedColor, int _lineColor, int _collSpacing)
{
    psImageDescription * collDesc, * expandDesc;
    
    
    tree              = _tree;
    g2d               = _g2d;
    selectedColor     = _selectedColor;
    lineColor         = _lineColor;
    collSpacing       = _collSpacing;

    collImage = expandImage = NULL;

    collDesc = PawsManager::GetSingleton().GetTextureManager()->GetImageDescription("treecollapse");
    if (collDesc != NULL)
    {
        collImage = new pawsImage( PawsManager::GetSingleton().GetObjectRegistry() );
        collImage->Description( collDesc );
    }

    expandDesc = PawsManager::GetSingleton().GetTextureManager()->GetImageDescription("treeexpand");
    if (expandDesc != NULL)
    {
        expandImage = new pawsImage( PawsManager::GetSingleton().GetObjectRegistry() );
        expandImage->Description( expandDesc );
    }
}

pawsStdTreeDecorator::~pawsStdTreeDecorator()
{
    if (collImage != NULL)
        delete collImage;
    if (expandImage != NULL)
        delete expandImage;
}

void pawsStdTreeDecorator::Decorate()
{
    pawsTreeNode *root;

    root = tree->GetRoot();
    if (root != NULL)
        DecorateSubtree(root);
}

void pawsStdTreeDecorator::DecorateSubtree(pawsTreeNode * node)
{
    pawsImage * currImage;
    pawsTreeNode * child;
    csRect nodeFrame, childFrame, collSignFrame;
    int vertLineX, vertLineYMin, vertLineYMax;    
            //coordinates of lines painted between siblings
    int horizLineXMin, horizLineY;
            //coordinates of lines painted between nodes and their collapse signs
    bool hasCollSign;    // has the currently processed node collapse/expand sign ?


    nodeFrame = node->ScreenFrame();

    if (node != tree->GetRoot())
    {
        // paints collapse/expand sign
        hasCollSign  =  node->IsCollapsable() && (node->GetFirstChild() != NULL);
        if (hasCollSign)
        {
            GetCollapseSignFrame(node, collSignFrame);
            if (node->IsCollapsed())
                currImage = expandImage;
            else 
                currImage = collImage;
            if (currImage != NULL)
                currImage->Draw(collSignFrame);
        }

        // paints node selection box
        if (node == tree->GetSelected())
            g2d->DrawBox(nodeFrame.xmin-2, nodeFrame.ymin, nodeFrame.Width()+4, nodeFrame.Height(), selectedColor);
    }
    
    if (node->IsCollapsed())
        return;

    // paints children of node
    child = node->GetFirstChild();
    if (child != NULL)
    {
        childFrame = child->ScreenFrame();
        vertLineX = (nodeFrame.xmin + childFrame.xmin) / 2;
        vertLineYMin = childFrame.ymin;

        do
        {
            hasCollSign = child->IsCollapsable() && (child->GetFirstChild() != NULL);
            childFrame = child->ScreenFrame();
            horizLineY = (childFrame.ymin + childFrame.ymax) / 2;
            GetCollapseSignFrame(child, collSignFrame);
            if (hasCollSign)
            {
                horizLineXMin = collSignFrame.xmax;
                vertLineYMax = collSignFrame.ymin-1;
            }
            else
            {
                horizLineXMin = vertLineX;
                vertLineYMax = horizLineY;
            }
            g2d->DrawLine(vertLineX, vertLineYMin, vertLineX, vertLineYMax, lineColor);
            g2d->DrawLine(horizLineXMin, horizLineY, childFrame.xmin - 3, horizLineY, lineColor);
            DecorateSubtree(child);
            
            // determines vertLineYMin for next sibling:
            if (hasCollSign)
                vertLineYMin = collSignFrame.ymax+1;
            else
                vertLineYMin = horizLineY;


            child = child->GetNextSibling();
        }
        while (child != NULL);
    }
}

bool pawsStdTreeDecorator::OnMouseDown(int button, int modifiers, int x, int y)
{
    pawsTreeNode * target, * root;

    root = tree->GetRoot();
    if (root != NULL)
    {
        target = FindCollapsingNodeInSubtree(root, x, y);
        if (target != NULL)
        {
            // has the currently processed node collapse/expand sign ?
            bool hasCollSign  =  target->IsCollapsable() && 
                                 (target->GetFirstChild() != NULL);
            if (hasCollSign)
            {
                if (target->IsCollapsed())
                    target->Expand();
                else
                    target->Collapse();
            }
        }
    }
    return false;
}

void pawsStdTreeDecorator::GetCollapseSignFrame(pawsTreeNode * node, csRect & rect)
{
    csRect nodeFrame = node->ScreenFrame();
    rect.SetPos(nodeFrame.xmin - collSpacing - 2,
                nodeFrame.ymin + nodeFrame.Height()/2 - 4);
    rect.SetSize(10, 10);
}

pawsTreeNode * pawsStdTreeDecorator::FindCollapsingNodeInSubtree(pawsTreeNode * subtreeRoot, int x, int y)
{
    csRect signFrame;
    pawsTreeNode * child, * found;
    
    
    // has the currently processed node collapse/expand sign ?
    bool hasCollSign  =  subtreeRoot->IsCollapsable() && 
                         (subtreeRoot->GetFirstChild() != NULL);
    if (hasCollSign)
    {
        GetCollapseSignFrame(subtreeRoot, signFrame);
        if (signFrame.Contains(x, y))
            return subtreeRoot;
    }
        
    child = subtreeRoot->GetFirstChild();
    while (child != NULL)
    {
        found = FindCollapsingNodeInSubtree(child, x, y);
        if (found != NULL)
            return found;
        child = child->GetNextSibling();
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////
//
//                            pawsTree
//
//////////////////////////////////////////////////////////////////////


pawsTree::pawsTree()
{
    selected = NULL;
    notificationTarget = NULL;

    layout = 0;
    SetTreeLayout(new pawsStdTreeLayout(this, 5, 20));

    decor = 0;
    SetTreeDecorator(new pawsStdTreeDecorator(this, graphics2D, 0x666666, 0x999999, 13));
    
    horizScrollBar = NULL;
    vertScrollBar = NULL;
}

pawsTree::~pawsTree()
{
    if (layout != NULL)
        delete layout;
    if (decor != NULL)
        delete decor;
    if (root != NULL)
    {
        pawsWidget::DeleteChild(root);
        root = NULL;
    }
}

void pawsTree::SetRoot(pawsTreeNode * _root)
{
    if (root != NULL)
    {
        if (selected == root)
            Deselect();
        pawsWidget::DeleteChild(root);
    }
    root = _root;
    AddChild(root);
}

void pawsTree::SetScrollBars(bool horiz, bool vert)
{
    if (horiz && (horizScrollBar == NULL))
    {
        horizScrollBar = (pawsScrollBar*)PawsManager::GetSingleton().CreateWidget("pawsScrollBar");
        if (horizScrollBar == NULL)
        { 
            Error1("Could not created pawsScrollBar");
            return;
        }
        AddChild(horizScrollBar);
        horizScrollBar->SetRelativeFrame(0, defaultFrame.Width()-GetActualHeight(SCROLLBAR_WIDTH),
                                         defaultFrame.Width(), GetActualHeight(SCROLLBAR_WIDTH));
        horizScrollBar->PostSetup();
        horizScrollBar->SetTickValue(20);
        horizScrollBar->Show();
    }
    if (!horiz && (horizScrollBar != NULL))
    {
        pawsWidget::DeleteChild(horizScrollBar);
        horizScrollBar = NULL;
        if (layout)
            layout->SetHorizScroll(0);
    }

    if (vert && (vertScrollBar == NULL))
    {
        vertScrollBar = (pawsScrollBar*)PawsManager::GetSingleton().CreateWidget("pawsScrollBar");
        if (vertScrollBar == NULL)
        { 
            Error1("Could not created pawsScrollBar");
            return;
        }
        AddChild(vertScrollBar);
        vertScrollBar->SetRelativeFrame(defaultFrame.Width()-GetActualWidth(SCROLLBAR_WIDTH), 0,
                                        GetActualWidth(SCROLLBAR_WIDTH), defaultFrame.Height());
        vertScrollBar->PostSetup();
        vertScrollBar->SetTickValue(20);
        vertScrollBar->SetAttachFlags(ATTACH_TOP | ATTACH_BOTTOM | ATTACH_RIGHT );
        vertScrollBar->Show();
    }
    if (!vert && (vertScrollBar != NULL))
    {
        pawsWidget::DeleteChild(vertScrollBar);
        vertScrollBar = NULL;
        if (layout)
            layout->SetVertScroll(0);    
    }
    SetScrollBarMax();
}


void pawsTree::Select(pawsTreeNode *node)
{
    selected = node;
    if (notificationTarget != NULL)
        notificationTarget->OnSelected(node);
}

void pawsTree::Deselect()
{
    selected = NULL;
}

pawsTreeNode * pawsTree::GetSelected()
{
    return selected;
}

bool pawsTree::Setup(iDocumentNode * node)
{
    csRef<iDocumentNode> decorator = node->GetNode("decorator");
    if (decorator)
    {
        csRGBcolor selColour = csRGBcolor(102,102,102);
        csRGBcolor lineColour = csRGBcolor(153,153,153);
        int colSpacing = 13;

        csRef<iDocumentAttribute> selR = decorator->GetAttribute("selR");
        if (selR) selColour.red = selR->GetValueAsInt();
        csRef<iDocumentAttribute> selG = decorator->GetAttribute("selG");
        if (selG) selColour.green = selG->GetValueAsInt();
        csRef<iDocumentAttribute> selB = decorator->GetAttribute("selB");
        if (selB) selColour.blue = selB->GetValueAsInt();
        
        csRef<iDocumentAttribute> lineR = decorator->GetAttribute("lineR");
        if (lineR) lineColour.red = lineR->GetValueAsInt();
        csRef<iDocumentAttribute> lineG = decorator->GetAttribute("lineG");
        if (lineG) lineColour.green = lineG->GetValueAsInt();
        csRef<iDocumentAttribute> lineB = decorator->GetAttribute("lineB");
        if (lineB) lineColour.blue = lineB->GetValueAsInt();

        csRef<iDocumentAttribute> colSpace = decorator->GetAttribute("colSpacing");
        if (colSpace) colSpacing = colSpace->GetValueAsInt();
        
        SetTreeDecorator(new pawsStdTreeDecorator(this, graphics2D, 
            MakeColor(graphics2D->GetPixelFormat(), selColour.red, selColour.blue, selColour.green),
            MakeColor(graphics2D->GetPixelFormat(), lineColour.red, lineColour.blue, lineColour.green),
            colSpacing));
    }
    
    csRef<iDocumentNode> layout = node->GetNode("layout");
    if (layout)
    {
        int rowSpacingVal = 5;
        int levelSpacingVal = 20;
        csRef<iDocumentAttribute> rowSpacing = layout->GetAttribute("rowSpacing");
        if (rowSpacing)
            rowSpacingVal = rowSpacing->GetValueAsInt();
        csRef<iDocumentAttribute> levelSpacing = layout->GetAttribute("levelSpacing");
        if (levelSpacing)
            levelSpacingVal = levelSpacing->GetValueAsInt();
        SetTreeLayout(new pawsStdTreeLayout(this, rowSpacingVal, levelSpacingVal));
    }

    return true;
}

void pawsTree::Draw()
{
    csRect oldClip;

    if (layout)
    {
        layout->SetLayout();
        SetScrollBarMax();
    }

    ClipToParent();
    DrawBackground();
    
    graphics2D->SetClipRect(screenFrame.xmin, screenFrame.ymin, screenFrame.xmax, screenFrame.ymax);
    if (decor)
        decor->Decorate();

    ClipToParent();
    DrawChildren();
}

void pawsTree::SetTreeLayout(pawsITreeLayout * _layout)
{
    if (layout)
    {
        delete layout;
        layout = 0;
    }
    layout = _layout;
    layout->SetLayout();
}

void pawsTree::SetTreeDecorator(pawsITreeDecorator *_decor)
{
    if (decor)
    {
        delete decor;
        decor = 0;
    }
    decor = _decor;
}

bool pawsTree::OnMouseDown(int button, int modifiers, int x, int y)
{
    if (button == csmbWheelUp)
    {
        if (vertScrollBar)
            vertScrollBar->SetCurrentValue(vertScrollBar->GetCurrentValue() - TREE_MOUSE_SCROLL_AMOUNT);
        return true;
    }
    else if (button == csmbWheelDown)
    {
        if (vertScrollBar)
            vertScrollBar->SetCurrentValue(vertScrollBar->GetCurrentValue() + TREE_MOUSE_SCROLL_AMOUNT);
        return true;
    }
    else
    {
        pawsTreeNode *node;

        if (root != NULL)
        {
            node = FindNodeAt(root, x, y);
            if (node != NULL)
            {
                Select(node);
                return true;
            }
        }
    
        if (decor != NULL)
            if (decor->OnMouseDown(button, modifiers, x, y))
                return true;
    }
    return false;
}

bool pawsTree::OnKeyDown( int keyCode, int keyChar, int modifiers )
{
    pawsTreeNode *node;

    switch (keyCode)
    {
    case 1000:
        if (selected != NULL)
        {
            node = selected;
            do
            {
                node = node->FindNodeAbove();
            } while ((node != NULL) && node->BuriedInRuins());
            if ((node != NULL) && (node != root))
                Select(node);
        }
        return true;

    case 1001:
        if (selected != NULL)
        {
            node = selected;
            do
            {
                node = node->FindNodeBelow();
            } while ((node != NULL) && node->BuriedInRuins());
            if (node != NULL)
                Select(node);
        }
        return true;
        
    case 13:
        if (selected == NULL)
        //some notify?
        return true;
    }
    return false;
}

bool pawsTree::LoadChildren( iDocumentNode* node )
{
    return pawsTreeStruct::Load(node);
}

void pawsTree::SetScrollBarMax()
{
    int width, height;
    
    if (layout != NULL)
    {
        layout->GetTreeSize(width, height);
        if (horizScrollBar != NULL)
            horizScrollBar  -> SetMaxValue(MAX(0, width  - screenFrame.Width()));
        if (vertScrollBar != NULL)
            vertScrollBar   -> SetMaxValue(MAX(0, height - screenFrame.Height()));
    }
}

void pawsTree::SetNotify(pawsWidget* _notificationTarget)
{
    notificationTarget = _notificationTarget;
}

bool pawsTree::OnScroll(int scrollDirection, pawsScrollBar* widget)
{
    int scroll;

    scroll = (int)widget->GetCurrentValue();
    version++;
    if (widget == horizScrollBar)
        layout->SetHorizScroll(scroll);
    else if (widget == vertScrollBar)
        layout->SetVertScroll(scroll);

    return true;
}

void pawsTree::NewNode(pawsTreeNode * node)
{
    pawsWidget::AddChild(node);
}
void pawsTree::RemoveNode(pawsTreeNode * node)
{
    pawsWidget::RemoveChild(node);
}


//////////////////////////////////////////////////////////////////////
//
//                        pawsSeqTreeNode
//
//////////////////////////////////////////////////////////////////////

pawsSeqTreeNode_widget::pawsSeqTreeNode_widget(pawsWidget * _w, int _width)
{
    widget = _w;
    width = _width;
}

void pawsSeqTreeNode::AddSeqWidget(pawsWidget * w, int width)
{
    csRect widgetFrame;

    widgets.PushBack(pawsSeqTreeNode_widget(w, width));

    AddChild(w);
    w->MoveTo(screenFrame.xmax+1, screenFrame.ymin);
    w->Show();

    Resize(width, 0, RESIZE_RIGHT);
    widgetFrame = w->ScreenFrame();
    if (widgetFrame.ymax > screenFrame.ymax)
        Resize(0, widgetFrame.ymax - screenFrame.ymax, RESIZE_BOTTOM);
}

void pawsSeqTreeNode::AddSeqWidget(pawsWidget * widget)
{
    int width = widget->ScreenFrame().Width();
    AddSeqWidget(widget, width);
}

pawsWidget * pawsSeqTreeNode::GetSeqWidget(int index)
{
    csList<pawsSeqTreeNode_widget>::Iterator iter(widgets);
    int currIndex=0;

    while ((iter.HasNext()))
    {
        iter.Next();
        if(currIndex == index)
            return iter->widget;

        currIndex++;
    }
    
    return NULL;
}
    
bool pawsSeqTreeNode::Load(iDocumentNode *node)
{
    return pawsTreeNode::Load(node);
}

//////////////////////////////////////////////////////////////////////
//
//                        pawsCheckTreeNode
//
//////////////////////////////////////////////////////////////////////

pawsCheckTreeNode::pawsCheckTreeNode()
{
    widget = NULL;
//    checkbox = NULL;
}

bool pawsCheckTreeNode::GetCheck()
{
    return false;
}

void pawsCheckTreeNode::SetCheck(bool ch)
{
    //checkbox->Set(ch);
}

void pawsCheckTreeNode::SetWidget(pawsWidget * _widget)
{
    csRect widgetFrame;

    if (widget != NULL)
    {
        pawsWidget::DeleteChild(widget);
        widget =  NULL;
    }
    
    widget = _widget;
    widgetFrame = widget->ScreenFrame();
    AddChild(widget);
    widget->MoveTo(screenFrame.xmin, screenFrame.ymin);
    widget->Show();
    SetSize(widgetFrame.Width(), widgetFrame.Height());
}


//////////////////////////////////////////////////////////////////////
//
//                        pawsWidgetTreeNode
//
//////////////////////////////////////////////////////////////////////

pawsWidgetTreeNode::pawsWidgetTreeNode()
{
    widget = NULL;
}
void pawsWidgetTreeNode::SetWidget(pawsWidget * _widget)
{
    csRect widgetFrame;
    if (widget != NULL)
        pawsWidget::DeleteChild(widget);

    widget = _widget;
    widget->MoveTo(screenFrame.xmin, screenFrame.ymin);
    widgetFrame = widget->ScreenFrame();
    SetSize(widgetFrame.Width(), widgetFrame.Height());
    AddChild(widget);
}

bool pawsWidgetTreeNode::Load(iDocumentNode *node)
{
    csRef<iDocumentNode> nodewidgetNode, widgetNode;
    csString factory;
    pawsWidget * newWidget;
    
    name = node->GetAttributeValue("name");

    nodewidgetNode = node->GetNode("nodewidget");
    if (nodewidgetNode == NULL)
        return false;
    widgetNode = nodewidgetNode->GetNode("widget");
    if (widgetNode == NULL)
        return false;
        
    factory = widgetNode->GetAttributeValue("factory");
    newWidget = PawsManager::GetSingleton().CreateWidget(factory);
    if (newWidget == NULL)
        return false;
    if ( ! newWidget->Load(widgetNode))
    {
        delete newWidget;
        return false;
    }
    SetWidget(newWidget);
    
    return pawsTreeNode::Load(node);
}

//////////////////////////////////////////////////////////////////////
//
//                            pawsSimpleTree
//
//////////////////////////////////////////////////////////////////////


pawsSimpleTreeNode::pawsSimpleTreeNode()
{
    textBox = NULL;
    image = NULL;
    SetWidget(new pawsWidget());
}


void pawsSimpleTreeNode::Set(int mode, bool checked, const csString & imageName, const csString & label)
{
    csRect frame;
    int lastX;
        
    assert(tree != NULL);

    if (textBox != NULL)
    {
        widget->DeleteChild(textBox);
        textBox = NULL;
    }
    if (image != NULL)
    {
        widget->DeleteChild(image);
        image = NULL;
    }

    lastX = screenFrame.xmin - 1;
    if (mode & showCheckBox)
    {
        assert("checkbox nodes are not currently implemented"==0);
    }
    if (mode & showImage)
    {
        image = new pawsWidget();
        image->SetSize(20, 20);
        image->SetBackground(imageName);
        image->MoveTo(lastX+1, screenFrame.ymin);
        widget->AddChild(image);
        lastX += 20;
    }
    if (mode & showLabel)
    {
        textBox = (pawsTextBox*)PawsManager::GetSingleton().CreateWidget("pawsTextBox");
        textBox->SetParent(widget);
        if (textBox == NULL)
            return;
        textBox->SetText(label);
        textBox->SetSizeByText();
        textBox->MoveTo(lastX+1,0);
        
        widget->AddChild(textBox);
        lastX += textBox->ScreenFrame().Width();
    }
    widget->SetRelativeFrame(0,0,lastX-screenFrame.xmin,20);
    widget->SetSize(lastX-screenFrame.xmin, 20);
    widget->MoveTo(0,0);
    SetRelativeFrameSize(lastX - screenFrame.xmin, 20);
}

bool pawsSimpleTreeNode::Load(iDocumentNode *node)
{
    csRef<iDocumentAttribute> labelAttr, imageAttr, checkedAttr;
    csString labelValue, imageValue;
    bool checkedValue = false;
    int mode;

    if (!pawsCheckTreeNode::Load(node))
        return false;

    mode = 0;

    name = node->GetAttributeValue("name");

    checkedAttr  = node->GetAttribute("checked");
    if (checkedAttr != NULL)
    {
        checkedValue = (checkedAttr->GetValueAsInt() == 1);
        mode |= showCheckBox;
    }
    imageAttr = node->GetAttribute("image");
    if (imageAttr != NULL)
    {
        imageValue = imageAttr->GetValue();
        mode |= showImage;
    }
    labelAttr = node->GetAttribute("label");
    if (labelAttr != NULL)
    {
        labelValue = PawsManager::GetSingleton().Translate(labelAttr->GetValue());
        mode |= showLabel;
    }

    Set(mode, checkedValue, imageValue, labelValue);
    return true;
}


bool pawsSimpleTree::Setup(iDocumentNode *node)
{
    csString colorStr;

    colorStr  =  node->GetAttributeValue("colour");
    name      = node->GetAttributeValue("name");
    if (colorStr.GetData() != NULL)
        defaultColor = ParseColor(colorStr.GetData(), graphics2D);
    else
        defaultColor = 0xffffff;

    return true;
}

int  pawsSimpleTree::GetDefaultColor()
{
    return defaultColor;
}

void pawsSimpleTree::SetDefaultColor(int _color)
{
    defaultColor = _color;
}

void pawsSimpleTree::InsertChildL(const csString & parent, const csString & name, const csString & label, const csString & nextSibling)
{
    pawsSimpleTreeNode * node = new pawsSimpleTreeNode();
    pawsTreeStruct::InsertChild(parent, node, nextSibling);
    node->SetColour(defaultColor);
    node->Set(showLabel, false, "", label);
    node->SetName(name);
}
void pawsSimpleTree::InsertChildI(const csString & parent, const csString & name, const csString & image, const csString & nextSibling)
{
    pawsSimpleTreeNode * node = new pawsSimpleTreeNode();
    pawsTreeStruct::InsertChild(parent, node, nextSibling);
    node->SetColour(defaultColor);
    node->Set(showImage, false, image, "");
    node->SetName(name);
}
void pawsSimpleTree::InsertChildIL(const csString & parent, const csString & name, const csString & image, const csString & label, const csString & nextSibling)
{
    pawsSimpleTreeNode * node = new pawsSimpleTreeNode();
    pawsTreeStruct::InsertChild(parent, node, nextSibling);
    node->SetColour(defaultColor);
    node->Set(showLabel | showImage, false, image, label);
    node->SetName(name);
}


pawsSimpleTree::pawsSimpleTree()
{
    layout = new pawsStdTreeLayout(this, 5, 20);
    decor = new pawsStdTreeDecorator(this, graphics2D, 0x0000ff, 0x00ffff, 13);
}



