/*
 *   DdeServ.c
 *
 *   This file is part of Emu28
 *
 *   Copyright (C) 2002 Christoph Gießelink
 *
 */
#include "pch.h"
#include "Emu28.h"
#include "io.h"

HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,HCONV hConv,
							  HSZ hsz1,HSZ hsz2,HDDEDATA hData,
							  DWORD dwData1,DWORD dwData2)
{
	TCHAR    *psz,szBuffer[32];
	HDDEDATA hReturn;
	LPBYTE   lpData,lpHeader;
	DWORD    dwAddress,dwSize,dwLoop,dwIndex;
	UINT     nStkLvl;
	BOOL     bSuccess;

	switch (iType)
	{
		case XTYP_CONNECT:
			// get service name
			DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0);
			if (0 != lstrcmp(szBuffer,szAppName))
				return (HDDEDATA) FALSE;
			// get topic name
			DdeQueryString(idDdeInst,hsz1,szBuffer,ARRAYSIZEOF(szBuffer),0);
			return (HDDEDATA) (INT_PTR) (0 == lstrcmp(szBuffer,szTopic));

		case XTYP_POKE:
			// all none HP28C or illegal data format or not in running state
			if (cCurrentRomType != 'P' || iFmt != uCF_HpObj || nState != SM_RUN)
				return (HDDEDATA) DDE_FNOTPROCESSED;

			// get item name
			DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0);
			nStkLvl = _tcstoul(szBuffer,&psz,10);
			if (*psz != 0 || nStkLvl < 1)	// invalid number format
				return (HDDEDATA) DDE_FNOTPROCESSED;

			SuspendDebugger();				// suspend debugger
			bDbgAutoStateCtrl = FALSE;		// disable automatic debugger state control

			if (!(Chipset.IORamM[DSPCTL]&DON)) // HP off
			{
				// turn on HP
				KeyboardEvent(TRUE,0,0x8000);
				Sleep(dwWakeupDelay);
				KeyboardEvent(FALSE,0,0x8000);
			}

			if (WaitForSleepState())		// wait for cpu SHUTDN then sleep state
			{
				hReturn = DDE_FNOTPROCESSED;
				goto cancel;
			}

			while (nState!=nNextState) Sleep(0);
			_ASSERT(nState==SM_SLEEP);

			bSuccess = FALSE;

			// get data and size
			lpData = DdeAccessData(hData,&dwSize);

			// has object length header
			if (lpData && dwSize >= sizeof(DWORD))
			{
				dwIndex = *(LPDWORD) lpData; // object length

				if (dwIndex <= dwSize - sizeof(DWORD))
				{
					// reserve unpacked object length memory
					LPBYTE pbyMem = (LPBYTE) malloc(dwIndex * 2);

					if (pbyMem != NULL)
					{
						// copy data and write to stack
						CopyMemory(pbyMem+dwIndex,lpData+sizeof(DWORD),dwIndex);
						bSuccess = (WriteStack(nStkLvl,pbyMem,dwIndex) == S_ERR_NO);
						free(pbyMem);		// free memory
					}
				}
			}

			DdeUnaccessData(hData);

			SwitchToState(SM_RUN);			// run state
			while (nState!=nNextState) Sleep(0);
			_ASSERT(nState==SM_RUN);

			if (bSuccess == FALSE)
			{
				hReturn = DDE_FNOTPROCESSED;
				goto cancel;
			}

			KeyboardEvent(TRUE,0,0x8000);
			Sleep(dwWakeupDelay);
			KeyboardEvent(FALSE,0,0x8000);
			// wait for sleep mode
			while(Chipset.Shutdn == FALSE) Sleep(0);
			hReturn = (HDDEDATA) DDE_FACK;

cancel:
			bDbgAutoStateCtrl = TRUE;		// enable automatic debugger state control
			ResumeDebugger();
			return hReturn;

		case XTYP_REQUEST:
			// all none HP28C or illegal data format or not in running state
			if (cCurrentRomType != 'P' || iFmt != uCF_HpObj || nState != SM_RUN)
				return NULL;

			// get item name
			DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0);
			nStkLvl = _tcstoul(szBuffer,&psz,10);
			if (*psz != 0 || nStkLvl < 1)	// invalid number format
				return NULL;

			if (WaitForSleepState())		// wait for cpu SHUTDN then sleep state
				return NULL;

			while (nState!=nNextState) Sleep(0);
			_ASSERT(nState==SM_SLEEP);

			// if the calculator is off the object is one stack level higher,
			// because stack level 1 contain the FALSE object in this case
			if ((Chipset.IORamM[DSPCTL]&DON) == 0)
				++nStkLvl;

			dwAddress = RPL_Pick(nStkLvl);	// pick address of stack level "item" object
			if (dwAddress == 0)
			{
				SwitchToState(SM_RUN);		// run state
				return NULL;
			}
			dwLoop = dwSize = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2;

			lpHeader = (LPBYTE) BINARYHEADER28C;

			// length of binary header
			dwIndex = (DWORD) strlen((LPCSTR) lpHeader);

			// size of objectsize + header + object
			dwSize += dwIndex + sizeof(DWORD);

			// reserve memory
			if ((lpData = (LPBYTE) malloc(dwSize)) == NULL)
			{
				SwitchToState(SM_RUN);		// run state
				return NULL;
			}

			// save data length
			*(DWORD *)lpData = dwLoop + dwIndex;
			
			// copy header
			memcpy(lpData + sizeof(DWORD),lpHeader,dwIndex);

			// copy data
			for (dwIndex += sizeof(DWORD);dwLoop--;++dwIndex,dwAddress += 2)
				lpData[dwIndex] = Read2(dwAddress);

			// write data
			hReturn = DdeCreateDataHandle(idDdeInst,lpData,dwSize,0,hsz2,iFmt,0);
			free(lpData);

			SwitchToState(SM_RUN);			// run state
			while (nState!=nNextState) Sleep(0);
			_ASSERT(nState==SM_RUN);

			return hReturn;
	}
	return NULL;
	UNREFERENCED_PARAMETER(hConv);
	UNREFERENCED_PARAMETER(dwData1);
	UNREFERENCED_PARAMETER(dwData2);
}
