#include "safe_include.h" 

// For compilers that supports precompilation , includes "wx/wx.h"
#include "wx/wxprec.h"

#ifndef WX_PRECOMP
	#include "wx/wx.h"
#endif

#include "V4StudioTree.h"
#include "V4StudioFrame.h"

#include "V4SceneManager.h" // DICTNAME constant

#include "wx/image.h"
#include "wx/imaglist.h"
#include "wx/treectrl.h"

#include <math.h>

#include "treeIcon1.xpm"
#include "treeIcon2.xpm"
#include "treeIcon3.xpm"
#include "treeIcon4.xpm"
#include "treeIcon5.xpm"

#include <gpac/m4_mpeg4_nodes.h>


BEGIN_EVENT_TABLE(V4StudioTree, wxTreeCtrl)
    EVT_TREE_SEL_CHANGED(-1, V4StudioTree::OnSelChanged)
	EVT_TREE_ITEM_RIGHT_CLICK(-1, V4StudioTree::OnItemRightClick)
    EVT_TREE_BEGIN_DRAG(-1, V4StudioTree::OnBeginDrag)
    EVT_TREE_END_DRAG(-1, V4StudioTree::OnEndDrag)
END_EVENT_TABLE()

M4Err V4StudioTreeItemData::GetField(FieldInfo *f)
{
	if (!f || fieldIndex < 0) return M4BadParam;
	return Node_GetField(parent, fieldIndex, f);	
}

V4StudioTree::V4StudioTree(wxWindow *parent_, wxSize size,V4FieldList *fieldView) : 
	wxTreeCtrl(parent_, -1, wxDefaultPosition, size, wxTR_DEFAULT_STYLE)
{
	CreateImageList();
	AddRoot("Scene", V4StudioTree::TreeCtrlIcon_Folder, V4StudioTree::TreeCtrlIcon_FolderOpened);
	m_transformNode = NULL;
	m_selectedNode = NULL;

	parent = (V4StudioFrame *) parent_;
}


// Refresh -- redraws the tree starting at the specified node
void V4StudioTree::Refresh(SFNode * node) 
{

	// check if we have to display the dictionnary or not
	if (parent->GetEditDict())
	  node = parent->GetV4SceneManager()->GetDictionnary();


	wxTreeItemId rootId = GetRootItem();
	CollapseAndReset(rootId);
	if (node) {
		AddNodesToItem(rootId, node, -1, -1);
		Expand(rootId);
	}
}

void V4StudioTree::AddNodesToItem(wxTreeItemId parentItemId, SFNode * node, s32 fieldIndex, s32 position) 
{
	FieldInfo field;
	u32 count, i, j;
	char *name;

	if (!node) return;

	// displays the name and the defname it exists
  u32 s = strlen(Node_GetName(node));

  if (Node_GetDefName(node)) {
    s += strlen(Node_GetDefName(node));
    name = new char[s+4];
    strcpy(name, Node_GetName(node));
    strcat(name, " - ");
    strcat(name, Node_GetDefName(node));
  } else {
    name = new char[s+1];
    strcpy(name, Node_GetName(node));
  }

	SFNode * parent = NULL;
	
	V4StudioTreeItemData *parentItemData = (V4StudioTreeItemData *)GetItemData(parentItemId);
	if (parentItemData != NULL) parent = parentItemData->GetNode();

	V4StudioTreeItemData * currentItemData = new V4StudioTreeItemData(node, parent, fieldIndex, position);
	wxTreeItemId nodeItemId;
	if (position == -1) nodeItemId = AppendItem(parentItemId, wxString(name), -1, -1, currentItemData);
	else nodeItemId = InsertItem(parentItemId, position, wxString(name), -1, -1, currentItemData);

  delete [] name;
  name = NULL;

	count = Node_GetFieldCount(node);
	for (i=0;i<count; i++) {		
		Node_GetField(node, i, &field);
		if (field.eventType == ET_EventIn || field.eventType == ET_EventOut) continue;
		switch (field.fieldType) {
		case FT_SFNode:
			if (* (SFNode **) field.far_ptr) {
				AddNodesToItem(nodeItemId, * (SFNode **) field.far_ptr, i, -1);
			}
			break;
		case FT_MFNode:
			{
				Chain *nodes = (* (Chain **) field.far_ptr); // list of children
				u32 nbNodes = ChainGetCount(nodes);
        u8 skipped = 0; // counts nodes not added

				for (j=0; j< nbNodes; j++) {          
          // gets a pointer to the current child
					SFNode *child = (SFNode *)ChainGetEntry(nodes, j);

          // prevents the dictionnary from being added to the graph
          const char * c = Node_GetDefName(child);
          if ( (c != NULL) && !strcmp(c,DICTNAME) ) {
            skipped++;
            continue;
          }

          // recursively adds children
					AddNodesToItem(nodeItemId, child, i, j-skipped);
				}
			}
			break;
		default:
			break;
		}
	}
	Expand(nodeItemId);
}


void V4StudioTree::CreateImageList(int size)
{
    if ( size == -1 ) {
        SetImageList(NULL);
        return;
    }
    // Make an image list containing small icons
    wxImageList *images = new wxImageList(size, size, TRUE);

    // should correspond to TreeCtrlIcon_xxx enum
    wxBusyCursor wait;
    wxIcon icons[5];
    icons[0] = wxIcon(icon1_xpm);
    icons[1] = wxIcon(icon2_xpm);
    icons[2] = wxIcon(icon3_xpm);
    icons[3] = wxIcon(icon4_xpm);
    icons[4] = wxIcon(icon5_xpm);
    int sizeOrig = icons[0].GetWidth();
    for ( size_t i = 0; i < WXSIZEOF(icons); i++ ) {
		if ( size == sizeOrig ) images->Add(icons[i]);
		else images->Add(wxBitmap(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size)));
    }
    AssignImageList(images);
}

void V4StudioTree::OnSelChanged(wxTreeEvent& event) 
{
	wxKeyEvent kevt = event.GetKeyEvent();

	wxTreeItemId itemId = event.GetItem();
	m_selectedItem = itemId;
	if (itemId.IsOk()) {
		V4StudioTreeItemData *itemData = (V4StudioTreeItemData *)GetItemData(itemId);
		if (!itemData) {
			event.Skip();
			return;
		}
		SFNode *itemNode = itemData->GetNode();
		SFNode *itemParentNode = itemData->GetNodeParent();
		if (!itemNode) {
			event.Skip();
			return;
		}
		V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
		mainFrame->UpdateSelection(itemNode, itemParentNode);
	}
}

void V4StudioTree::SetSelectedItem(SFNode *node)
{
	if (m_selectedNode == node) return;
	wxTreeItemId rootId = GetRootItem();
	wxTreeItemId itemId = FindNodeItem(rootId, node);
	m_transformNode = FindTransformNode(itemId);
	itemId = GetItemParent(itemId);
	if (itemId.IsOk()) {
		V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId);
		if (data != NULL) {
			V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
			SFNode *itemNode = data->GetNode();
			m_selectedNode = itemNode;
			SFNode *itemParentNode = data->GetNodeParent();
			mainFrame->UpdateSelection(itemNode, itemParentNode);
		}
		SelectItem(itemId);
	}
}

wxTreeItemId V4StudioTree::FindNodeItem(wxTreeItemId itemId, SFNode *node) 
{
	long cookie;
	V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId);
	if (data != NULL) {
		if (data->GetNode() == node) return itemId;
	} 
	wxTreeItemId ret = (wxTreeItemId)0l;
	wxTreeItemId child = GetFirstChild(itemId, cookie);
	while (child.IsOk()) {
		wxTreeItemId ret = FindNodeItem(child, node);
		if (ret.IsOk()) return ret;
		child = GetNextChild(itemId, cookie);
	}
	return ret;
}

void V4StudioTree::OnItemRightClick(wxTreeEvent &event) 
{
	wxTreeItemId itemId = event.GetItem();
}

void V4StudioTree::OnBeginDrag(wxTreeEvent& event)
{
    // need to explicitly allow drag
    if ( event.GetItem() != GetRootItem() ){
        m_draggedItem = event.GetItem();
		V4StudioTreeItemData *draggedData = (V4StudioTreeItemData *)GetItemData(m_draggedItem);		
        event.Allow();
    } else {
        wxLogMessage(wxT("OnBeginDrag: this item can't be dragged."));
    }
}

void V4StudioTree::OnEndDrag(wxTreeEvent& event)
{
    wxTreeItemId itemSrc = m_draggedItem, itemDst = event.GetItem(), dstParentItem = GetItemParent(itemDst);
    m_draggedItem = (wxTreeItemId)0l;

	V4StudioTreeItemData *srcData = (V4StudioTreeItemData *)GetItemData(itemSrc);
	FieldInfo srcField;
	srcData->GetField(&srcField);
	// Removal of the src item from its parent field
	switch (srcField.fieldType) {
	case FT_SFNode:
		if (* (SFNode **) srcField.far_ptr) {}
		break;
	case FT_MFNode:
		{
			Chain *nodes = (* (Chain **) srcField.far_ptr);
			ChainDeleteEntry(nodes, srcData->GetPosition());
		}
		break;
	default:
		break;
	}

	SFNode *srcNode = srcData->GetNode();
	FieldInfo dstField;
	V4StudioTreeItemData *dstData = (V4StudioTreeItemData *)GetItemData(itemDst);
	dstData->GetField(&dstField);
	// Addition of the src item prior to the dest item
	switch (dstField.fieldType) {
	case FT_SFNode:
		if (* (SFNode **) dstField.far_ptr) {}
		break;
	case FT_MFNode:
		{
			Chain *nodes = (* (Chain **) dstField.far_ptr);
			ChainInsertEntry(nodes, srcNode, dstData->GetPosition());
			Node_SetDirty(dstData->GetNode(), 1);
		}
		break;
	default:
		break;
	}

	SFNode *dstParentNode = dstData->GetNodeParent();
	
	Delete(itemSrc);
	AddNodesToItem(dstParentItem, srcNode, dstData->GetFieldIndex(), dstData->GetPosition());
	V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
	mainFrame->UpdateSelection(srcNode,dstData->GetNodeParent());
	mainFrame->Update();
}

SFNode *V4StudioTree::FindTransformNode(wxTreeItemId itemId) 
{
	SFNode *transformNode = NULL;
	while (true) {
		if (itemId.IsOk()) {
			V4StudioTreeItemData *data = (V4StudioTreeItemData *)GetItemData(itemId);
			if (data != NULL) {
				SFNode *itemNode = data->GetNode();
				u32 tag = Node_GetTag(itemNode);
				if (tag == TAG_MPEG4_Transform2D || tag == TAG_MPEG4_TransformMatrix2D) {
					transformNode = itemNode;
					break;
				}
			} else {
				break;
			}
		} else {
			break;
		}
		itemId = GetItemParent(itemId);
	}
	return transformNode;
}

void V4StudioTree::Translate(int dX, int dY) 
{
	if (m_transformNode) {
		FieldInfo field;
		u32 tag = Node_GetTag(m_transformNode);
		if (tag == TAG_MPEG4_Transform2D){
			Node_GetField(m_transformNode, 7, &field);
			((SFVec2f *)field.far_ptr)->x += dX;
			((SFVec2f *)field.far_ptr)->y += dY;
		} else {
			Node_GetField(m_transformNode, 5, &field);
			*((SFFloat *)field.far_ptr) += dX;
			Node_GetField(m_transformNode, 8, &field);
			*((SFFloat *)field.far_ptr) += dY;
		}
		V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
		mainFrame->GetFieldView()->SetNode(m_transformNode);
		mainFrame->GetFieldView()->Create();
	}
}

void V4StudioTree::Scale(int dX, int dY) 
{
	if (m_transformNode && (dX || dY)) {
		FieldInfo field;
		u32 tag = Node_GetTag(m_transformNode);
		if (tag == TAG_MPEG4_Transform2D){
			Node_GetField(m_transformNode, 5, &field);
			if (dX) ((SFVec2f *)field.far_ptr)->x *= (dX>0?1.01:0.99);
			if (dY) ((SFVec2f *)field.far_ptr)->y *= (dY>0?1.01:0.99);
		} else {
			Node_GetField(m_transformNode, 3, &field);
			if (dX) *((SFFloat *)field.far_ptr) *= (dX>0?1.01:0.99);
			Node_GetField(m_transformNode, 7, &field);
			if (dY) *((SFFloat *)field.far_ptr) *= (dY>0?1.01:0.99);
		}
		V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
		mainFrame->GetFieldView()->SetNode(m_transformNode);
		mainFrame->GetFieldView()->Create();
	}
}


void V4StudioTree::Rotate(int dX, int dY) 
{
	/*quick and dirty of course...*/
	dY *= -1;
	if (m_transformNode && (dX || dY)) {
		u32 tag = Node_GetTag(m_transformNode);
		if (tag == TAG_MPEG4_Transform2D){
			Double ang;
			ang = atan2(dY, dX) / 100;
			((M_Transform2D *) m_transformNode)->rotationAngle += ang;
		} else {
			((M_TransformMatrix2D *) m_transformNode)->mxy += dX;
			((M_TransformMatrix2D *) m_transformNode)->myx += dY;
		}
		V4StudioFrame *mainFrame = (V4StudioFrame *)GetParent();
		mainFrame->GetFieldView()->SetNode(m_transformNode);
		mainFrame->GetFieldView()->Create();
	}
}


