//===========================================================================
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved.
//
//===========================================================================
//
//	filename: cvcrutil.cpp
//
// Base classes implementing external device control (VCR) interfaces.
// Derive from these and implement just the custom method and property
// methods.
//

#include <windows.h>
#include <streams.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <vfw.h>

#include "vcruids.h"
#include <initguid.h>
#include <olectl.h>

#include "ctimecod.h"
#include "cdevcom.h"
#include "cvcrutil.h"

#define DbgFunc(a) DbgLog(( LOG_TRACE, \
                            2, \
                            TEXT("VCRTC::%16hu"), \
                            TEXT(a) \
                         ));

const long ExtVidConnType[] = {
		PhysConn_Video_Tuner,
		PhysConn_Video_Composite,
		PhysConn_Video_SVideo,
		PhysConn_Video_RGB,
		PhysConn_Video_YRYBY,
		PhysConn_Video_SerialDigital,
		PhysConn_Video_ParallelDigital,
		PhysConn_Video_SCSI,
		PhysConn_Video_AUX,
		PhysConn_Video_1394,
		PhysConn_Video_USB
};
const int ExtVidConnCnt = sizeof(ExtVidConnType);

const TCHAR *strExtVidConnType[] = {
		TEXT("Tuner"),
		TEXT("Composite"),
		TEXT("S-Video"),
		TEXT("RGB"),
		TEXT("Y//R-Y//B-Y"),
		TEXT("Serial Digital"),
		TEXT("Parallel Digital"),
		TEXT("SCSI"),
		TEXT("AUX"),
		TEXT("1394"),
		TEXT("USB")
};

const long ExtAudConnType[] = {
		PhysConn_Audio_Tuner,
		PhysConn_Audio_Line,
		PhysConn_Audio_Mic,
		PhysConn_Audio_AESDigital,
		PhysConn_Audio_SPDIFDigital,
		PhysConn_Audio_SCSI,
		PhysConn_Audio_AUX,
		PhysConn_Audio_1394,
		PhysConn_Audio_USB
};
const int ExtAudConnCnt = sizeof(ExtAudConnType);

const TCHAR *strExtAudConnType[] = {
		TEXT("Tuner"),
		TEXT("Line"),
		TEXT("Mic"),
		TEXT("AES Digital"),
		TEXT("SPDIF Digital"),
		TEXT("SCSI"),
		TEXT("AUX"),
		TEXT("1394"),
		TEXT("USB")
};
static TCHAR buf[32];
static TCHAR buf1[32];
const TCHAR strDummyDeviceVersion[] = TEXT("1.0");

//------------------------------------------
//
//	CAMPhysicalPinInfo Implementation
//
//------------------------------------------
CAMPhysicalPinInfo::CAMPhysicalPinInfo(HRESULT * phr, TCHAR *tszName) :
	CUnknown( tszName, NULL )
{
	
}

//------------------------------------------
//
//	Destructor
//
//------------------------------------------
CAMPhysicalPinInfo::~CAMPhysicalPinInfo()
{
 	
}

//------------------------------------------
//
// NonDelegatingQueryInterface
//
// Reveal our Interface
//------------------------------------------
STDMETHODIMP
CAMPhysicalPinInfo::NonDelegatingQueryInterface(REFIID riid, void **ppv) {

	CAutoLock cObjectLock(&m_StateLock);

    if (riid == IID_IAMPhysicalPinInfo)
        return GetInterface((IAMPhysicalPinInfo *) this, ppv);
    else
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
} 

//------------------------------------------
//
// CAMExtDevice implementation
//
//------------------------------------------
CAMExtDevice::CAMExtDevice(HRESULT * phr, TCHAR *tszName) :
	CUnknown( tszName, NULL ),
	m_CurDevicePort(NULL)
{
	HRESULT Result;
	// must have a way to talk to a machine
	m_devcom = new CDevCom((CBaseFilter *)this, &Result);
    if (m_devcom == NULL || Result) {
        *phr = E_OUTOFMEMORY;
        return;
	}

	// initialize the capabilities structures - we default to a test VCR,
	// in this case a simple SVHS recorder such as the SVO-5800 (RS-422)
	// or other machine with an SVBK-10 RS-232 interface
	m_DevCaps.CanRecord = OATRUE;
	m_DevCaps.CanRecordStrobe = OAFALSE;
	m_DevCaps.HasAudio = OATRUE;
	m_DevCaps.HasVideo = OATRUE;          
	m_DevCaps.UsesFiles = OAFALSE;         
	m_DevCaps.CanSave = OAFALSE;           
	m_DevCaps.DeviceType = ED_DEVTYPE_VCR;        
	m_DevCaps.TCRead = OATRUE;            
	m_DevCaps.TCWrite = OAFALSE;           
	m_DevCaps.CTLRead = OATRUE;           
	m_DevCaps.IndexRead = OAFALSE;         
	m_DevCaps.Preroll = 150L;           
	m_DevCaps.Postroll = 30L;;          
	m_DevCaps.SyncAcc = ED_SYNCACC_PRECISE;           
	m_DevCaps.NormRate = ED_RATE_2997;          
	m_DevCaps.CanPreview = OATRUE;        
	m_DevCaps.CanMonitorSrc = OATRUE;    
	m_DevCaps.CanTest = OATRUE;           
	m_DevCaps.VideoIn = OATRUE;           
	m_DevCaps.AudioIn = OATRUE;           
	m_DevCaps.Calibrate = OAFALSE;
	m_DevCaps.SeekType = ED_SEEK_SLOW;          
			
	// default comm port and power state
	put_DevicePort(DEV_PORT_COM2);
	put_DevicePower(ED_POWER_ON);
}

//------------------------------------------
//
//	Destructor
//
//------------------------------------------
CAMExtDevice::~CAMExtDevice()
{
 	m_devcom->CloseDevPort();
}

//------------------------------------------
//
// NonDelegatingQueryInterface
//
// Reveal our Interface
//------------------------------------------
STDMETHODIMP
CAMExtDevice::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
	CAutoLock cObjectLock(&m_StateLock);

    if (riid == IID_IAMExtDevice)
        return GetInterface((IAMExtDevice *) this, ppv);
    else
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
} 

//------------------------------------------
//
//	GetCapability
//
// General device capabilities property
//------------------------------------------
HRESULT 
CAMExtDevice::GetCapability(long Capability, long *pValue, double *pdblValue )
{
	HRESULT result = S_OK;
	
	switch (Capability){
		 
		case ED_DEVCAP_CAN_RECORD:
			*pValue = m_DevCaps.CanRecord;
			break;
		case ED_DEVCAP_CAN_RECORD_STROBE:
			*pValue = m_DevCaps.CanRecordStrobe;
			break;
		case ED_DEVCAP_HAS_AUDIO:
			*pValue = m_DevCaps.HasAudio;
			break;
		case ED_DEVCAP_HAS_VIDEO:		
			*pValue = m_DevCaps.HasVideo;
			break;
		case ED_DEVCAP_USES_FILES:	
			*pValue = m_DevCaps.UsesFiles;
			break;
		case ED_DEVCAP_CAN_SAVE:		
			*pValue = m_DevCaps.CanSave;
			break;
		case ED_DEVCAP_DEVICE_TYPE:	
			*pValue = m_DevCaps.DeviceType;
			break;
		case ED_DEVCAP_TIMECODE_READ:			
			*pValue = m_DevCaps.TCRead;
			break;
		case ED_DEVCAP_TIMECODE_WRITE:	
			*pValue = m_DevCaps.TCWrite;
			break;
		case ED_DEVCAP_CTLTRK_READ: 			
			*pValue = m_DevCaps.CTLRead;
			break;
		case ED_DEVCAP_INDEX_READ:			
			*pValue = m_DevCaps.IndexRead;
			break;
		case ED_DEVCAP_PREROLL: 		
			*pValue = m_DevCaps.Preroll;
			break;
		case ED_DEVCAP_POSTROLL: 		
			*pValue = m_DevCaps.Postroll;
			break;
		case ED_DEVCAP_SYNC_ACCURACY: 	
			*pValue = m_DevCaps.SyncAcc;
			break;
		case ED_DEVCAP_NORMAL_RATE:
			*pValue = m_DevCaps.NormRate;
			break;
		case ED_DEVCAP_CAN_PREVIEW: 		
			*pValue = m_DevCaps.CanPreview;
			break;
		case ED_DEVCAP_CAN_MONITOR_SOURCES: 
			*pValue = m_DevCaps.CanMonitorSrc;
			break;
		case ED_DEVCAP_CAN_TEST:
			*pValue = m_DevCaps.CanTest;
			break;
		case ED_DEVCAP_VIDEO_INPUTS: 			
			*pValue = m_DevCaps.VideoIn;
			break;
		case ED_DEVCAP_AUDIO_INPUTS: 			
			*pValue = m_DevCaps.AudioIn;
			break;
		case ED_DEVCAP_NEEDS_CALIBRATING:
			*pValue = m_DevCaps.Calibrate;
			break;
		case ED_DEVCAP_SEEK_TYPE:
			*pValue = m_DevCaps.SeekType;
			break;
		default:
			result = VFW_E_NOT_FOUND;
	}	                           
	return S_OK;
}        

//------------------------------------------
//
//	get_ExternalDeviceID
//
// Get external device identification string
//------------------------------------------
HRESULT
CAMExtDevice::get_ExternalDeviceID(LPOLESTR * ppszData)
{
	int Cookie;
	
	CheckPointer(ppszData, E_POINTER);
	*ppszData = NULL;
	// query the device
	if (m_devcom->SendDevCmd(DEVICE_TYPE_REQUEST, &Cookie) )
		return E_FAIL;
	if (m_devcom->Wait4DevResponse(buf, Cookie))
		return E_FAIL;
	m_devcom->ProcessDeviceTypeResponse(buf, buf1);
	if (buf1 !=NULL) {
		*ppszData = (LPOLESTR)
			QzTaskMemAlloc(sizeof(WCHAR) * (1+lstrlenW((LPOLESTR)buf1)));
		if (*ppszData !=NULL)
			lstrcpyW(*ppszData, (LPOLESTR)buf1);
	}
	return S_OK;
}

//------------------------------------------
//
//	get_ExternalDeviceVersion
//
//------------------------------------------
HRESULT
CAMExtDevice::get_ExternalDeviceVersion(LPOLESTR * ppszData)
{
	// The sample machine doesn't support this, so send
	// a dummy response
	CheckPointer(ppszData, E_POINTER);
	*ppszData = NULL;
	// query the device
	*ppszData = (LPOLESTR)
			QzTaskMemAlloc(sizeof(WCHAR) * \
			(1+lstrlenW((LPOLESTR)strDummyDeviceVersion)));
	if (*ppszData !=NULL)
			lstrcpyW(*ppszData, (LPOLESTR)strDummyDeviceVersion);

	return S_OK;
}

//------------------------------------------
//
//	put_DevicePower	
//
// Controls the external device's power mode.
// Unfortunately, the sample machine doesn't support this
//------------------------------------------
HRESULT
CAMExtDevice::put_DevicePower(long PowerMode)
{
	TESTFLAG(PowerMode, OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_DevicePower
//
//------------------------------------------
HRESULT 
CAMExtDevice::get_DevicePower(long *pPowerMode)
{
	// if the device port is being simulated, assume power off
	if (m_CurDevicePort == DEV_PORT_SIM)
		m_PowerMode = ED_POWER_OFF;
	else
		m_PowerMode = ED_POWER_ON;
	*pPowerMode = m_PowerMode;
	return S_OK;
}

//------------------------------------------
//
//	Calibrate
//
//------------------------------------------		
HRESULT
CAMExtDevice::Calibrate(HEVENT hEvent, long Mode, long *pStatus)
{
	TESTFLAG( Mode , OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_DevicePort
//
//------------------------------------------
HRESULT
CAMExtDevice::get_DevicePort(long *pDevicePort)
{
	*pDevicePort = m_CurDevicePort;
	return S_OK;
}

//------------------------------------------
//
//	put_DevicePort
//
//------------------------------------------
HRESULT
CAMExtDevice::put_DevicePort(long DevicePort)
{
	TESTFLAG( DevicePort, OATRUE);

	if ( DevicePort != m_CurDevicePort ) {
		// range check
		if ((DevicePort < DEV_PORT_MIN) ||
			(DevicePort > DEV_PORT_MAX))
				return VFW_E_NOT_FOUND;	// find correct error code
		if ( (m_CurDevicePort != NULL) & (m_CurDevicePort !=DEV_PORT_SIM) )
			m_devcom->CloseDevPort();
		if (m_devcom->OpenDevPort(DevicePort))
			m_CurDevicePort = DEV_PORT_SIM;
		else
			m_CurDevicePort = DevicePort;
	}
	return S_OK;
}

//------------------------------------------
//
// CAMExtTransport implementation
//
//------------------------------------------
CAMExtTransport::CAMExtTransport(HRESULT * phr, TCHAR *tszName) :
	CUnknown( tszName, NULL )
{
	// initialize the video parms
	m_TranVidParms.OutputMode = ED_PLAYBACK;
	m_TranVidParms.Input = 0;		// use the first (zeroth) input as the default

	m_TranAudParms.EnableOutput = ED_AUDIO_ALL;
	m_TranAudParms.EnableRecord = 0L;
	m_TranAudParms.Input = 0;
	m_TranAudParms.MonitorSource = 0;

	m_TranStatus.Mode = ED_MODE_STOP;
	m_TranStatus.LastError = 0L;
	m_TranStatus.RecordInhibit = OAFALSE;
	m_TranStatus.ServoLock = OAFALSE;
	m_TranStatus.MediaPresent = OAFALSE;
	m_TranStatus.MediaLength = 
	m_TranStatus.MediaSize = 
	m_TranStatus.MediaTrackCount = 
	m_TranStatus.MediaTrackLength =
	m_TranStatus.MediaTrackSide =
	m_TranStatus.MediaType = 0L;
	m_TranStatus.LinkMode = OATRUE;	// default to "linked"

	m_localcontrol = FALSE;
}

//------------------------------------------
//
//	Destructor
//
//------------------------------------------
CAMExtTransport::~CAMExtTransport()
{

}

//------------------------------------------
//
// NonDelegatingQueryInterface
//
// Reveal our Interface
//------------------------------------------
STDMETHODIMP
CAMExtTransport::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
	CAutoLock cObjectLock(&m_StateLock);

    if (riid == IID_IAMExtTransport)
        return GetInterface((IAMExtTransport *) this, ppv);
    else
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
} 

//------------------------------------------
//
//	SetCommunicationObject
//
//------------------------------------------
HRESULT 
CAMExtTransport::SetCommunicationObject(CDevCom *pCDevCom)
{
	m_pTDevCom = pCDevCom;
	return S_OK;
}

//------------------------------------------
//
//	GetCapability
//
// Transport capabilities property
//------------------------------------------
HRESULT 
CAMExtTransport::GetCapability(long Capability, long *pValue,
							   double *pdblValue )
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	put_MediaState
//
//------------------------------------------
HRESULT
CAMExtTransport::put_MediaState(long State)
{
	TESTFLAG(State, OATRUE);
	int Cookie;
	// the only thing we'll do here is eject - all others imply
	// a bigger state change (like STOP->FREEZE) that we'd like to 
	// do here
	if (State == ED_MEDIA_UNLOAD) {
		if (m_TranStatus.Mode != ED_MODE_STOP)
			CAMExtTransport::put_Mode(ED_MODE_STOP);
		if (m_pTDevCom->SendDevCmd(EJECT, &Cookie))
			return E_FAIL;
		if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
			return E_FAIL;
		Sleep(4000);	// wait a bit until the machine completes
		m_mediastate = State;
		return S_OK;
	}
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_MediaState
//
//------------------------------------------
HRESULT
CAMExtTransport::get_MediaState(long FAR* pState)
{
	int Cookie;
	BOOL bStatusRet;

	// This might be different for a device other than a VCR, but:
	// ED_MEDIA_SPIN_UP: media inserted and not stopped
	// ED_MEDIA_SPIN_DOWN: media inserted and stopped
	// ED_MEDIA_UNLOAD: media ejected
	if (m_TranStatus.Mode == ED_MODE_STOP) {
		// check device for presence of media
		do {
			if (m_pTDevCom->SendDevCmd(REQUEST_STATUS, &Cookie))
				return E_FAIL;
			if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
				return E_FAIL;
			bStatusRet = m_pTDevCom->ProcessDeviceStatusResponse(buf,
										&m_VcrStatus);
		} while (bStatusRet);
		if (m_VcrStatus.bCassetteOut == OATRUE)
			*pState = ED_MEDIA_UNLOAD;
		else
			*pState = ED_MEDIA_SPIN_DOWN;
	}
	else
		*pState = ED_MEDIA_SPIN_UP;

	m_mediastate = *pState;
	return S_OK;
}

//------------------------------------------
//
//	put_LocalControl
//
//------------------------------------------
HRESULT
CAMExtTransport::put_LocalControl(long State)
{
	TESTFLAG(State, OATRUE);
	long temp;
	int Cookie;
	DWORD dwResult;

	if (State == OATRUE)
		temp = LOCAL_ON;
	else
		temp = LOCAL_OFF;
	dwResult = m_pTDevCom->SendDevCmd(temp, &Cookie);
	if (dwResult == DEV_COMM_ERR_COMMAND_NOT_SUPPORTED)
		return E_NOTIMPL;
	if (dwResult)
		return E_FAIL;
	if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
		return E_FAIL;
	return S_OK;
}

//------------------------------------------
//
//	get_LocalControl
//
//------------------------------------------
HRESULT
CAMExtTransport::get_LocalControl(long FAR* pState)
{
	int Cookie;

	if (m_pTDevCom->SendDevCmd(REQUEST_STATUS, &Cookie))
		return E_FAIL;
	if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
		return E_FAIL;
	m_pTDevCom->ProcessDeviceStatusResponse(buf, &m_VcrStatus);
	*pState = m_VcrStatus.bLocal;
	return S_OK;
}

//------------------------------------------
//
//	GetStatus
//
//------------------------------------------
HRESULT
CAMExtTransport::GetStatus(long StatusItem, long FAR* pValue)
{
	switch (StatusItem) {
		case ED_LINK_MODE:
			*pValue = m_TranStatus.LinkMode;
			return S_OK;
		default:
			return E_NOTIMPL;
	}
}

//------------------------------------------
//
//	GetTransportBasicParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::GetTransportBasicParameters(long Param, long FAR* pValue,
											 LPOLESTR * ppszData)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetTransportBasicParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::SetTransportBasicParameters(long Param, long Value,
											 LPCOLESTR pszData)
{
	// we test false because this is not implemented
	TESTFLAG(Param, OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetTransportVideoParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::GetTransportVideoParameters(long Param, long FAR* pValue)
{
	HRESULT hr = S_OK;

	switch (Param) {
		case ED_TRANSVIDEO_SET_OUTPUT:
			*pValue = m_TranVidParms.OutputMode;
			break;
		case ED_TRANSVIDEO_SET_SOURCE:
			*pValue = m_TranVidParms.Input;
			break;
		default:
			hr = VFW_E_NOT_FOUND;
	}
	return hr;
}

//------------------------------------------
//
//	SetTransportVideoParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::SetTransportVideoParameters(long Param, long Value)
{
	// we test false because this is not implemented
	TESTFLAG(Param, OAFALSE);
	return E_NOTIMPL;	
}

//------------------------------------------
//
//	GetTransportAudioParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::GetTransportAudioParameters(long Param, long FAR* pValue)
{
	HRESULT hr = S_OK;

	switch (Param) {
		case ED_TRANSAUDIO_ENABLE_OUTPUT:
			*pValue = m_TranAudParms.EnableOutput;
			break;
		case ED_TRANSAUDIO_ENABLE_RECORD:
			*pValue = m_TranAudParms.EnableRecord;
			break;
		case ED_TRANSAUDIO_SET_SOURCE:
			*pValue = m_TranAudParms.Input;
			break;
		case ED_TRANSAUDIO_SET_MONITOR:
			*pValue = m_TranAudParms.MonitorSource;
			break;
		default:
			hr = VFW_E_NOT_FOUND;
	}
	return hr;
}

//------------------------------------------
//
//	SetTransportAudioParameters
//
//------------------------------------------
HRESULT
CAMExtTransport::SetTransportAudioParameters(long Param, long Value)
{
	// we test false because this is not implemented
	TESTFLAG(Param, OAFALSE);
	return E_NOTIMPL;	
}

//------------------------------------------
//
//	put_Mode
//
//------------------------------------------
HRESULT
CAMExtTransport::put_Mode(long Mode)
{
	HRESULT result = S_OK;
	DWORD cmdresult;
	static TCHAR buf[32];
	int Cookie;
	
	TESTFLAG(Mode, OATRUE);
	if ( Mode == m_TranStatus.Mode )
			return S_OK;	// don't repeat commands
	if ( m_localcontrol == TRUE )
		return ED_ERR_DEVICE_NOT_READY;

	switch (Mode) {
		case ED_MODE_PLAY:
			cmdresult = m_pTDevCom->SendDevCmd(PLAY, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_PLAY;
			break;
		case ED_MODE_STOP:
			cmdresult = m_pTDevCom->SendDevCmd(STOP, &Cookie);		
			ASSERT(cmdresult == 0L);
			if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
				return E_FAIL;
			cmdresult = m_pTDevCom->SendDevCmd(STANDBY_OFF, &Cookie);
			// we know that some machines don't support STANDBY_OFF
			if (cmdresult != 0 &&
				cmdresult != DEV_COMM_ERR_COMMAND_NOT_SUPPORTED )
				ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_STOP;
			break;
		case ED_MODE_FREEZE:
			if (m_TranStatus.Mode == ED_MODE_STOP) {
				return result;
			}
			cmdresult = m_pTDevCom->SendDevCmd(FREEZE_ON, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_FREEZE;
			break;
		case ED_MODE_THAW:
			cmdresult = m_pTDevCom->SendDevCmd(FREEZE_OFF, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_TranStatus.Mode = m_lastmode;
			break;

		case ED_MODE_FF:			
			cmdresult = m_pTDevCom->SendDevCmd(FFWD, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_FF;
			break;
		case ED_MODE_REW:
			cmdresult = m_pTDevCom->SendDevCmd(REWIND, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_REW;
			break;
		case ED_MODE_RECORD:
			cmdresult = m_pTDevCom->SendDevCmd(RECORD, &Cookie);
			ASSERT(cmdresult == 0L);
			if (cmdresult == 0L)
				if (m_pTDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			m_lastmode = m_TranStatus.Mode;
			m_TranStatus.Mode = ED_MODE_RECORD;
			break;
		case ED_MODE_RECORD_STROBE:
			result = E_NOTIMPL;
			break;
		case ED_MODE_STEP:
			result = E_NOTIMPL;
			break;
		case ED_MODE_SHUTTLE:
			result = E_NOTIMPL;
			break;
		case ED_MODE_LINK_ON:
			m_TranStatus.LinkMode = OATRUE;
			break;
		case ED_MODE_LINK_OFF:
			m_TranStatus.LinkMode = OAFALSE;
			break;
		default:
			result = E_NOTIMPL;
	}
	// clean up if things went well
	return result;
}

//------------------------------------------
//
//	get_Mode
//
//------------------------------------------
HRESULT
CAMExtTransport::get_Mode(long FAR* pMode)
{
	*pMode = m_TranStatus.Mode;
	return S_OK;
}

//------------------------------------------
//
//	put_Rate
//
//------------------------------------------
HRESULT
CAMExtTransport::put_Rate(double dblRate)
{
	// add Test flag support
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_Rate
//
//------------------------------------------
HRESULT
CAMExtTransport::get_Rate(double FAR* pdblRate)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetChase
//
//------------------------------------------
HRESULT
CAMExtTransport::GetChase(long FAR* pEnabled, long FAR* pOffset,
						  HEVENT FAR* phEvent)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetChase
//
//------------------------------------------
HRESULT
CAMExtTransport::SetChase(long Enable, long Offset, HEVENT hEvent)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetBump
//
//------------------------------------------
HRESULT
CAMExtTransport::GetBump(long FAR* pSpeed, long FAR* pDuration)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetBump
//
//------------------------------------------
HRESULT
CAMExtTransport::SetBump(long Speed, long Duration)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_AntiClogControl
//
//------------------------------------------
HRESULT
CAMExtTransport::get_AntiClogControl(long FAR* pEnabled)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	put_AntiClogControl
//
//------------------------------------------
HRESULT
CAMExtTransport::put_AntiClogControl(long Enable)
{
	TESTFLAG(Enable, OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetEditPropertySet
//
//------------------------------------------
HRESULT
CAMExtTransport::GetEditPropertySet(long EditID, long FAR* pState)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetEditPropertySet
//
//------------------------------------------
HRESULT
CAMExtTransport::SetEditPropertySet(long FAR* pEditID, long State)
{
	TESTFLAG(State, OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetEditProperty
//
//------------------------------------------
HRESULT
CAMExtTransport::GetEditProperty(long EditID, long Param, long FAR* pValue)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetEditProperty
//
//------------------------------------------
HRESULT
CAMExtTransport::SetEditProperty(long EditID, long Param, long Value)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_EditStart
//
//------------------------------------------
HRESULT
CAMExtTransport::get_EditStart(long FAR* pValue)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	put_EditStart
//
//------------------------------------------
HRESULT
CAMExtTransport::put_EditStart(long Value)
{
	TESTFLAG(Value, OAFALSE);
	return E_NOTIMPL;
}

//------------------------------------------
//
// CAMTcr implementation
//
//------------------------------------------
CAMTcr::CAMTcr(HRESULT * phr, TCHAR *tszName) :
	CUnknown( tszName, NULL )
{
	// need some help with timecode
	m_timecode = new CTimecode();
}

//------------------------------------------
//
//	Destructor
//
//------------------------------------------
CAMTcr::~CAMTcr()
{

}
//------------------------------------------
//
// NonDelegatingQueryInterface
//
// Reveal our Interface
//------------------------------------------
STDMETHODIMP
CAMTcr::NonDelegatingQueryInterface(REFIID riid, void **ppv) {

	CAutoLock cObjectLock(&m_StateLock);

    if (riid == IID_IAMTimecodeReader)
        return GetInterface((IAMTimecodeReader *) this, ppv);
    else
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
} 

//------------------------------------------
//
//	SetCommunicationObject
//
//------------------------------------------
HRESULT 
CAMTcr::SetCommunicationObject(CDevCom *pCDevCom)
{
	m_pTCDevCom = pCDevCom;
	return S_OK;
}

//------------------------------------------
//
//	GetTCRMode
//
//------------------------------------------
HRESULT 
CAMTcr::GetTCRMode(long Param, long FAR* pValue)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	get_VITCLine
//
//------------------------------------------
HRESULT 
CAMTcr::get_VITCLine(long * pLine)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	put_VITCLine
//
//------------------------------------------
HRESULT 
CAMTcr::put_VITCLine(long Line)
{
	return E_NOTIMPL;
}

//------------------------------------------
//
//	SetTCRMode
//
//------------------------------------------
HRESULT 
CAMTcr::SetTCRMode(long Param, long Value)
{
	TCHAR buf[20];
	int Cookie;
	DWORD dwResult;

	TESTFLAG(Param, OATRUE);
	// the real implementation will use a table instead
	//	of a switch.  This seriously partial implementation
	//	is just to get basic timecode reading right
	switch (Param)
	{
		case ED_TCR_SOURCE:
			if ( Value == ED_TCR_LTC ) {
				dwResult = m_pTCDevCom->SendDevCmd(TIMER_MODE_SELECT_LTC,
										&Cookie);
				// we know that some machines don't support this command
				// and don't even ask for an answer
				if ( dwResult == DEV_COMM_ERR_COMMAND_NOT_SUPPORTED)
					return E_NOTIMPL;
				if ( dwResult )	// fail on all other result codes
					return E_FAIL;
				if (m_pTCDevCom->Wait4DevResponse(buf, Cookie))
					return E_FAIL;
			}
			return S_OK;
		default:
			return E_NOTIMPL;
	}
	return E_NOTIMPL;
}

//------------------------------------------
//
//	GetTimecode
//
//------------------------------------------
HRESULT 
CAMTcr::GetTimecode( PTIMECODE_SAMPLE pTimecodeSample)
{
	DWORD cmdresult;
	static TCHAR buf[32];
	DWORD fcm;
	int Cookie;
       LONG lFrame, lUser ;

	cmdresult = m_pTCDevCom->SendDevCmd(READ_TC, &Cookie);
	if ( cmdresult == 0L )
	{	// commands might get refused
		if ( (cmdresult = m_pTCDevCom->Wait4DevResponse(buf, Cookie)) )
			return cmdresult;//E_FAIL;
		DbgLog((LOG_TRACE, 1, buf));
		m_pTCDevCom->ProcessVcrTC(buf, &fcm, &lFrame, &lUser);
		pTimecodeSample->timecode.wFrameRate = (WORD)fcm;
		pTimecodeSample->timecode.dwFrames = (DWORD) lFrame ;
		pTimecodeSample->dwUser = (DWORD) lUser ;
		pTimecodeSample->dwFlags = 0L;
		return S_OK;
	}
	// leave the reference time alone for now 
	// (to be filled in by calling filter)
	return S_FALSE;
}

//eof  cvcrutil.cpp
