#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_MCSNC);
/*
 *	channel.cpp
 *
 *	Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY
 *
 *	Abstract:
 *		This is the implementation file for class Channel.  It contains the
 *		code necessary to implement static and assigned channels in the
 *		MCS system.
 *
 *		This is also to be the base class for other classes that represent
 *		channels in the system.  Therefore, there will be times when some
 *		of these member functions are overridden to provide different
 *		behavior.  These derived classes may or may not invoke the operations
 *		in this class.
 *
 *	Protected Instance Variables:
 *		Channel_ID
 *			This instance variable contains the channel ID that is associated
 *			with a given instance of this class.
 *		m_pDomain
 *			This is a pointer to the local provider.  Note that no messages
 *			ever sent to this provider.  This pointer is used as a parameter
 *			whenever other MCS commands are issued, since this class acts on
 *			behalf of the local provider.
 *		m_pConnToTopProvider
 *			This is a pointer to the Top Provider.  This is used when it is
 *			necessary to send requests to the Top Provider.
 *		m_pChannelList2
 *			This is a reference to the channel list that is owned and maintained
 *			by the parent domain.  It is NEVER modified by this class.
 *		m_JoinedAttachmentList
 *			This is a container that contains the list of attachments currently
 *			joined to the channel.
 *
 *	Private Member Functions:
 *		None.
 *
 *	Caveats:
 *		None.
 *
 *	Author:
 *		James P. Galvin, Jr.
 */
/*
 *	Channel ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is the primary constructor for the Channel class.  It simply
 *		initializes the instance variable to valid values.  It leaves the
 *		attachment list empty.
 */
Channel::Channel (
        ChannelID			channel_id,
        PDomain             local_provider,
        PConnection         top_provider,
        CChannelList2      *channel_list,
        CAttachmentList    *attachment_list)
:
    Channel_ID (channel_id),
    m_pDomain(local_provider),
    m_pConnToTopProvider(top_provider),
    m_pChannelList2(channel_list),
    m_pAttachmentList(attachment_list)
{
}

/*
 *	Channel ()
 *
 *	Public
 *
 *	Functional Description:
 *		This version of the constructor is used to create a Channel object
 *		with an existing attachment.  It is otherwise the same as the primary
 *		constructor above.
 */
Channel::Channel (
        ChannelID			channel_id,
        PDomain             local_provider,
        PConnection         top_provider,
        CChannelList2      *channel_list,
        CAttachmentList    *attachment_list,
        PConnection         pConn)
:
    Channel_ID (channel_id),
    m_pDomain(local_provider),
    m_pConnToTopProvider(top_provider),
    m_pChannelList2(channel_list),
    m_pAttachmentList(attachment_list)
{
	/*
	 *	Add the initial attachment to the attachment list.
	 */
	if (pConn != NULL)
		m_JoinedAttachmentList.Append(pConn);
}

/*
 *	~Channel ()
 *
 *	Public
 *
 *	Functional Description:
 *		If the object is destroyed before the attachment list is empty, it is
 *		the responsibility of this destructor to issue channel leave indications
 *		to all locally joined users.
 */
Channel::~Channel ()
{
	CAttachment        *pAtt;
	//DWORD				type;

	/*
	 *	Iterate through the joined attachment list sending channel leave
	 *	indications to all users who are locally attached to this provider.
	 */
	m_JoinedAttachmentList.Reset();
	while (NULL != (pAtt = m_JoinedAttachmentList.Iterate()))
	{
		if (m_pAttachmentList->Find(pAtt) && pAtt->IsUserAttachment())
		{
		    PUser pUser = (PUser) pAtt;
			pUser->ChannelLeaveIndication(REASON_CHANNEL_PURGED, Channel_ID);
		}
	}
}

/*
 *	Channel_Type		GetChannelType ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function returns the type of the channel.  For a Channel object,
 *		this will always be either STATIC_CHANNEL or ASSIGNED_CHANNEL, depending
 *		on the value of the channel ID.
 */
Channel_Type Channel::GetChannelType ()
{
	/*
	 *	T.125 specifies that channels from 1 to 1000 are static.  The rest
	 *	are dynamic (for this type of Channel object, that equates to
	 *	assigned).
	 */
	return (Channel_ID <= 1000) ? STATIC_CHANNEL : ASSIGNED_CHANNEL;
}

/*
 *	BOOL	IsValid ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function returns TRUE if the Channel object is still valid, or
 *		FALSE if it is ready to be deleted.
 */
BOOL	Channel::IsValid ()
{
	CAttachment        *pAtt;
	CAttachmentList     deletion_list;

	/*
	 *	Iterate through the joined attachment list, building a list of those
	 *	attachments in the list that are no longer valid.
	 */
	m_JoinedAttachmentList.Reset();
	while (NULL != (pAtt = m_JoinedAttachmentList.Iterate()))
	{
		if (m_pAttachmentList->Find(pAtt) == FALSE)
			deletion_list.Append(pAtt);
	}

	/*
	 *	Iterate through the deletion list, removing all those attachments that
	 *	were found to be invalid above.
	 */
	while (NULL != (pAtt = deletion_list.Get()))
	{
		m_JoinedAttachmentList.Remove(pAtt);
	}

	return (! m_JoinedAttachmentList.IsEmpty());
}

/*
 *	Void	IssueMergeRequest ()
 *
 *	Public
 *
 *	Functional Description:
 *		This member function is used to cause the Channel object to issue a
 *		merge request to the pending top provier.
 */
Void	Channel::IssueMergeRequest ()
{
	Channel_Type			channel_type;
	ChannelAttributes		channel_attributes;
	CChannelAttributesList	merge_channel_list;
	CChannelIDList			purge_channel_list;

	if (m_pConnToTopProvider != NULL)
	{
		/*
		 *	Fill in the fields of the channel attributes structure so that it
		 *	accurately describes this channel.  Then put the structure into the
		 *	merge channel list.
		 */
		channel_type = GetChannelType ();
		channel_attributes.channel_type = channel_type;
		switch (channel_type)
		{
			case STATIC_CHANNEL:
				channel_attributes.u.static_channel_attributes.channel_id =
						Channel_ID;
				break;

			case ASSIGNED_CHANNEL:
				channel_attributes.u.assigned_channel_attributes.channel_id =
						Channel_ID;
				break;
		}
		merge_channel_list.Append(&channel_attributes);

		/*
		 *	Send the merge request to the indicated provider.
		 */
		m_pConnToTopProvider->MergeChannelsRequest(&merge_channel_list, &purge_channel_list);
	}
}

/*
 *	Void	ChannelJoinRequest ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to add a new attachment to the attachment list.
 *		If the user ID is valid, this routine will also issue an automatic
 *		join confirm to the user.
 */
Void	Channel::ChannelJoinRequest (
				CAttachment        *pOrigAtt,
				UserID				uidInitiator,
				ChannelID			channel_id)
{
	/*
	 *	Make sure the attachment isn't already in the list before adding it.
	 */
	if (m_JoinedAttachmentList.Find(pOrigAtt) == FALSE)
	{
		TRACE_OUT (("Channel::ChannelJoinRequest: "
				"user %04X joining channel %04X", (UINT) uidInitiator, (UINT) Channel_ID));

		m_JoinedAttachmentList.Append(pOrigAtt);
	}

	/*
	 *	If the user ID is valid, then send a join confirm to the initiating
	 *	attachment.  Note that setting the user ID to 0 is a way of disabling
	 *	this behavior.  This is sometimes useful when adding attachments during
	 *	a domain merge.
	 */
	if (uidInitiator != 0)
	{
		pOrigAtt->ChannelJoinConfirm(RESULT_SUCCESSFUL, uidInitiator, channel_id, Channel_ID);
    }
}

/*
 *	Void	ChannelJoinConfirm ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function performs the same operation as JoinRequest above.
 */
Void	Channel::ChannelJoinConfirm (
				CAttachment        *pOrigAtt,
				Result,
				UserID				uidInitiator,
				ChannelID			requested_id,
				ChannelID)
{
	/*
	 *	Make sure the attachment isn't already in the list before adding it.
	 */
	if (m_JoinedAttachmentList.Find(pOrigAtt) == FALSE)
	{
		TRACE_OUT (("Channel::ChannelJoinConfirm: "
				"user %04X joining channel %04X", (UINT) uidInitiator, (UINT) Channel_ID));

		m_JoinedAttachmentList.Append(pOrigAtt);
	}

	/*
	 *	Send a join confirm to the initiating attachment.
	 */
	pOrigAtt->ChannelJoinConfirm(RESULT_SUCCESSFUL, uidInitiator, requested_id, Channel_ID);
}

/*
 *	Void	ChannelLeaveRequest ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to remove an attachment from the attachment list.
 *		A leave request will also be issued upward (unless this is the Top
 *		Provider).
 */
Void	Channel::ChannelLeaveRequest (
				CAttachment     *pOrigAtt,
				CChannelIDList *)
{
	CChannelIDList		channel_leave_list;

	/*
	 *	Make sure the attachment is in the list before trying to remove it.
	 */
	if (m_JoinedAttachmentList.Remove(pOrigAtt))
	{
		TRACE_OUT (("Channel::ChannelLeaveRequest: leaving channel %04X", Channel_ID));

		/*
		 *	Remove the attachment from the list.
		 */

		/*
		 *	If this results in an empty list, then we have more work to do.
		 */
		if (m_JoinedAttachmentList.IsEmpty())
		{
			/*
			 *	If this is not the Top Provider, send a leave request upward
			 *	to the Top Provider.
			 */
			if (! IsTopProvider())
			{
				TRACE_OUT (("Channel::ChannelLeaveRequest: "
						"sending ChannelLeaveRequest to Top Provider"));

				channel_leave_list.Append(Channel_ID);
				m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
			}
		}
	}
}

/*
 *	Void	SendDataRequest ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to send data through the channel.
 */
Void	Channel::SendDataRequest (
				CAttachment        *pOrigAtt,
				UINT				type,
				PDataPacket			data_packet)
{
	CAttachment *pAtt;

	ASSERT (Channel_ID == data_packet->GetChannelID());
	/*
	 *	If this is not the Top Provider, forward the data upward.
	 */
	if (m_pConnToTopProvider != NULL)
		m_pConnToTopProvider->SendDataRequest(data_packet);

	/*
	 *	Iterate through the attachment list, sending the data to all
	 *	the attachments (except for one from whence the data came).
	 */
	m_JoinedAttachmentList.Reset();
	while (NULL != (pAtt = m_JoinedAttachmentList.Iterate()))
	{
		if ((pAtt != pOrigAtt) || (type != MCS_SEND_DATA_INDICATION))
		{
			pAtt->SendDataIndication(type, data_packet);
		}
	}
}

/*
 *	Void	SendDataIndication ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function is used to send data through the channel.
 */
Void	Channel::SendDataIndication (
				PConnection,
				UINT				type,
				PDataPacket			data_packet)
{
	CAttachment *pAtt;

	ASSERT (Channel_ID == data_packet->GetChannelID());
	/*
	 *	Iterate through the attachment list, sending the data to all
	 *	the attachments.
	 */
	m_JoinedAttachmentList.Reset();
	while (NULL != (pAtt = m_JoinedAttachmentList.Iterate()))
	{
		pAtt->SendDataIndication(type, data_packet);
	}
}