/*! \brief Helper function that creates an outgoing channel and returns it immediately */
static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context)
{
	char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
	struct ast_channel *chan;
	int cause;

	/* Fill the variable with the extension and context we want to call */
	snprintf(destination, sizeof(destination), "%s@%s", exten, context);

	/* Now we request that chan_local prepare to call the destination */
	if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) {
		return NULL;
	}

	/* Before we actually dial out let's inherit appropriate information. */
	ast_channel_lock_both(caller, chan);
	ast_connected_line_copy_from_caller(&chan->connected, &caller->caller);
	ast_channel_inherit_variables(caller, chan);
	ast_channel_datastore_inherit(caller, chan);
	ast_channel_unlock(chan);
	ast_channel_unlock(caller);

	/* Since the above worked fine now we actually call it and return the channel */
	if (ast_call(chan, destination, 0)) {
		ast_hangup(chan);
		return NULL;
	}

	return chan;
}
static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target)
{
	struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
	struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
	enum ast_rtp_glue_result native_type;
	struct ast_rtp_glue *glue0, *glue1 = NULL;
	RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);

	if (bc0 == bc1) {
		return;
	}

	ast_channel_lock_both(bc0->chan, bc1->chan);
	native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);

	switch (native_type) {
	case AST_RTP_GLUE_RESULT_LOCAL:
		if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
			ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL);
		}
		if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) {
			ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL);
		}
		ast_rtp_instance_set_bridged(instance0, NULL);
		if (instance1) {
			ast_rtp_instance_set_bridged(instance1, NULL);
		}
		break;
	case AST_RTP_GLUE_RESULT_REMOTE:
		if (target) {
			/*
			 * If a target was provided, it is being put on hold and should expect to
			 * receive media from Asterisk instead of what it was previously connected to.
			 */
			if (bc0->chan == target) {
				glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
			} else {
				glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
			}
		}
		break;
	case AST_RTP_GLUE_RESULT_FORBID:
		break;
	}

	if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) {
		glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0);
		glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0);
	}

	ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n",
		ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));

	ast_channel_unlock(bc0->chan);
	ast_channel_unlock(bc1->chan);
}
/* 
 * \brief Send a pvt in with no locks held and get all locks
 *
 * \note NO locks should be held prior to calling this function
 * \note The pvt must have a ref held before calling this function
 * \note if outchan or outowner is set != NULL after calling this function
 *       those channels are locked and reffed.
 * \note Batman.
 */
static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, struct ast_channel **outowner)
{
	struct ast_channel *chan = NULL;
	struct ast_channel *owner = NULL;

	for (;;) {
		ao2_lock(p);
		if (p->chan) {
			chan = p->chan;
			ast_channel_ref(chan);
		}
		if (p->owner) {
			owner = p->owner;
			ast_channel_ref(owner);
		}
		ao2_unlock(p);

		/* if we don't have both channels, then this is very easy */
		if (!owner || !chan) {
			if (owner) {
				ast_channel_lock(owner);
			} else if(chan) {
				ast_channel_lock(chan);
			}
			ao2_lock(p);
		} else {
			/* lock both channels first, then get the pvt lock */
			ast_channel_lock_both(chan, owner);
			ao2_lock(p);
		}

		/* Now that we have all the locks, validate that nothing changed */
		if (p->owner != owner || p->chan != chan) {
			if (owner) {
				ast_channel_unlock(owner);
				owner = ast_channel_unref(owner);
			}
			if (chan) {
				ast_channel_unlock(chan);
				chan = ast_channel_unref(chan);
			}
			ao2_unlock(p);
			continue;
		}

		break;
	}
	*outowner = p->owner;
	*outchan = p->chan;
}
/*!
 * \internal
 * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly
 *        identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the
 *        local channel and the channel that instigated the park.
 */
static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data)
{
	char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
	struct ast_channel *parkee;
	struct ast_channel *parkee_side_2;
	int cause;

	/* Fill the variable with the extension and context we want to call */
	snprintf(destination, sizeof(destination), "%s@%s", exten, context);

	/* Now we request that chan_local prepare to call the destination */
	parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination,
		&cause);
	if (!parkee) {
		return NULL;
	}

	/* Before we actually dial out let's inherit appropriate information. */
	ast_channel_lock_both(parker, parkee);
	ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT);
	ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker));
	ast_channel_inherit_variables(parker, parkee);
	ast_channel_datastore_inherit(parker, parkee);
	ast_channel_unlock(parker);

	parkee_side_2 = ast_local_get_peer(parkee);
	ast_assert(parkee_side_2 != NULL);
	ast_channel_unlock(parkee);

	/* We need to have the parker subscribe to the new local channel before hand. */
	if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) {
		ast_channel_unref(parkee_side_2);
		ast_hangup(parkee);
		return NULL;
	}

	ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0);

	ast_channel_unref(parkee_side_2);

	/* Since the above worked fine now we actually call it and return the channel */
	if (ast_call(parkee, destination, 0)) {
		ast_hangup(parkee);
		return NULL;
	}

	return parkee;
}
static int native_rtp_bridge_compatible(struct ast_bridge *bridge)
{
	struct ast_bridge_channel *bc0;
	struct ast_bridge_channel *bc1;
	int is_compatible;

	/* We require two channels before even considering native bridging */
	if (bridge->num_channels != 2) {
		ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n",
			bridge->uniqueid);
		return 0;
	}

	bc0 = AST_LIST_FIRST(&bridge->channels);
	bc1 = AST_LIST_LAST(&bridge->channels);

	ast_channel_lock_both(bc0->chan, bc1->chan);
	is_compatible = native_rtp_bridge_compatible_check(bridge, bc0, bc1);
	ast_channel_unlock(bc0->chan);
	ast_channel_unlock(bc1->chan);

	return is_compatible;
}
/*!
 * \internal
 * \brief Start native RTP bridging of two channels
 *
 * \param bridge The bridge that had native RTP bridging happening on it
 * \param target If remote RTP bridging, the channel that is unheld.
 *
 * \note Bridge must be locked when calling this function.
 */
static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target)
{
	struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels);
	struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels);
	enum ast_rtp_glue_result native_type = AST_RTP_GLUE_RESULT_FORBID;
	struct ast_rtp_glue *glue0, *glue1;
	RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup);
	RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup);
	RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
	RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);

	if (bc0 == bc1) {
		return;
	}

	ast_channel_lock_both(bc0->chan, bc1->chan);
	if (!bc0->suspended && !bc1->suspended) {
		native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1);
	}

	switch (native_type) {
	case AST_RTP_GLUE_RESULT_LOCAL:
		if (ast_rtp_instance_get_engine(instance0)->local_bridge) {
			ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1);
		}
		if (ast_rtp_instance_get_engine(instance1)->local_bridge) {
			ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0);
		}
		ast_rtp_instance_set_bridged(instance0, instance1);
		ast_rtp_instance_set_bridged(instance1, instance0);
		ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n",
			ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
		break;

	case AST_RTP_GLUE_RESULT_REMOTE:
		if (glue0->get_codec) {
			glue0->get_codec(bc0->chan, cap0);
		}
		if (glue1->get_codec) {
			glue1->get_codec(bc1->chan, cap1);
		}

		/* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */
		if (!target) {
			glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
			glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
			ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n",
				ast_channel_name(bc0->chan), ast_channel_name(bc1->chan));
		} else {
			/*
			 * If a target was provided, it is the recipient of an unhold or an update and needs to have
			 * its media redirected to fit the current remote bridging needs. The other channel is either
			 * already set up to handle the new media path or will have its own set of updates independent
			 * of this pass.
			 */
			if (bc0->chan == target) {
				glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0);
			} else {
				glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0);
			}
		}
		break;
	case AST_RTP_GLUE_RESULT_FORBID:
		break;
	}

	ast_channel_unlock(bc0->chan);
	ast_channel_unlock(bc1->chan);
}