void CStateTable::remove( const CNode &node )
	{
		/* remove self from path map */
		const string &path = node.get_path();
		if( m_nodes.count( path ) != 1 )
		{
			INTEGRA_TRACE_ERROR << "missing key in state table: " << path;
		}
		else
		{
			m_nodes.erase( path );
		}

		/* remove self from id map */
		internal_id id = node.get_id();
		if( m_nodes_by_id.count( id ) != 1 )
		{
			INTEGRA_TRACE_ERROR << "missing key in state table: " << id;
		}
		else
		{
			m_nodes_by_id.erase( id );
		}

		/* remove node endpoints */
		const node_endpoint_map node_endpoints = node.get_node_endpoints();
		for( node_endpoint_map::const_iterator i = node_endpoints.begin(); i != node_endpoints.end(); i++ )
		{
			const INodeEndpoint *node_endpoint = i->second;
			const string &path = node_endpoint->get_path();
			if( m_node_endpoints.count( path ) != 1 )
			{
				INTEGRA_TRACE_ERROR << "missing key in state table: " << path;
			}
			else
			{
				m_node_endpoints.erase( path );
			}
		}

		/* remove child nodes */
		const node_map &children = node.get_children();
		for( node_map::const_iterator i = children.begin(); i != children.end(); i++ )
		{
			remove( *CNode::downcast_writable( i->second ) );
		}
	}
	void CStateTable::add( CNode &node )
	{
		/* add self to path map */
		const string &path = node.get_path();
		if( m_nodes.count( path ) > 0 )
		{
			INTEGRA_TRACE_ERROR << "duplicate key in state table: " << path;
		}
		else
		{
			m_nodes[ path ] = &node;
		}

		/* add self to id map */
		internal_id id = node.get_id();
		if( m_nodes_by_id.count( id ) > 0 )
		{
			INTEGRA_TRACE_ERROR << "duplicate key in state table: " << id;
		}
		else
		{
			m_nodes_by_id[ id ] = &node;
		}

		/* add node endpoints */
		node_endpoint_map node_endpoints = node.get_node_endpoints_writable();
		for( node_endpoint_map::const_iterator i = node_endpoints.begin(); i != node_endpoints.end(); i++ )
		{
			INodeEndpoint *node_endpoint = i->second;
			const string &path = node_endpoint->get_path();
			if( m_node_endpoints.count( path ) > 0 )
			{
				INTEGRA_TRACE_ERROR << "duplicate key in state table: " << path;
			}
			else
			{
				m_node_endpoints[ path ] = node_endpoint;
			}
		}

		/* add child nodes */
		node_map &children = node.get_children_writable();
		for( node_map::iterator i = children.begin(); i != children.end(); i++ )
		{
			add( *CNode::downcast_writable( i->second ) );
		}
	}
	void CPlayerHandler::handle_path_change( const CNode &player_node )
	{
		pthread_mutex_lock( &m_mutex );

		player_state_map::iterator lookup = m_player_states.find( player_node.get_id() );
		if( lookup != m_player_states.end() )
		{
			CPlayerState *player_state = lookup->second;

			player_state->m_tick_path = player_node.get_path();
			player_state->m_tick_path.append_element( CPlayerLogic::endpoint_tick );

			player_state->m_play_path = player_node.get_path();
			player_state->m_play_path.append_element( CPlayerLogic::endpoint_play );
		}

		pthread_mutex_unlock( &m_mutex );	
	}
	void CPlayerHandler::handle_delete( const CNode &player_node )
	{
		stop_player( player_node.get_id() );
	}
	void CPlayerHandler::update( const CNode &player_node )
	{
		const INodeEndpoint *play_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_play );
		const INodeEndpoint *active_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_active );
		const INodeEndpoint *tick_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_tick );
		assert( play_endpoint && active_endpoint && tick_endpoint );

		int play_value = *play_endpoint->get_value();
		int active_value = *active_endpoint->get_value();

		if( play_value == 0 || active_value == 0 )
		{
			stop_player( player_node.get_id() );
			return;
		}

		/*
		lookup player attributes
		*/
		const INodeEndpoint *rate_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_rate );
		const INodeEndpoint *loop_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_loop );
		const INodeEndpoint *start_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_start );
		const INodeEndpoint *end_endpoint = player_node.get_node_endpoint( CPlayerLogic::endpoint_end );

		assert( rate_endpoint && loop_endpoint && start_endpoint && end_endpoint );

		pthread_mutex_lock( &m_mutex );

		/*
		see if the player is already playing
		*/

		CPlayerState *player_state = NULL;

		internal_id player_id = player_node.get_id();
		player_state_map::iterator lookup = m_player_states.find( player_id );
		if( lookup == m_player_states.end() )
		{
			/* create new player if not already playing */
			player_state = new CPlayerState;
			player_state->m_id = player_id;
			m_player_states[ player_id ] = player_state;
		}
		else
		{
			/*this player is already playing - update it*/
			player_state = lookup->second;
		}

		/* recreate paths, in case they have changed */
		player_state->m_tick_path = player_node.get_path();
		player_state->m_tick_path.append_element( CPlayerLogic::endpoint_tick );

		player_state->m_play_path = player_node.get_path();
		player_state->m_play_path.append_element( CPlayerLogic::endpoint_play );

		/*
		setup all other player state fields
		*/

		player_state->m_initial_ticks = *tick_endpoint->get_value(); 
		player_state->m_previous_ticks = player_state->m_initial_ticks; 
		player_state->m_rate = *rate_endpoint->get_value();
		player_state->m_start_msecs = get_current_msecs();

		int loop_value = *loop_endpoint->get_value();
		player_state->m_loop = ( loop_value != 0 );
		player_state->m_loop_start_ticks = *start_endpoint->get_value();
		player_state->m_loop_end_ticks = *end_endpoint->get_value();

		pthread_mutex_unlock( &m_mutex );
	}