/******************************************************************************
 *
 * Copyright (c) 1999	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	LineDevice.cpp
 *
 *****************************************************************************/

// SOL++2000

#include <sol\LineDevice.h>

#include <sol\stdio.h>


LineDevice* LineDevice::self = NULL;



LRESULT CALLBACK LineDevice::lineCallbackProc(DWORD dwDevice, 
		DWORD dwMsg,
        DWORD dwCallbackInst, DWORD dwParam1,
        DWORD dwParam2,DWORD dwParam3) 
{
	LRESULT rc = 0;

	if(self && self->getView()) {
		View* view = self -> getView();
		UINT message = self -> getCallbackMessage();

		TAPI_CALLBACK param;
		param.dwDevice = dwDevice;
		param.dwMsg    = dwMsg;
		param.dwCallbackInst = dwCallbackInst;
		param.dwParam1 = dwParam1;
		param.dwParam2 = dwParam2;
		param.dwParam3 = dwParam3;
		rc = view -> send(message, (WPARAM)self, (LPARAM)&param);		
	}
	return rc;
}


LineDevice::LineDevice(View* view, UINT message) 
{
	extraSize = 2000;
	this -> view = view;
	lineApp  = NULL;
	hLine    = NULL;
	hCall    = NULL;
	dwNumDevices = 0;
	dwAPIVersion = 0;
	self = this;
	dialString[0] = Zero;
	dialStringOriginal[0] = Zero;
	dialStringCanonical[0] = Zero;
	calledParty[0] = Zero; 
	lineName[0] = Zero;

	hdialog	= NULL;
	sizeDeviceConfig = 0;
	deviceConfig = NULL;
	callbackMessage = message;
}

LineDevice::~LineDevice() 
{
	deallocateCall();
	close();
	shutdown();
	self = NULL;
}


long LineDevice::accept(const char* lpsUserUserInfo, DWORD dwSize) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineAccept(hCall, lpsUserUserInfo, dwSize);
	} 
	return rc;  
}

	
long LineDevice::addProvider(const char* providerFilename, 
			HWND hwndOwner, DWORD* dwPermanentProviderID) 
{
	return ::lineAddProvider(providerFilename, hwndOwner, 
				dwPermanentProviderID);	
}


long LineDevice::addToConference(HCALL hConsultCall) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineAddToConference(hCall, hConsultCall);
	}
	return rc;
}	
  
long LineDevice::answer(const char* userUserInfo, DWORD dwSize) 
{ 
	long rc = Error;
	if(hCall) {
		rc = ::lineAnswer(hCall, userUserInfo, dwSize);
	}
	return rc;
}


long LineDevice::blindTransfer(const char* destAddress, 
		DWORD dwCountryCode) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineBlindTransfer(hCall, destAddress, dwCountryCode);
	}
	return rc;
}  

long LineDevice::close() 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineClose(hLine);
		hLine = NULL;
	}
	return rc;
}


long LineDevice::completeCall(LPDWORD lpdwCompletionID, 
				DWORD dwCompletionMode, DWORD dwMessageID) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineCompleteCall(hCall, lpdwCompletionID, 
				dwCompletionMode, dwMessageID);
	}
	return rc;
}

	
long LineDevice::completeTransfer(HCALL hConsultCall, 
				HCALL* hConfCall, DWORD dwTransferMode) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineCompleteTransfer(hCall, hConsultCall, 
				hConfCall, dwTransferMode);
	}
	return rc;
}

long LineDevice::configDialogEdit (DWORD dwDeviceID, HWND hwndOwner, 
			LPCSTR deviceClass, LPVOID const lpDeviceConfigIn, 
			DWORD dwSize, VARSTRING* deviceConfigOut) 
{
	return  ::lineConfigDialogEdit(dwDeviceID,hwndOwner, 
				deviceClass, lpDeviceConfigIn, 
				dwSize, deviceConfigOut);
}


long LineDevice::deallocateCall() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineDeallocateCall(hCall);
		hCall = NULL;
	}
	return rc;
}
	

long LineDevice::devSpecific(DWORD dwAddressID, VOID* params, DWORD dwSize) 
{
	long rc = Error;
	if(hLine && hCall) {
		rc = ::lineDevSpecific(hLine, dwAddressID, hCall, 
					params, dwSize);
	}
	return rc;
}

long LineDevice::devSpecificFeature(DWORD dwFeature, void* params, 
									DWORD dwSize) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineDevSpecificFeature(hLine, dwFeature, params, dwSize);
	}
	return rc;
}

long LineDevice::dial(const char* destAddress, DWORD dwCountryCode) 
{		
	long rc = Error;
	if(hCall) {
		rc = ::lineDial(hCall, destAddress, dwCountryCode);
	}
	return rc;
}

long LineDevice::drop() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineDrop(hCall, NULL, 0);
	}
	return rc;
}
	
long LineDevice::forward(DWORD bAllAddresses, DWORD dwAddressID, 
			LPLINEFORWARDLIST const lpForwardList, DWORD dwNumRingsNoAnswer, 
			LPHCALL lphConsultCall, LPLINECALLPARAMS const lpCallParams) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineForward(hLine, bAllAddresses, dwAddressID, 
				lpForwardList, dwNumRingsNoAnswer, 
				lphConsultCall, lpCallParams);
	}
	return rc;
}

  
long LineDevice::gatherDigits(DWORD dwDigitModes, char* digits, 
			DWORD dwNumDigits, const char* terminationDigits, 
			DWORD dwFirstDigitTimeout, DWORD dwInterDigitTimeout) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineGatherDigits(hCall, dwDigitModes, digits, 
				dwNumDigits, terminationDigits, 
				dwFirstDigitTimeout, dwInterDigitTimeout);
	}
	return rc;
}

long LineDevice::generateDigits(DWORD dwDigitMode, 
								const char* digits, 
								DWORD dwDuration) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineGenerateDigits(hCall, dwDigitMode, 
					digits, dwDuration);
	}
	return rc;
}


long LineDevice::generateTone(DWORD dwToneMode, DWORD dwDuration, DWORD dwNumTones, 
		LPLINEGENERATETONE const lpTones) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineGenerateTone(hCall, dwToneMode, 
			dwDuration, dwNumTones, lpTones);
	}
	return rc;

}

long LineDevice::getAddressID(LPDWORD lpdwAddressID, 
			DWORD dwAddressMode, LPCSTR lpsAddress, DWORD dwSize) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineGetAddressID(hLine, lpdwAddressID, 
			dwAddressMode, lpsAddress, dwSize);
	}
	return rc;
}
  

long LineDevice::getCallInfo(LPLINECALLINFO lpCallInfo) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineGetCallInfo(hCall, lpCallInfo);
	}
	return rc;
}

long LineDevice::getConfRelatedCalls(LPLINECALLLIST lpCallList) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineGetConfRelatedCalls(hCall, lpCallList);
	}
	return rc;
}

LINEDEVCAPS* LineDevice::getDevCaps() 
{
	if(!lineApp) {
		return NULL;
	}

	int size = sizeof(LINEDEVCAPS) + extraSize;

	LINEDEVCAPS*  devCaps = (LINEDEVCAPS *) new char[size];
	memset(devCaps, 0, size);
	devCaps->dwTotalSize = size;

	strcpy(lineName, "");
	
	for(unsigned int i = 0; i<dwNumDevices; i++) {
		::lineGetDevCaps(lineApp, i, dwAPIVersion, 0, devCaps);    
		if (devCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM) {
			dwNumAddress = devCaps->dwNumAddresses;
			dwLineID = i;
      		if(devCaps->dwLineNameSize > 0) {
  		     	strcpy(lineName,
					   ((char*)(devCaps)+ devCaps->dwLineNameOffset));
			}
			break;
		}
	}
	return devCaps;
}


HANDLE LineDevice::getID()
{
	if(!hLine) {
		return NULL;
	}

	unsigned int size = sizeof(VARSTRING);

	VARSTRING* vstring = (VARSTRING*) new char[size];
	memset(vstring, 0, size);
	vstring -> dwTotalSize = size;
	vstring -> dwStringFormat = STRINGFORMAT_BINARY;
	::lineGetID(hLine, 0, hCall, LINECALLSELECT_CALL,
				vstring, "comm/datamodem");

	if(vstring->dwNeededSize>size) {
		size = vstring->dwNeededSize;
		delete [] vstring;
		vstring = (VARSTRING*) new char[size];
		memset(vstring, 0, size);
		vstring -> dwTotalSize = size;
		vstring -> dwStringFormat = STRINGFORMAT_BINARY;
		::lineGetID(hLine, 0, hCall, LINECALLSELECT_CALL,
				vstring, "comm/datamodem");
	}	
	HANDLE hcomm = *((LPHANDLE)((LPBYTE)vstring +
								vstring -> dwStringOffset));
	delete [] vstring;

	return hcomm;
}


long LineDevice::getID(DWORD dwAddressID, DWORD dwSelect, 
			LPVARSTRING lpDeviceID, LPCSTR lpszDeviceClass) 
{
	long rc = Error;
	if(hLine && hCall) {
		rc = ::lineGetID(hLine, dwAddressID, hCall, 
				dwSelect, lpDeviceID, lpszDeviceClass);
	}
	return rc;
}

long LineDevice::getLineDevStatus(LINEDEVSTATUS* status) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineGetLineDevStatus(hLine, status);
	}
	return rc;
}


long LineDevice::getNewCalls(DWORD dwAddressID, DWORD dwSelect, 
		LPLINECALLLIST lpCallList) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineGetNewCalls(hLine, dwAddressID, dwSelect, 
				lpCallList);
	}
	return rc;
}


long LineDevice::getNumRings(DWORD dwAddressID, LPDWORD lpdwNumRings) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineGetNumRings(hLine, dwAddressID, lpdwNumRings);
	}
	return rc;
}


long LineDevice::getRequest(DWORD dwRequestMode, LPVOID lpRequestBuffer) 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineGetRequest(lineApp, dwRequestMode, 
			lpRequestBuffer);
	}
	return rc;
}

long LineDevice::getStatusMessages(LPDWORD lpdwLineStates, 
								   LPDWORD lpdwAddressStates) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineGetStatusMessages(hLine, lpdwLineStates, 
				lpdwAddressStates);
	}
	return rc;
}

long LineDevice::getTranslateCaps(DWORD dwAPIVersion, 
				LPLINETRANSLATECAPS lpTranslateCaps) 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineGetTranslateCaps(lineApp, dwAPIVersion, lpTranslateCaps);
	}
	return rc;
}
  

long LineDevice::handoff() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineHandoff(hCall, NULL,
			LINEMEDIAMODE_DATAMODEM);
	}
	return rc;
}

	
long LineDevice::handoff(LPCSTR lpszFileName, DWORD dwMediaMode) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineHandoff(hCall, lpszFileName, dwMediaMode);
	}
	return rc;
}

long LineDevice::hold() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineHold(hCall);
	}
	return rc;
}


long LineDevice::initialize(HINSTANCE hInst) 
{	
	return  ::lineInitialize(&lineApp, hInst, 
				(LINECALLBACK)lineCallbackProc, NULL,
				&dwNumDevices);
}


long LineDevice::makeCall()
{
	long rc = Error;

	if(hLine) {	
		LINECALLPARAMS cp;
		memset(&cp, 0, sizeof(cp));
		cp.dwTotalSize  = sizeof(cp);
		cp.dwBearerMode = LINEBEARERMODE_VOICE;
		cp.dwMediaMode   =  LINEMEDIAMODE_DATAMODEM;
		cp.dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
		cp.dwAddressMode	= LINEADDRESSMODE_ADDRESSID;
		rc = ::lineMakeCall(hLine, &hCall, dialString ,0,  &cp);

		dwRequestID = (DWORD)-1;   	
		if (rc > 0) {		
			dwRequestID = rc;
		}
	}
	return rc;
}

long LineDevice::makeCall(LPCSTR lpszDestAddress, DWORD dwCountryCode, 
			LPLINECALLPARAMS const lpCallParams) 
{
	long rc = Error;
	if(hLine) {
		rc = ::lineMakeCall(hLine, &hCall, lpszDestAddress, 
				dwCountryCode, lpCallParams);
	}
	return rc;
}


long LineDevice::monitorDigits(DWORD dwDigitModes) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineMonitorDigits(hCall, dwDigitModes);
	}
	return rc;
}

long LineDevice::monitorMedia(DWORD dwMediaModes) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineMonitorMedia(hCall, dwMediaModes);
	}
	return rc;
}

long LineDevice::monitorTones(LPLINEMONITORTONE	const lpToneList, 
			DWORD dwNumEntries) 
{
	long rc = Error;
	if(hCall){
		rc = ::lineMonitorTones(hCall, lpToneList, dwNumEntries);
	}
	return rc;
}

long LineDevice::negotiateAPIVersion(DWORD lowVersion, DWORD hiVersion) 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineNegotiateAPIVersion(lineApp, 0, 
							lowVersion, hiVersion, 
         					&dwAPIVersion, &ext);
	}
	return rc;
}



long LineDevice::negotiateExtVersion(DWORD lowVersion, DWORD hiVersion) 
{
	long rc = Error;
	DWORD extVersion;
	if(lineApp) {
		rc = ::lineNegotiateExtVersion(lineApp, 0, 
					dwAPIVersion, lowVersion, 
       				hiVersion, &extVersion);
	}
	return rc;
}


long LineDevice::open() 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineOpen(lineApp, dwLineID, &hLine,
            		dwAPIVersion, 0, 0,
					LINECALLPRIVILEGE_OWNER|
					LINECALLPRIVILEGE_MONITOR,
					LINEMEDIAMODE_DATAMODEM, 
					NULL);
	}
	return rc;
}


long LineDevice::open(DWORD dwDeviceID,  
				DWORD dwAPIVersion, DWORD dwExtVersion,
				DWORD dwPrivileges, DWORD dwMediaModes, 
				LPLINECALLPARAMS const lpCallParams) 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineOpen(lineApp, dwDeviceID, &hLine, 
				dwAPIVersion, dwExtVersion, (DWORD)this, 
				dwPrivileges, dwMediaModes, lpCallParams);
	}  
	return rc;
}

long LineDevice::park(DWORD dwParkMode, LPCSTR lpszDirAddress, 
			LPVARSTRING lpNonDirAddress) 
{
	long rc = Error;
	if(hCall) {
		rc = ::linePark(hCall, dwParkMode, lpszDirAddress, 
				lpNonDirAddress);
	}
	return rc;
}
	
long LineDevice::pickup(DWORD dwAddressID, LPHCALL lphCall, 
			LPCSTR lpszDestAddress, LPCSTR lpszGroupID) 
{
	long rc = Error;
	if(hLine) {
		rc = ::linePickup(hLine, dwAddressID, &hCall, 
			lpszDestAddress, lpszGroupID);
	}
	return rc;
}

long LineDevice::prepareAddToConference(HCALL hConfCall, LPHCALL lphConsultCall, 
			LPLINECALLPARAMS const lpCallParams) 
{
	long rc = Error;
	if(hConfCall) {
		rc = ::linePrepareAddToConference(hConfCall, lphConsultCall, 
			lpCallParams);
	}
	return rc;
}

long LineDevice::redirect(LPCSTR lpszDestAddress, DWORD dwCountryCode) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineRedirect(hCall, lpszDestAddress, 
				dwCountryCode);
	}
	return rc;
} 
  
long LineDevice::registerRequestRecipient(DWORD dwRegistrationInstance, 
			DWORD dwRequestMode, DWORD bEnable) 
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineRegisterRequestRecipient(lineApp, 
				dwRegistrationInstance, dwRequestMode, bEnable);
	}
	return rc;
}  

long LineDevice::releaseUserUserInfo() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineReleaseUserUserInfo(hCall);
	}
	return rc;
}

	
long LineDevice::removeFromConference() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineRemoveFromConference(hCall);
	}
	return rc;
}

long LineDevice::removeProvider(DWORD dwPermanentProviderID, HWND hwndOwner)
{
	return ::lineRemoveProvider(dwPermanentProviderID, hwndOwner);
}

long LineDevice::secureCall() 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineSecureCall(hCall);
	}
	return rc;
}


long LineDevice::sendUserUserInfo(LPCSTR lpsUserUserInfo, 
			DWORD dwSize) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineSendUserUserInfo(hCall, lpsUserUserInfo, 
					dwSize);
	}
	return rc;
}

	
long  LineDevice::setAppPriority(LPCSTR lpszAppFilename, DWORD dwMediaMode, 
			LPLINEEXTENSIONID const lpExtensionID, DWORD dwRequestMode, 
			LPCSTR lpszExtensionName, DWORD dwPriority) 
{

	return ::lineSetAppPriority(lpszAppFilename, dwMediaMode, 
				lpExtensionID, dwRequestMode, 
				lpszExtensionName, dwPriority);
}

long LineDevice::setAppSpecific(DWORD dwAppSpecific) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineSetAppSpecific(hCall, dwAppSpecific);
	}
	return rc;
}
  
long LineDevice::setCallParams(DWORD dwBearerMode, DWORD dwMinRate, 
			DWORD dwMaxRate, LPLINEDIALPARAMS const lpDialParams) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineSetCallParams(hCall, dwBearerMode, dwMinRate, 
				dwMaxRate,  lpDialParams);
	}
	return rc;
}

long LineDevice::setCallPrivilege(DWORD dwCallPrivilege) 
{
	long rc = Error;
	if(hCall) {
		rc = ::lineSetCallPrivilege(hCall, dwCallPrivilege);
	}
	return rc;
}

LONG LineDevice::setCurrentLocation(DWORD dwLocation)	
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineSetCurrentLocation(lineApp, dwLocation);
	}
	return rc;
}

	
LONG LineDevice::setDevConfig(DWORD dwDeviceID, LPVOID const lpDeviceConfig,
		int size, LPCSTR devClass) 
{ 
	return  ::lineSetDevConfig(dwDeviceID, lpDeviceConfig,
						size, devClass);
}


long LineDevice::setStatusMessages(DWORD devState, DWORD devAddr)
{
	long rc = Error;
	if(hLine) {
		rc = ::lineSetStatusMessages(hLine, devState, devAddr);
	}
	return rc;
}


long LineDevice::shutdown() 
{
	long rc = Error;
	self = NULL;
	if(lineApp) {
		rc = ::lineShutdown(lineApp);
		lineApp = NULL;
	}

	return rc;
}


long LineDevice::translateAddress(char* number)
{
	long rc = Error;

	if(lineApp) {
		strcpy(dialStringOriginal, number); 

		unsigned int size = sizeof(LINETRANSLATEOUTPUT)+extraSize;

		LINETRANSLATEOUTPUT*			   
		output = (LINETRANSLATEOUTPUT *) new char[size];
		memset(output, 0, size);
		output->dwTotalSize = size;

		rc = ::lineTranslateAddress(lineApp, 
					dwLineID, dwAPIVersion,
    				dialStringOriginal, 0, 0, 
					output);
		if (rc == 0) {
			   		
			strncpy(dialString, 
				(char*)(output)
                	+ output->dwDialableStringOffset,
            		  output->dwDialableStringSize); 
    
    		strncpy(dialStringCanonical,
				(char*)(output) 
                	+ output->dwDisplayableStringOffset,
            		  output->dwDisplayableStringSize); 
								   		
			dwTranslateResults = output->dwTranslateResults;
		}
		delete [] output;
	}
	return rc;
}



long LineDevice::translateDialog(DWORD dwDeviceID, 
    DWORD dwAPIVersion, HWND hwndOwner, LPCSTR lpszAddressIn)
{
	long rc = Error;
	if(lineApp) {
		rc = ::lineTranslateDialog(lineApp, dwDeviceID, 
			dwAPIVersion, hwndOwner, lpszAddressIn);
	}
	return rc;
}
    


long LineDevice::unhold() 
{
	int rc = Error;
	if(hCall) {
		rc = ::lineUnhold(hCall);
	}
	return rc;
}

