/*****************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/
// ProtocolFR.cpp: implementation of the CProtocolFR class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"
#include "ProtocolFR.h"
#include "ObjectFactory.h"
#include "reghelpers.h"
#include "utils.h"
#include "FRConfig.h"
#include "Adapter.h"
#include "Line.h"		// the parent trap
#include "frreg.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC(CProtocolFR, CProtocol)
INT_PTR CProtocolFR::PVCCount = 0;
CProtocolFR::CProtocolFR(HKEY const hKeyRoot, CString const strRegPath, CConfigObject const * const pParent) : CProtocol(hKeyRoot, strRegPath, pParent)
{

	m_FR.Flags = DXB_REGVAL_FR_LINK_FLAGS_DEFAULT;
	m_FR.max_frsize = DXB_REGVAL_FR_LINK_MAX_FRAMESIZE_DEFAULT;
	m_FR.lmi_dlci = DXB_REGVAL_FR_LINK_LMI_DLCI_DEFAULT;
	m_FR.mN1 = DXB_REGVAL_FR_LINK_MN1_DEFAULT;
	m_FR.mN2 = DXB_REGVAL_FR_LINK_MN2_DEFAULT;
	m_FR.mN3 = DXB_REGVAL_FR_LINK_MN3_DEFAULT;
	m_FR.mN4 = DXB_REGVAL_FR_LINK_MN4_DEFAULT;
	m_FR.mT1 = DXB_REGVAL_FR_LINK_MT1_DEFAULT;
	m_FR.mT2 = DXB_REGVAL_FR_LINK_MT2_DEFAULT;
	m_FR.mT3 = DXB_REGVAL_FR_LINK_MT3_DEFAULT;
	m_FR.mP1 = DXB_REGVAL_FR_LINK_MP1_DEFAULT;
	m_FR.mP10 = DXB_REGVAL_FR_LINK_MP10_DEFAULT;
	m_pwpLmiPage = NULL;
	m_pwsSheet = NULL;
	try
	{

		// Get protocol parameters
		AffirmThrow(GetParameters());

		// solve the identity crisis
		SetIdentity(DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY);
		// Set our Display Name
		CString szDisplayName;
		AffirmThrow(szDisplayName.LoadString(IDS_FRProtocolName));
		SetDisplayName(szDisplayName);

	   // define our abilities
	   // we don't allow adding/deleting childs from the GUI. Defining the
	   // FR protocol automatically creates the service which is simply 
	   // a holding object allowing us to create PVC's.
		SetAbility(CConfigObject::DOPROPERTIES);
	
		// get our static config data
		AffirmThrow(CProtocol::GetConfigData());
		// Get LineSpeed from parent
		m_dwLineSpeed = ((CLine*)pParent)->GetLineSpeed();
		m_astrServices.Copy(m_astrServicesAllowed);
       // Give birth to our children

		for (int i = 0; i < m_astrServices.GetSize(); i++)
		{
			m_apChildren.SetAtGrow(i, NULL);

         // This registry path thingie needs a little explanation!
         //    Here's the layout so far:
         //       ...\DigiDxb?\Parameters\N           (where ? = the adapter instance, and
         //                                                  N = 0, 1, ... # of lines)
         //    The line service parameters reside at:
         //       ...\DigiDxb?\Parameters\XXXX\N      (where XXXX = the service name)
         //    So, pass in the hKey we were told to start at, and the XXXX\N reg path.

			CString strServiceRegPath = m_astrServices[i] + TEXT("\\") + strRegPath;
			AffirmThrow(NewConfigObject(&m_apChildren[i], CConfigObject::SERVICE, m_astrServices[i], hKeyRoot, strServiceRegPath, this));
			AffirmThrow(m_apChildren[i] != NULL);
		}

		// our child is a service, whose child (port) count will indicate # of PVC's configured
		// across the protocol.
		CProtocolFR::PVCCount += m_apChildren[0]->GetChildCount();

	}
	catch (CAffirmException ae)
	{
	}

}

CProtocolFR::~CProtocolFR()
{
	delete m_pwpLmiPage;
}

void CProtocolFR::SetFRConfig()
{
	m_WorkingFR = m_FR;
}

void CProtocolFR::GetFRConfig()
{
	m_FR = m_WorkingFR;
}


BOOL CProtocolFR::ReadWriteFrameRelayParams(REGMODE emode)
{
	HKEY hsubkey;
	// if we are reading and the key does not exist, we don't want to wipe
	// out our defaults. Do nothing.
	if (emode == READ && !RegKeyExists(m_hKey,DXB_REGKEY_FRAME_RELAY))
	{
		return TRUE;	
	}
	//
	// open the FRAME RELAY section
	if (!RegCreateKey(m_hKey,DXB_REGKEY_FRAME_RELAY,&hsubkey))
		return FALSE;

	REGSTRUCT fr[] = {
		{DXB_REGVAL_FR_LINK_MAX_FRAMESIZE,&(m_FR.max_frsize)},
		{DXB_REGVAL_FR_LINK_FLAGS,&(m_FR.Flags)},
		{DXB_REGVAL_FR_LINK_LMI_DLCI,&(m_FR.lmi_dlci)},
		{DXB_REGVAL_FR_LINK_MN1,&(m_FR.mN1)},
		{DXB_REGVAL_FR_LINK_MN2,&(m_FR.mN2)},
		{DXB_REGVAL_FR_LINK_MN3,&(m_FR.mN3)},
		{DXB_REGVAL_FR_LINK_MN4,&(m_FR.mN4)},
		{DXB_REGVAL_FR_LINK_MP10,&(m_FR.mP10)},
		{DXB_REGVAL_FR_LINK_MT1,&(m_FR.mT1)},
		{DXB_REGVAL_FR_LINK_MT2,&(m_FR.mT2)},
		{DXB_REGVAL_FR_LINK_MT3,&(m_FR.mT3)},
		{DXB_REGVAL_FR_LINK_MP1,&(m_FR.mP1)},
	};

	ReadWriteRegItems(hsubkey,fr,sizeof(fr)/sizeof(fr[0]),emode);
	RegCloseKey(hsubkey);
	return TRUE;
}

// write FrameRelay protocol params to registry
BOOL CProtocolFR::SetParameters()
{
	BOOL bSuccess = FALSE;
	try
	{
		((CLine*)GetParent())->SetMaxFrameSize(m_FR.max_frsize);
		// for now FrameRelay protocol has one and only one child -
		// FrameRelay service. Get the # of PVC's configured on this service
		m_FR.mP1 = (int)m_apChildren[0]->GetChildCount();

		AffirmThrow(CProtocol::SetParameters());
		AffirmThrow(ReadWriteFrameRelayParams(WRITE));		
		bSuccess = TRUE;
	}
	catch(CAffirmException ae)
	{
	}
	return bSuccess;
}

// read frame relay protocol params from registry
BOOL CProtocolFR::GetParameters()
{
	BOOL bSuccess = FALSE;
	try
	{
		AffirmThrow(CProtocol::GetParameters());
		ReadWriteFrameRelayParams(READ);
		bSuccess = TRUE;
	}
	catch(CAffirmException ae)
	{
	}
	return bSuccess;

}

BOOL CProtocolFR::RemoveParameters(BOOL fRemoveKey)
{

    BOOL fSuccess = FALSE;

    try
	{
		// remove the FR keys.
		RegDelKey(m_hKey,DXB_REGKEY_FRAME_RELAY);
		AffirmThrow(CProtocol::RemoveParameters(fRemoveKey));
		fSuccess = TRUE;
	}
	catch (CAffirmException ae)
	{
	}

	return fSuccess;
}

#define CHOICE_LMI_NONE			0
#define CHOICE_LMI_REV1			1
#define CHOICE_LMI_STRATACOM	2
#define CHOICE_LMI_ANNEX_D		3
#define CHOICE_LMI_ANNEX_A		4

BOOL CProtocolFR::AddWizardPages(CConfigWizSheet * const pSheet)
{
	BOOL bSuccess = FALSE;
	try
	{
		m_pwsSheet = pSheet;

		if (!m_pwpLmiPage)
		{
			CString strTitle,strInstructions;
			strTitle.LoadString(IDS_WIZLMITITLE);
			strInstructions.LoadString(IDS_WIZLMIINS);
			m_pwpLmiPage = new CWizPageComboBox(this,WP_LMIREVISION,strTitle,strInstructions);
			AffirmThrow(m_pwpLmiPage != NULL);
			m_pwpLmiPage->SetHelpID(IDH_WIZ_LMIPAGE);
		}
		CStringArray astrLMI;
		UINT nIdLMITypes[] = {IDS_LMINONE,IDS_LMIREV1,IDS_LMISTRATACOM,IDS_LMIANNEXD,IDS_LMIANNEXA};
		for(int i=0;i<ElementCount(nIdLMITypes);i++)
		{
			CString sztemp((LPCTSTR)nIdLMITypes[i]);
			astrLMI.SetAtGrow(i,sztemp);
		}
		int LMIRevision = CHOICE_LMI_NONE;
		int Flags = m_FR.Flags;
		if (Flags & FRS_LINK_FLAGS_LMI_REV1)
			LMIRevision = CHOICE_LMI_REV1;
		else if (Flags & FRS_LINK_FLAGS_GANG_OF_4)
			LMIRevision = CHOICE_LMI_STRATACOM;
		else if (Flags & FRS_LINK_FLAGS_ANNEX_D)
			LMIRevision = CHOICE_LMI_ANNEX_D;
		else if (Flags & FRS_LINK_FLAGS_ANNEX_A)
			LMIRevision = CHOICE_LMI_ANNEX_A;
		
		m_pwpLmiPage->SetValues(astrLMI,LMIRevision);
		pSheet->InsertPage(m_pwpLmiPage);
		bSuccess = TRUE;
	}
	catch (CAffirmException ae)
	{
	}
	return bSuccess;
}

// No handling of NextWizardPage
BOOL CProtocolFR::NextWizardPage(int const State)
{
#define DLCI_COMM_CHANNEL_ANNEX_A_OR_D	0
#define DLCI_COMM_CHANNEL_OTHERS		1023

	BOOL bSuccess = FALSE;
	try
	{
	if (State == WP_LMIREVISION)
	{
		int nChoice = m_pwpLmiPage->GetChoice();
		int Flags = m_FR.Flags & (LMIFLAGSMASK | FRS_LINK_FLAGS_DCE);
		BOOL bFlowControl = (Flags & FRS_LINK_FLAGS_LMI_XON_XOFF) > 0;
		BOOL bBandwidthControl = (Flags & FRS_LINK_FLAGS_LMI_BANDWIDTH_CONTROL) > 0;
		BOOL bStratacom = FALSE;
		switch(nChoice)
		{
		case CHOICE_LMI_NONE:Flags = 0;break;
		case CHOICE_LMI_REV1:Flags |= FRS_LINK_FLAGS_LMI_REV1;break;
		case CHOICE_LMI_STRATACOM:Flags |= FRS_LINK_FLAGS_GANG_OF_4;
			bStratacom = TRUE;
			if (bStratacom ^ bBandwidthControl || bStratacom ^ bFlowControl)
				bBandwidthControl = bFlowControl = bStratacom;
			if (bFlowControl)
				Flags |= FRS_LINK_FLAGS_LMI_XON_XOFF;
			if (bBandwidthControl)
				Flags |= FRS_LINK_FLAGS_LMI_BANDWIDTH_CONTROL;

			break;
		case CHOICE_LMI_ANNEX_D:Flags |= FRS_LINK_FLAGS_ANNEX_D;break;
		case CHOICE_LMI_ANNEX_A:Flags |= FRS_LINK_FLAGS_ANNEX_A;break;
		default:AffirmThrow(FALSE);
		}
		m_FR.Flags |= Flags;
		// assing DLCI communication channel based on LMI revision choice
		m_FR.lmi_dlci = (nChoice > CHOICE_LMI_STRATACOM)?
						DLCI_COMM_CHANNEL_ANNEX_A_OR_D:
						DLCI_COMM_CHANNEL_OTHERS;
		for (int i=0;i<GetChildCount();i++)
			AffirmThrow(m_apChildren[i]->AddWizardPages(m_pwsSheet));
		bSuccess = TRUE;
	}
	else
		AffirmThrow(FALSE);
	}
	catch(CAffirmException ae)
	{
	}
	return bSuccess;
}

// do the brunt of configuration. Let users define PVC's using this
// dialogue.
BOOL CProtocolFR::DoProperties(void)
{
	BOOL bSuccess = FALSE;
	try
	{
		SetFRConfig();
		CPropertySheet frsheet(IDS_FRProtocolName);
		CFRGeneral m_FRGeneral(&m_WorkingFR);
		CFRLMI m_FRLMI(&m_WorkingFR);
		// ****
		// some serious coupling here. We load up a copy of our FrameRelay
		// parameter structure and get a new copy back if the structure has
		// been modified via property sheet.
		frsheet.AddPage(&m_FRGeneral);
		frsheet.AddPage(&m_FRLMI);

		if (frsheet.DoModal() == IDOK)
		{
			GetFRConfig();
			bSuccess = TRUE;

		}
	}

	catch(CAffirmException ae)
	{
	}
	return bSuccess;
}

// Decide if another PVC can be added on this protocol. The protocol may run
// on N different lines but over the entire adapter, it can only support 
// MAXPVC pvc's. This count is further clamped by ports available on the adapter
BOOL CProtocolFR::AllocPVC()
{
	BOOL bSuccess = FALSE;
	int nErrmsgPorts = m_cMaxPorts;
	int nErrmsgID = IDS_VCLIMIT;
	// if at max PVC limit, we are done
	if (CProtocolFR::PVCCount < m_cMaxPorts)
	{
		// our parent is the line,grandparent is adapter
		CAdapter* pAdapter = (CAdapter*)(GetParent()->GetParent());	
		ASSERT(pAdapter);
		INT_PTR TotalConfiguredPorts = pAdapter->GetConfiguredPorts();
		// now clamp against available ports on the adapter
		nErrmsgPorts = pAdapter->GetAdapterMaxPorts();
		nErrmsgID = IDS_VCADAPTERLIMIT;
		if ((1 +  TotalConfiguredPorts) <= pAdapter->GetAdapterMaxPorts())
		{
			CProtocolFR::PVCCount++;
			bSuccess = TRUE;
		}
	}
	if (!bSuccess)
	{
		CString strMsgFmt((LPCTSTR)nErrmsgID);
		CString strErrMsg;
		strErrMsg.Format(strMsgFmt,nErrmsgPorts);
		AfxMessageBox(strErrMsg);
	}
	return bSuccess;
}

void CProtocolFR::DeAllocPVC()
{
	CProtocolFR::PVCCount--;
	ASSERT(CProtocolFR::PVCCount > -1);
}

// return static information to the user. 
// MaxFrame size, Total # of PVC's (max & configured)
CString CProtocolFR::GetInformationText(void)
{
	CString strfmt;
	strfmt.LoadString(IDS_FRINFO_FMT);
	CString strInfo;
	strInfo.Format(strfmt,m_FR.max_frsize,m_cMaxPorts,CProtocolFR::PVCCount);
	return strInfo;
	
}
// return the line speed associated with this protocol.
DWORD CProtocolFR::GetLineSpeed() const
{
	return m_dwLineSpeed;
}

// Parent is notifying of change. Get linespeed and inform child service
// that change is on the way.
void CProtocolFR::ParentUpdate()
{
	m_dwLineSpeed = ((CLine*)GetParent())->GetLineSpeed();
	for(int i=0;i<GetChildCount();i++)
		m_apChildren[i]->ParentUpdate();
}

// overridden method. Called when the protocol has been placed into the trash
// heap. We remove all ports configured as well as set all channel counts to 0.
// This method will be called when the user is switching to some other protocol
// we take the brute force approach which requires the user if reselecting this
// protocol again, to reconfigure all valid parameters.
// (The other apporach is to delete the PVC's but restore them back - as many 
//  as possible if the protocol is reselected.But how often does the user really
//  does this operation)
void CProtocolFR::ObjectToTrash(void)
{
	// delete all ports that are configured on this protocol
	for(int i=0;i<GetChildCount();i++)
	{
		CConfigObject* pChild = GetChild(i);
		INT_PTR grandchildren = pChild->GetChildCount();
		// need to remove all ports configured on this service.
		// doing a DeleteChild() will reduce the array size by 1 
		// for each call. If we work up to increasing indices for GetChild
		// that will break. e.g. 2 ports (0,1). Do a get(0)/delete. Now size of
		// array is 1. Doing a get(1) will break since there is only 1 child 
		// so work backwards from higher index to lower. This will keep index
		// in sync with # of entries in array
		for(INT_PTR j=0;j<grandchildren;j++)
			pChild->DeleteChild(pChild->GetChild(int(grandchildren-(j+1))));
	}


}
