/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

//Created Abe Jarrett:  Most of the methods in this class were taken from
//Garrett's code in dde.cpp.  This file is intended only as
//a means of detecting seconds instance criteria and handling processing
//of it and  any command line content as well.

#include "stdafx.h"

#include "ddecmd.h"
#include "ddectc.h"


//	Our DDE instance identifier.
DWORD CDDECMDWrapper::m_dwidInst;

//	Wether or not DDE was successfully initialized.
BOOL CDDECMDWrapper::m_bDDEActive;

//	Our array of hsz strings.
HSZ CDDECMDWrapper::m_hsz[CDDECMDWrapper::m_MaxHSZ];

//	A list of all current conversations.  The PtrToPtr map was used since
//		the pointers are known to be 32 bits long, and the HCONV is a 
//		DWORD or possibly a pointer, and WordToPtr just wouldn't cut it
//		with 16 bits and all.  
//	Sounds good to me!
CMapPtrToPtr CDDECMDWrapper::m_ConvList;

//	Command filter for DDEML
DWORD CDDECMDWrapper::m_dwafCmd;

//	Call back function for use with DDEML
FARPROC CDDECMDWrapper::m_pfnCallBack;


//This kicks it all off.
void DDEInitCmdLineConversation()
{

	CDDECMDWrapper::m_pfnCallBack = NULL;

#ifdef XP_WIN16
	//	Find out if we can even use DDEML (must be in protected mode).
	//	Undoubtedly, this is the case since we must be in protected
	//		mode to use WINSOCK services, but just to be anal....
	//  GetWinFlags() has been removed in MSVC 2.0 and the analog doesn't
	//      look like it does the same thing.  chouck 29-Dec-94
	if(!(GetWinFlags() & WF_PMODE))	{
		//	Not in protected mode, can not continue with DDEML
		return ;
	}
#endif
	
	//	Set up our callback function to be registered with DDEML.
	CDDECMDWrapper::m_pfnCallBack = (FARPROC)CmdLineDdeCallBack;

			
	if(DdeInitialize(&CDDECMDWrapper::m_dwidInst,
		(PFNCALLBACK)CDDECMDWrapper::m_pfnCallBack,
		CDDECMDWrapper::m_dwafCmd, 0L))	{
		//	Unable to initialize, don't continue.
		return ; 
	}


	CDDECMDWrapper::m_bDDEActive = TRUE;
	
	//	Load in all HSZs.
	//	Unfortunately, there is really no good way to detect any errors
	//		on these string intializations, so let the blame land
	//		where it will if something fails.
	CString strCS;
	
	strCS.LoadString(IDS_DDE_CMDLINE_SERVICE_NAME);
	CDDECMDWrapper::m_hsz[CDDECMDWrapper::m_ServiceName] =
		DdeCreateStringHandle(CDDECMDWrapper::m_dwidInst,
		(char *)(const char *)strCS, CP_WINANSI);

	strCS.LoadString(IDS_DDE_PROCESS_CMDLINE);
	CDDECMDWrapper::m_hsz[CDDECMDWrapper::m_ProcessCmdLine] =
		DdeCreateStringHandle(CDDECMDWrapper::m_dwidInst,
		(char *)(const char *)strCS, CP_WINANSI);
	
}


//	Purpose:	Construct a DDECMDWrapper object
//	Arguments:	hszService	The name of the service we are handling.
//				hszTopic	The name of the topic we are handling.
//				hConv		The handle of the conversation we are
//								handling.
//	Returns:	nothing
//	Comments:	Handles all created objects internally through a map.
//	Revision History:
//
CDDECMDWrapper::CDDECMDWrapper(HSZ hszService, HSZ hszTopic, HCONV hConv)
{
	//	Set our members passed in.
	m_hszService = hszService;
	m_hszTopic = hszTopic;
	m_hConv = hConv;
	
	//	Figure out our enumerated service number.
	//	We know this anyhow, we only have one service name at this
	//		point.
	m_iService = m_ServiceName;
	
	//	Figure out our enumerated topic number.
	m_iTopic = EnumTopic(hszTopic);
	
	//	Add ourselves to the current map of conversations.
	m_ConvList.SetAt((void *)hConv, this);
}


//	Purpose:	Destory a CDDECMDWrapper object
//	Arguments:	void
//	Returns:	nothing
//	Comments:	Removes us from the internally handled list
//	Revision History:
//	

CDDECMDWrapper::~CDDECMDWrapper()
{
	//	Remove ourselves from the list of current conversations.
	m_ConvList.RemoveKey((void *)m_hConv);
}



//	Purpose:	Connect to a service using a specific topic
//	Arguments:	cpService	The service name of the DDE server
//				hszTopic	The topic of the conversation to the server
//	Returns:	CDDECMDWrapper *	The conversation object if established,
//									otherwise NULL.
//	Comments:	Generic connection establishment.
//	Revision History:
//		01-05-95	created GAB
CDDECMDWrapper *CDDECMDWrapper::ClientConnect(const char *cpService,
	HSZ& hszTopic)
{
	CDDECMDWrapper *pConv = NULL;

	//	Make the service name into an HSZ.
	HSZ hszService = DdeCreateStringHandle(m_dwidInst, 
	                                       (char *) cpService,
										   CP_WINANSI);
	if(hszService == NULL)	{
		return(NULL);
	}
	
	//	Establish the connection.
	HCONV hConv = DdeConnect(m_dwidInst, hszService, hszTopic, NULL);
	if(hConv != NULL)	{
		//	We have a connection, all that's left to do is to create
		//		a CDDECMDWrapper, we'll be creating it with the wrong
		//		service name of course, but client connections no
		//		longer really care about the service connection number,
		//		all they need is the conversation handle.
		pConv = new CDDECMDWrapper(m_hsz[m_ServiceName], hszTopic, hConv);
	}
		
	//	Free off our created hsz.
	DdeFreeStringHandle(m_dwidInst, hszService);
	

	return(pConv);
}

//	Purpose:	Figure out which wrapper is associated with a conversation.
//	Arguments:	hConv	The conversation to find out about.
//	Returns:	CDDECMDWrapper *	A pointer to the CDDECMDWrapper that is
//									handling the conversation, or NULL
//									if none is present.
//	Comments:	Shouldn't ever return NULL really.
//	Revision History:
//		12-30-94	created GAB
//		12-04-96	reused by AJ.  Probably not necessary, but you never know
//					know when we may have to do more than one conversation.
CDDECMDWrapper *CDDECMDWrapper::GetConvObj(HCONV hConv)
{
	//	Query our static map of conversations for the object.
	void *pWrap;
	
	if(m_ConvList.Lookup((void *)hConv, pWrap) == 0)	{
		return(NULL);
	}
	return((CDDECMDWrapper *)pWrap);
}


//	Purpose:	Return the offset of the hsz topic string in our static
//					m_hsz array
//	Arguments:	hsz	The HSZ string to find in our array
//	Returns:	int	The offset into the array, also correlating to
//						it's enumerated value.  If not found, the
//						returned failure value is 0.
//	Comments:	Mainly coded to remove redundant lookups.  Modularity...
//	Revision History:
//		12-30-94	created GAB
//		12-04-96	reused by AJ.  Probably not necessary, but you never know
//					know when we may have to handle more than one topic.
int CDDECMDWrapper::EnumTopic(HSZ& hsz)
{
	int i_retval = 0;
	int i_counter;
	
	//	Just start looking for the HSZ string in our static array.
	for(i_counter = m_TopicStart; i_counter < m_MaxHSZ; i_counter++)
	{
		if(m_hsz[i_counter] == hsz)	{
			i_retval = i_counter;
			break;
		}
	}
	
	return(i_retval);
}

HDDEDATA 
CALLBACK 
#ifndef XP_WIN32
_export 
#endif

CmdLineDdeCallBack(UINT type, UINT fmt,
	HCONV hconv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1,
	DWORD dwData2)
{
	
	
	//	Depending on the class of transaction, we return different data.
	if(type & XCLASS_BOOL)	{
		//	Must return (HDDEDATA)TRUE or (HDDEDATA)FALSE
		switch(type)	{

		case XTYP_CONNECT:	{
			//	We are the server.
			//	A client call the DdeConnect specifying a service and
			//		topic name which we support.
			HSZ& hszTopic = hsz1;
			HSZ& hszService = hsz2;
			
			//	Deny the connection if the service name is not the
			//		one we are taking connections for.
			if(hszService !=
				CDDECMDWrapper::m_hsz[CDDECMDWrapper::m_ServiceName])	{
				return((HDDEDATA)FALSE);
			}
			
			//	Now, the topic can be NULL, or it can be any one of our
			//		topic names to be accepted.
			if(hszTopic == NULL)	{
				return((HDDEDATA)TRUE);
			}
			
			//	Go through all our topics, see if we match.
			if(0 != CDDECMDWrapper::EnumTopic(hszTopic))	{
				return((HDDEDATA)TRUE);
			}
			
			//	Topic not supported
			return((HDDEDATA)FALSE);
		}
		default:
			//	unknown
			return((HDDEDATA)FALSE);
		}
		
		//	Break handled here
		return((HDDEDATA)FALSE);
	}
	else if(type & XCLASS_DATA)	{
		//	Must return DDE data handle, CBR_BLOCK, or NULL		
			return(NULL);
	}
	else if(type & XCLASS_FLAGS)	{
		//	Must return DDE_FACK, DDE_BUSY, or DDE_FNOTPROCESSED
		switch(type)	{
		case XTYP_ADVDATA:	{
			//	We are the client.
			//	The server gave us a data handle.
			break;
		}
		case XTYP_EXECUTE:	{
			//	We are the server.
			//	A client said XTYP_EXECUTE in DdeClientTransaction

            //  If we are currently not fully initialized, we must ignore
            //      the request, but acknowledge it nonetheless.
            if(theApp.m_bInInitInstance) {
                return (HDDEDATA)DDE_FACK;
            }

			HDDEDATA& hDataExecute = hData;
			char *pData = (char *)DdeAccessData(hDataExecute, NULL);
			char szCmd[_MAX_PATH+12];
			strcpy ( szCmd, "[cmdline(\"" );
			strcat ( szCmd, pData );
			strcat ( szCmd, "\")]" );
			BOOL bRetval = theApp.OnDDECommand(szCmd);
			
			DdeUnaccessData(hDataExecute);
			return((HDDEDATA) bRetval);
		}

		default:
			//	unknown
			return(DDE_FNOTPROCESSED);
		}
		
		//	Break handled here
		return(DDE_FNOTPROCESSED);
	}
	else if(type & XCLASS_NOTIFICATION)	{
		//	Must return NULL, as the return value is ignored
		switch(type)	{
		case XTYP_ADVSTOP:	{
			//	We are the server
			//	A client said XTYP_ADVSTOP in DdeClientTransaction
			break;
		}
		case XTYP_CONNECT_CONFIRM:	{
			//	We are the server.
			//	We returned 1 to a XTYP_CONNECT transaction.
			
			//	This callback is mainly to inform us of the conversation
			//		handle that now exists, but we will actually be
			//		creating an instance of an object to handle this
			//		conversation from now on.
			HSZ& hszTopic = hsz1;
			HSZ& hszService = hsz2;
			
			//	Create the object, correctly initialized so that
			//		it understands the service, topic, and conversation
			//		it is handling.
			//	It will add itself to the conversation list.
			CDDECMDWrapper *pObject = new CDDECMDWrapper(hszService, hszTopic,
				hconv);
			break;
		}
		case XTYP_DISCONNECT:	{
			//	We are either client/server
			//	The partner in the conversation called DdeDisconnect
			//		causing both client/server to receive this.
			
			//	Find out which conversation object we are dealing with.
			CDDECMDWrapper *pWrapper = CDDECMDWrapper::GetConvObj(hconv);
			
			//	Simply delete it.
			//	The object takes care of removing itself from the list.
			if(pWrapper != NULL)	{
				delete pWrapper;
			}
			break;
		}
		case XTYP_ERROR:	{
			//	We are either client/server
			//	A serious error has occurred.
			//	DDEML doesn't have any resources left to guarantee
			//		good communication.
			break;
		}
		case XTYP_REGISTER:	{
			//	We are either client/server
			//	A server application used DdeNameService to register
			//		a new service name.
			break;
		}
		case XTYP_UNREGISTER:	{
			//	We are either client/server
			//	A server applciation used DdeNameService to unregister
			//		an old service name.
			break;
		}
		case XTYP_XACT_COMPLETE:	{
			//	We are the client
			//	An asynchronous tranaction, sent when the client specified
			//		the TIMEOUT_ASYNC flag in DdeClientTransaction has
			//		concluded.
			break;
		}
		default:
			//	unknown
			return(NULL);
		}
		return(NULL);
	}

	//	Unknown class type
	return(NULL);

}


