#include "V4SceneManager.h"
#include "V4StudioFrame.h"

#include <gpac/m4_author.h>
#include <gpac/m4_mpeg4_nodes.h>
#include <gpac/intern/m4_scenegraph_dev.h>


V4SceneManager::V4SceneManager(V4StudioFrame *parent) : frame(parent)
{
	m_pSm = NULL;
	m_pIs = NULL;

  units = 1000; // used to calculate AU timings
}

V4SceneManager::~V4SceneManager()
{
	// m_pIs should be deleted here or by the terminal

	// m_pSm should be deleted here or by the terminal
	// TODO : pquoi fallait il commenter cette ligne ?
	//if (m_pSm) M4SM_Delete(m_pSm);
	delete m_gpac_panel;
	m_gpac_panel = NULL;
}

void V4SceneManager::LoadNew() 
{
	void node_init(void *cbkObject, SFNode *node);

	m_pIs = NewInlineScene(NULL);
	SG_SetInitCallback(m_pIs->graph, node_init, this);
	m_gpac_panel = new wxGPACPanel(this, NULL);
	M4Client *term = m_gpac_panel->GetMPEG4Terminal();
	SR_SetSceneGraph(term->renderer, m_pIs->graph);

	m_pSm = NewSceneManager(m_pIs->graph);
	/* Create a BIFS stream with one AU with on ReplaceScene */
	M4StreamContext *sc = M4SM_NewStream(m_pSm, 1, M4ST_SCENE, 0);
	M4AUContext *au = M4SM_NewAU(sc, 0, 0, 1);
	SGCommand *command = SG_NewCommand(m_pIs->graph, SG_SceneReplace);
	ChainAddEntry(au->commands, command);

  SetLength(50);
  SetFrameRate(25);

	// reinitializes the node pool
	pools.Clear();
}

void V4SceneManager::LoadFileOld(const char *path) 
{
	m_gpac_panel = new wxGPACPanel(this, path);
	M4Client *term = m_gpac_panel->GetMPEG4Terminal();

	// Get the RootScene created by the terminal.
	m_pIs = term->root_scene;


	if (0) {
		//Create a Scene Manager based on the current scene_graph
		m_pSm = NewSceneManager(m_pIs->graph);

		/* Loading of a file (BT, MP4 ...) and modification of the SceneManager */
		M4ContextLoader load;
		memset(&load, 0, sizeof(M4ContextLoader));
		load.fileName = path;
		load.ctx = m_pSm;
		load.cbk = this;
		if (strstr(path, ".mp4") || strstr(path, ".MP4") ) load.isom = M4_MovieOpen(path, M4_OPEN_READ);
		M4SM_LoaderInit(&load);
		M4SM_LoaderRun(&load);
		M4SM_LoaderDone(&load);
		if (load.isom) M4_MovieDelete(load.isom);

		/* SceneManager should be initialized and filled correctly */
		SG_SetSizeInfo(m_pIs->graph, m_pSm->scene_width, m_pSm->scene_height, m_pSm->is_pixel_metrics);

		// TODO : replace with GetBifsStream
		M4StreamContext *sc = (M4StreamContext *) ChainGetEntry(m_pSm->streams,0);
		ChainGetCount(m_pSm->streams);

		if (sc->streamType == 3) {
			M4AUContext *au = (M4AUContext *) ChainGetEntry(sc->AUs,0);
		ChainGetCount(sc->AUs);
			SGCommand *c = (SGCommand *) ChainGetEntry(au->commands,0);
		ChainGetCount(au->commands);
			SG_ApplyCommand(m_pIs->graph, c, 0);
			/* This is a patch to solve the save pb:
				When ApplyCommand is made on a Scene Replace Command
				The command node is set to NULL
				When we save a BIFS stream whose first command is of this kind,
				the file saver thinks the bifs commands should come from an NHNT file 
			   This is a temporary patch */
			if (c->tag == SG_SceneReplace) { c->node = m_pIs->graph->RootNode; }
		}
		SR_SetSceneGraph(term->renderer, m_pIs->graph);

		// retrieves all the node from the tree and adds them to the node pool
		SFNode * root = SG_GetRootNode(m_pIs->graph);
		CreateDictionnary();

	}

}

void V4SceneManager::LoadFile(const char *path) 
{	
	m_gpac_panel = new wxGPACPanel(this, NULL);
	M4Client *term = m_gpac_panel->GetMPEG4Terminal();

	// initializes a new scene
	// We need an InlineScene, a SceneManager and an ODManager
	m_pIs = NewInlineScene(NULL);
	m_pSm = NewSceneManager(m_pIs->graph);

	m_pIs->root_od = NewODManager();
	m_pIs->root_od->parentscene = NULL;
	m_pIs->root_od->subscene = m_pIs;
	m_pIs->root_od->term = term;

	term->root_scene = m_pIs;
	
	/* Loading of a file (BT, MP4 ...) and modification of the SceneManager */
	M4ContextLoader load;
	memset(&load, 0, sizeof(M4ContextLoader));
	load.fileName = path;
	load.ctx = m_pSm;
	load.cbk = this;
	if (strstr(path, ".mp4") || strstr(path, ".MP4") ) load.isom = M4_MovieOpen(path, M4_OPEN_READ);
	M4SM_LoaderInit(&load);
	M4SM_LoaderRun(&load);
	M4SM_LoaderDone(&load);
	if (load.isom) M4_MovieDelete(load.isom);

	/* SceneManager should be initialized  and filled correctly */

	SG_SetSizeInfo(m_pIs->graph, m_pSm->scene_width, m_pSm->scene_height, m_pSm->is_pixel_metrics);

	// TODO : replace with GetBifsStream
	M4StreamContext *sc = (M4StreamContext *) ChainGetEntry(m_pSm->streams,0);

	if (sc->streamType == 3) {
		M4AUContext *au = (M4AUContext *) ChainGetEntry(sc->AUs,0);
		SGCommand *c = (SGCommand *) ChainGetEntry(au->commands,0);
		SG_ApplyCommand(m_pIs->graph, c, 0);
		/* This is a patch to solve the save pb:
		    When ApplyCommand is made on a Scene Replace Command
		    The command node is set to NULL
		    When we save a BIFS stream whose first command is of this kind,
		    the file saver thinks the bifs commands should come from an NHNT file 
		   This is a temporary patch */
		if (c->tag == SG_SceneReplace) { c->node = m_pIs->graph->RootNode; }
	}
	SR_SetSceneGraph(term->renderer, m_pIs->graph);

  // TODO : read actual values from file
  SetLength(50);
  SetFrameRate(25);

	// retrieves all the node from the tree and adds them to the node pool
	SFNode * root = SG_GetRootNode(m_pIs->graph);
	CreateDictionnary();
}

void V4SceneManager::SaveFile(const char *path) 
{
	char rad_name[5000];
	strcpy(rad_name, "dump");
	M4SM_DumpToText(m_pSm, rad_name, 0);
	M4File *mp4 = M4_MovieOpen(path, M4_WRITE_EDIT);
	m_pSm->max_node_id = SG_GetMaxNodeID(m_pSm->scene_graph);
	M4SM_EncodeFile(m_pSm, mp4, "c:\\log.txt", NULL, M4CL_MPEG4_STRICT, 0);
	M4_MovieClose(mp4);
}

void V4SceneManager::SetSceneSize(int w, int h)
{
	SG_SetSizeInfo(m_pIs->graph, w,h, 1);
	/*reassign scene graph to update scene size in renderer*/
	SR_SetSceneGraph(m_gpac_panel->GetMPEG4Terminal()->renderer, m_pIs->graph);
}

void V4SceneManager::GetSceneSize(wxSize &size) 
{
	SG_GetSizeInfo(m_pIs->graph, (u32 *)&(size.x), (u32 *)&(size.y));	
}

SFNode *V4SceneManager::SetTopNode(u32 tag) 
{
	M_OrderedGroup * root = (M_OrderedGroup *)NewNode(TAG_MPEG4_OrderedGroup);
	SG_SetRootNode(m_pIs->graph, (SFNode *)root);
	return (SFNode *)root;
}


SFNode *V4SceneManager::NewNode(u32 tag)
{
	SFNode *n = SG_NewNode(m_pIs->graph, tag);
	if (!n) return NULL;
	Node_Init(n);
	return n;
}

SFNode *V4SceneManager::CopyNode(SFNode *node, SFNode *parent, bool copy) 
{
	if (copy) return CloneNodeForEditing(m_pIs->graph, node);
	u32 nodeID = Node_GetID(node);
	if (!nodeID) {
		nodeID = SG_GetNextAvailableNodeID(m_pIs->graph);
		Node_SetDEF(node, nodeID, NULL);
		Node_Register(node, parent);
	}
	return node;
}

extern "C" {

SFNode *CloneNodeForEditing(LPSCENEGRAPH inScene, SFNode *orig) //, SFNode *cloned_parent)
{
	u32 i, j, count;
	SFNode *node, *child, *tmp;
	Chain *list, *list2;
	FieldInfo field_orig, field;

	/*this is not a mistake*/
	if (!orig) return NULL;

	/*check for DEF/USE
	if (orig->sgprivate->NodeID) {
		node = SG_FindNode(inScene, orig->sgprivate->NodeID);
		//node already created, USE
		if (node) {
			Node_Register(node, cloned_parent);
			return node;
		}
	}
	*/
	/*create a node*/
/*
	if (orig->sgprivate->tag == TAG_MPEG4_ProtoNode) {
		proto_node = ((ProtoInstance *)orig)->proto_interface;
		//create the instance but don't load the code -c we MUST wait for ISed routes to be cloned before
		node = Proto_CreateNode(inScene, proto_node, (ProtoInstance *) orig);
	} else {
*/
	node = SG_NewNode(inScene, Node_GetTag(orig));
//	}

	count = Node_GetFieldCount(orig);

/*
#ifdef M4_DEF_Script
	if (orig->sgprivate->tag==TAG_MPEG4_Script) 
		Script_PrepareClone(node, orig);
#endif
*/
	/*copy each field*/
	for (i=0; i<count; i++) {
		Node_GetField(orig, i, &field_orig);

		/*get target ptr*/
		Node_GetField(node, i, &field);

		assert(field.eventType==field_orig.eventType);
		assert(field.fieldType==field_orig.fieldType);

		/*duplicate it*/
		switch (field.fieldType) {
		case FT_SFNode:
			child = CloneNodeForEditing(inScene, (SFNode *) (* ((SFNode **) field_orig.far_ptr)));//, node);
			*((SFNode **) field.far_ptr) = child;
			break;
		case FT_MFNode:
			list = *( (Chain **) field_orig.far_ptr);
			list2 = *( (Chain **) field.far_ptr);

			for (j=0; j<ChainGetCount(list); j++) {
				tmp = (SFNode *)ChainGetEntry(list, j);
				child = CloneNodeForEditing(inScene, tmp);//, node);
				ChainAddEntry(list2, child);
			}
			break;
		default:
			VRML_FieldCopy(field.far_ptr, field_orig.far_ptr, field.fieldType);
			break;
		}
	}
	/*register node
	if (orig->sgprivate->NodeID) {
		Node_SetID(node, orig->sgprivate->NodeID);
		Node_Register(node, cloned_parent);
	}*/

	/*init node before creating ISed routes so the eventIn handler are in place*/
	if (Node_GetTag(node) != TAG_ProtoNode) Node_Init(node);

	return node;
}

} // extern "C"


// GetBifsStream -- gets the first stream with id = 3
M4StreamContext * V4SceneManager::GetBifsStream() {
  M4StreamContext * ctx = NULL;
  u32 count = ChainGetCount(m_pSm->streams);

  // cycle trough each stream
  while (count--) {
    ctx = (M4StreamContext *) ChainGetEntry(m_pSm->streams, count);
    if (ctx->streamType == 3) break;
  }

  if (ctx == NULL) return NULL;

  // if we went through the loop without finding any BIFS stream return null
  if (ctx->streamType != 3) return NULL;

  return ctx;
}


// CreateDictionnary -- Create the root node of the dictionnary and populates it
void V4SceneManager::CreateDictionnary() {
  SFNode * root = SG_GetRootNode(m_pIs->graph);
  dictionnary = NewNode(TAG_MPEG4_Switch);

  // Insert the dictionnary in the scene and gives it a name
  Node_InsertChild(root, dictionnary, 0);
  Node_Register(dictionnary, root);
  Node_SetDEF(dictionnary, SG_GetNextAvailableNodeID(m_pIs->graph), DICTNAME);

  // makes the dictionnary invisible
  // the number 1 field is the "whichChoice", setting it to -1 makes it display nothing
  FieldInfo field;
  Node_GetField(dictionnary, 1, &field);
  frame->GetFieldView()->SetFieldValue(field, &wxString("-1"), -1);  // TODO : maybe put the method SetFieldValue somewhere more accessible

  // populates dictionnary with the root node of the scene
  AddToDictionnary(root);
}


// AddToDictionnary -- adds a node to the dictionnary, increases its reference count, the node must already have an ID
// WARNING : does not add the node to the pool if it has a parent in the dictionnary
void V4SceneManager::AddToDictionnary(SFNode * node) {

  // checks if nodes has a parent with an id
  // if true then there is already a reference to this node 
  if (HasDefParent(node)) return;

  AddRecursive(node);
}


// AddRecursive -- recursively adds items with an ID to the dictionnary
void V4SceneManager::AddRecursive(SFNode * node, bool parentAdded) {

  // skips empty nodes
  if (!node) return;

  // skips the dictionnary
  const char * c = Node_GetDefName(node);
  if ( (c != NULL) && (!strcmp(c, DICTNAME)) ) return;

  // if node as an id adds it to the dictionnary and the node pool
  u32 id = Node_GetID(node);
  if (id) {
    pools.Add(node);
    // children of added node are not added to the dictionnary
    if (!parentAdded) AddEffective(node);
    parentAdded = true;
  }
  
  FieldInfo field;
  Chain * list;
  int count = Node_GetFieldCount(node);

  // tests all fields, if a field is a node then adds it and process its children recursively
  for (int i=0; i<count; i++) {

    Node_GetField(node, i, &field);

    // single value node field
    if (field.fieldType == FT_SFNode)
      AddRecursive( * ((SFNode **) field.far_ptr), parentAdded );

    // multiple value node field
    if (field.fieldType == FT_MFNode) {
      list = *( (Chain **) field.far_ptr);
			for (u32 j=0; j<ChainGetCount(list); j++)
				AddRecursive( (SFNode *) ChainGetEntry(list, j), parentAdded );
    }
  }
}


// AddEffective --
void V4SceneManager::AddEffective(SFNode * node) {

  // gets the node type (equals pool number)
  u32 poolN = pools.poolN(Node_GetTag(node));

  // adds the node using a type specific function
  switch (poolN) {
    case DICT_GEN: {
      AddGen(node);
      break;
    }

    case DICT_GEOMETRY: {
      AddGeometry(node);
      break;
    }

    case DICT_APPEARANCE: {
      AddAppearance(node);
      break;
    }

    case DICT_TEXTURE: {
      AddTexture(node);
      break;
    }

    case DICT_MATERIAL: {
      AddMaterial(node);
      break;
    }

    // TODO : raise an exception when not found
  }

}


// RemoveFromDictionnary -- Removes _ONE_ item from the dictionnary, if some of the children are also in the dictionnary preserves them
void V4SceneManager::RemoveFromDictionnary(SFNode * node) {

}


/******* Add element to dictionnary functions *******/

// TODO : can add genericity around here
// ATTENTION, all these functions thinks the node as already been assigned an ID

// Add gen -- adds any element which can be a children of a switch node
void V4SceneManager::AddGen(SFNode * node) {

  // retrieves the chain of children (field number 0 named "choice")
  FieldInfo field;
  Chain * list;

  Node_GetField(dictionnary, 0, &field);
  list = *( (Chain **) field.far_ptr);

  // adds a reference to that node in the dictionnary
  ChainAddEntry(list, node);
  Node_Register(node, dictionnary);

}


// TODO : check if there is a dummy node with free space in it


// AddGeometry -- adds any element which can be a children of a shape as a geometry node
void V4SceneManager::AddGeometry(SFNode * node) {

  // creates a dummy node to hold this one
  SFNode * parent = CreateDummyNode(TAG_MPEG4_Shape, dictionnary, 0);

  // sets the parentship relation
  MakeChild(node, parent, 1); // geometry is field one in a shape

}


// AddAppearance -- adds any element which can be a children of a shape as an appearance node
void V4SceneManager::AddAppearance(SFNode * node) {

  // creates a dummy node to hold this one
  SFNode * parent = CreateDummyNode(TAG_MPEG4_Shape, dictionnary, 0);

  // sets the parentship relation
  MakeChild(node, parent, 0); // appearance is field zero in a shape
}


// AddTexture -- adds any element which can be a children of an appearance as a texture node
void V4SceneManager::AddTexture(SFNode * node) {

  // creates dummy nodes to hold this one
  SFNode * parent1 = CreateDummyNode(TAG_MPEG4_Shape, dictionnary, 0);
  SFNode * parent2 = CreateDummyNode(TAG_MPEG4_Appearance, parent1, 3);

  // sets the parentship relation
  MakeChild(node, parent2, 1); // texture is field one in a appearance

}


// AddMaterial -- adds any element which can be a children of an appearance as a material node
void V4SceneManager::AddMaterial(SFNode * node) {

    // creates dummy nodes to hold this one
  SFNode * parent1 = CreateDummyNode(TAG_MPEG4_Shape, dictionnary, 0);
  SFNode * parent2 = CreateDummyNode(TAG_MPEG4_Appearance, parent1, 3);

  // sets the parentship relation
  MakeChild(node, parent2, 0); // material is field zero in appearance
}


// CreateDummyNode -- creates a node to hold another one, adds this node to given field of the given parent
// (designed for use with nodes in the dictionnary)
SFNode * V4SceneManager::CreateDummyNode(const u32 tag, SFNode * parent, const u32 fieldIndex) {

  // creates a new node for this scene
  SFNode * newNode = NewNode(tag);

  // sets the parentship relation !! AND registers the node !!
  MakeChild(newNode, parent, fieldIndex);

  return newNode;
}


// MakeChild -- Sets the first node as a child of the second using the given field
void V4SceneManager::MakeChild(SFNode * child, SFNode * parent, const u32 fieldIndex) {
  // get the field where we have to add the new child
  FieldInfo field;
  Node_GetField(parent, fieldIndex, &field);

  // keeps the count of uses up to date
  Node_Register(child, parent);

  // if the field is single valued
  if (field.fieldType == FT_SFNode) {
    *((SFNode **) field.far_ptr) = child;
  }

  // if the field is multi valued
  if (field.fieldType == FT_MFNode)
    ChainAddEntry( *((Chain **)field.far_ptr), child );

}


// GetDictionnary -- access to the dictionnary
SFNode * V4SceneManager::GetDictionnary() const {
  return dictionnary;  
}


// HasDefParent -- checks if an ancestor of this node has an ID
SFNode * V4SceneManager::HasDefParent(SFNode * node) {
  
  assert(node);

  // also checks the node itself

  int count = Node_GetParentCount(node);
  SFNode * parent;

  // loops through all the parents and recurses
  for (int i=0; i<count; i++) {
    parent = Node_GetParent(node, i);
    const char * name = Node_GetDefName(parent);
    if (name) return parent;

    parent = HasDefParent(parent);
    if (parent) return parent;
  }

  return NULL;
}

void node_init(void *cbkObject, SFNode *node)
{
	switch (Node_GetTag(node)) {
	case TAG_MPEG4_Conditional:
	case TAG_MPEG4_QuantizationParameter:
		break;
	default:
		SR_NodeInit(((V4SceneManager *)cbkObject)->GetSceneRenderer(), node);
		break;
	}
}

void V4SceneManager::CreateIDandAddToPool(SFNode *node)
{
	// gets a node ID
	u32 id = SG_GetNextAvailableNodeID(GetSceneGraph());

	char d[10]; // supposes no more than 1 000 000 000 objects

	// queries the pool to know which number to use for the name
	itoa(pools.GetCount(Node_GetTag(node)), d, 10); 

	char c[50];
	strcpy(c, Node_GetName(node));
	strncat(c, "_", 1);
	strncat(c, d, 10);

	Node_SetDEF(node, id, c); // assigns NodeID and name
	pools.Add(node); // adds to the node pool

}


// SetLength -- updates the number of cells in the timeline
void V4SceneManager::SetLength(const u32 length_) {
  length = length_;
  frame->GetTimeLine()->SetLength(length_);
}