//==========================================================================;
//
// playbackimpl.h : additional infrastructure to support implementing IMSVidPlayback
// nicely from c++
// Copyright (c) Microsoft Corporation 1999.
//
/////////////////////////////////////////////////////////////////////////////


#pragma once

#ifndef PLAYBACKIMPL_H
#define PLAYBACKIMPL_H
#include <math.h>
#include "inputimpl.h"
#include <uuids.h>
namespace MSVideoControl {

#define BACKWARDS_STEPPING 0
const long nano_to_hundredths = 100000;    
template<class T, LPCGUID LibID, LPCGUID KSCategory, class MostDerivedInterface = IMSVidPlayback>
    class DECLSPEC_NOVTABLE IMSVidPlaybackImpl :         
    	public IMSVidInputDeviceImpl<T, LibID, KSCategory, MostDerivedInterface> {
protected:
    bool m_fEnableResetOnStop;
public:
    IMSVidPlaybackImpl():        
        m_fEnableResetOnStop(false) {}
	
    virtual ~IMSVidPlaybackImpl() {} 
//-----------------------------------------------------------------------------------------
// Name:
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_Length)(/*[out, retval]*/long *lLength){
        HRESULT hr = S_OK;
        LONGLONG tempval;
        PositionModeList curMode;
        try{
            // Checking args and init'ing interfaces
            if (!lLength){
                return E_POINTER;
            }
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }

            // See if object supports IMediaSeeking
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                // Find out what postion mode is being used
                hr = get_PositionMode(&curMode);
                if(FAILED(hr)){
                    return hr;
                }
                hr = PQIMSeeking->GetDuration(&tempval);
                if(FAILED(hr)){
                    return hr;
                }
                // If it is FrameMode no conversion needed
                if(curMode == FrameMode){
                    *lLength = static_cast<long>(tempval);
                    hr = S_OK;
                    return hr;
                }
                // If it is TenthsSecondsMode need to be converted from 100 nanosecond units
                else if(curMode == TenthsSecondsMode){
                    *lLength = static_cast<long>(tempval / nano_to_hundredths);
                    hr = S_OK;
                    return hr;
                }
                // If it is some other mode not supported by the vidctl
                else{
                    return E_UNEXPECTED;
                }
            }
    
            // See if object supports IMediaPostion
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                // Get position
                double tempDub;
                hr =  PQIMPos->get_CurrentPosition(&tempDub);
                // IMediaPostion only supports 100 Nanosecond units
                *lLength = static_cast<long>(tempDub / nano_to_hundredths);
                hr = S_OK;
                return hr;
            }
            // Could Not QI IMedia Seeking or Position
            return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
            
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
    
//-----------------------------------------------------------------------------------------
// Name: get_CurrentPosition(LONGLONG*)
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_CurrentPosition)(/*[out,retval]*/long *lPosition) {
        HRESULT hr = S_OK;
        LONGLONG tempval;
        PositionModeList curMode;
        try{
            // Checking args and init'ing interfaces
            if (!lPosition){
                return E_POINTER;
            }
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }

            // See if object supports IMediaSeeking
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                // Find out what postion mode is being used
                hr = get_PositionMode(&curMode);
                if(FAILED(hr)){
                    return hr;
                }
                hr = PQIMSeeking->GetCurrentPosition(&tempval);
                if(FAILED(hr)){
                    return hr;
                }
                // If it is FrameMode no conversion needed
                if(curMode == FrameMode){
                    *lPosition = static_cast<long>(tempval);
                    hr = S_OK;
                    return hr;
                }
                // If it is TenthsSecondsMode need to be converted from 100 nanosecond units
                else if(curMode == TenthsSecondsMode){
                    *lPosition = static_cast<long>(tempval / nano_to_hundredths);
                    hr = S_OK;
                    return hr;
                }
                // If it is some other mode not supported by the vidctl
                else{
                    return E_UNEXPECTED;
                }
            }
    
            // See if object supports IMediaPostion
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                // Get position
                double tempDub;
                hr =  PQIMPos->get_CurrentPosition(&tempDub);
                // IMediaPostion only supports 100 Nanosecond units
                *lPosition = static_cast<long>(tempDub / nano_to_hundredths);
                hr = S_OK;
                return hr;
            }
            // Could Not QI IMedia Seeking or Position
            return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
            
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
//-----------------------------------------------------------------------------------------
// Name: put_CurrentPosition(LONGLONG)
//-----------------------------------------------------------------------------------------
	STDMETHOD(put_CurrentPosition)(/*[in]*/long lPosition) {
        HRESULT hr = S_OK;
        LONGLONG tempval = 0;
        PositionModeList curMode;
            LONG curPos;
            try{
            // Checking args and interfaces
            if (!m_pGraph) {
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }

			hr = get_CurrentPosition(&curPos);
			if(curPos == lPosition){
				return NOERROR;
			}
            // Check for a IMediaSeeking Interface
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                // Get the position Mode
                hr = get_PositionMode(&curMode);
                if(FAILED(hr)){
                    return hr;
                }
                tempval = lPosition;
                // If it is in TenthsSecondsMode convert input into 100 nanosecond units
                if(curMode == TenthsSecondsMode){
                    tempval = static_cast<LONGLONG>(lPosition);
                    tempval = tempval * nano_to_hundredths;
                }
                // If it is in some other mode
                else if(curMode != FrameMode){
                    return E_UNEXPECTED;
                }
                // Set the new Position
#if 0
                if(curPos > lPosition && !m_pGraph.IsStopped()){

					DWORD seekingFlags = AM_SEEKING_CanSeekBackwards;
					hr = PQIMSeeking->CheckCapabilities(&seekingFlags);
					if(FAILED(hr)){
						return hr;
					}

				}
#endif
                hr = PQIMSeeking->SetPositions(&tempval, AM_SEEKING_AbsolutePositioning, NULL, 0);
                return hr;
            }
            // Check for a IMediaPostion
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                if(curPos > lPosition && !m_pGraph.IsStopped()){
					long canSeekBackwardRetVal;
                	PQIMPos->CanSeekBackward(&canSeekBackwardRetVal);
            		if(canSeekBackwardRetVal != -1){// OATRUE = -1
                 		return E_INVALIDARG;
            		}
                }
                // IMediaPosition only does 100 nanosecond units
                double tempDub = lPosition;
                tempDub = tempDub * nano_to_hundredths;
                hr = PQIMPos->put_CurrentPosition(tempDub);
                return hr;
            }
            // Could Not QI Media Position or Seeking
            return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
            
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
//-----------------------------------------------------------------------------------------
// Name: put_PositionMode(LONGLONG)
//-----------------------------------------------------------------------------------------

    STDMETHOD(put_PositionMode)(/*[in]*/PositionModeList lPositionMode) {
        HRESULT hr = S_OK;
        double testval;
        get_Rate(&testval);
        try{
            // Checking args and interfaces
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }
            // only valid values
            if(lPositionMode != FrameMode && lPositionMode != TenthsSecondsMode){
                return E_INVALIDARG;
            }
            // Try for a IMediaSeeking
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                // Set the new mode
                if(lPositionMode == FrameMode){
                    return PQIMSeeking->SetTimeFormat( &( static_cast<GUID>(TIME_FORMAT_FRAME) ) );
                }
                if(lPositionMode == TenthsSecondsMode){
                    return PQIMSeeking->SetTimeFormat(&(static_cast<GUID>(TIME_FORMAT_MEDIA_TIME)));
                }
            }
            // Try for a IMediaPosition
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                // Only supports TenthsSecondsMode
                if(lPositionMode == TenthsSecondsMode){
                    return S_OK;
                }
                else{
                    return E_FAIL;
                }
            }
            // Could Not QI
            return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
            
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }


//-----------------------------------------------------------------------------------------
// Name: get_PositionMode(LONGLONG*)
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_PositionMode)(/*[out,retval]*/PositionModeList* lPositionMode) {
        HRESULT hr = S_OK;
        double testval;
        get_Rate(&testval);
        try{
            // Checking args and interfaces
            if(!lPositionMode){
                return E_POINTER;
            }
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }
            // Get an IMediaSeeking Interface
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                // Get the mode
                GUID cur_mode;
                hr = PQIMSeeking->GetTimeFormat(&cur_mode);
                if(FAILED(hr)){
                    return hr;
                }
                // Check to see which mode it is in
                if(cur_mode == static_cast<GUID>(TIME_FORMAT_FRAME)){
                    *lPositionMode = FrameMode;
                    return S_OK;
                }
                if(cur_mode == static_cast<GUID>(TIME_FORMAT_MEDIA_TIME)){
                    *lPositionMode = TenthsSecondsMode;
                    return S_OK;
                }
                // Not in a vidctl supported mode
                else{
                    return E_FAIL;
                }
            }
            // Get IMediaPosition
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                // Only supports TenthsSecondsMode
                *lPositionMode = TenthsSecondsMode;
                return S_OK;
            }
            // Could Not QI
            return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
            
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }

	STDMETHOD(get_Duration)(double *dPos) {
        return E_NOTIMPL;
    }
	STDMETHOD(get_PrerollTime)(double *dPos) {
        return E_NOTIMPL;
    }
	STDMETHOD(put_PrerollTime)(double dPos) {
        return E_NOTIMPL;
    }
	STDMETHOD(get_StartTime)(double *StartTime) {
        return E_NOTIMPL;
    }
	STDMETHOD(put_StartTime)(double StartTime) {
        return E_NOTIMPL;
    }
	STDMETHOD(get_StopTime)(double *StopTime) {
        return E_NOTIMPL;
    }
	STDMETHOD(put_StopTime)(double StopTime) {
        return E_NOTIMPL;
    }

//-----------------------------------------------------------------------------------------
// Name: get_EnableResetOnStop(VARIANT_BOOL*)
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_EnableResetOnStop)(/*[out, retval]*/ VARIANT_BOOL *pVal){
        HRESULT hr = S_OK;
        
        try {
            if(NULL == pVal){ 
                throw(E_POINTER);
            }

            if(m_fEnableResetOnStop == true){
                *pVal = VARIANT_TRUE;
            }
            else{
                *pVal = VARIANT_FALSE;
            }
       
        }
        
        catch(HRESULT hrTmp){  
            hr = hrTmp;
        }
        
        catch(...){
            hr = E_UNEXPECTED;
        }
        
        return hr;
    }// end of function get_EnableResetOnStop 
//-----------------------------------------------------------------------------------------
// Name: put_EnableResetOnStop(VARIANT_BOOL)
//-----------------------------------------------------------------------------------------
    STDMETHOD(put_EnableResetOnStop)(/*[in]*/ VARIANT_BOOL newVal){
        HRESULT hr = S_OK;
        
        try {
            if(newVal == VARIANT_TRUE){ 
                m_fEnableResetOnStop = true;
            }
            else{
                m_fEnableResetOnStop = false;
            }
        }
        catch(...){   
            hr = E_UNEXPECTED;
        }
        
        return hr;
    }// end of function put_EnableResetOnStop

//-----------------------------------------------------------------------------------------
// Name: get_CanStep(VARIANT_BOOL, VARIANT_BOOL*)
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_CanStep)(VARIANT_BOOL fBackwards, VARIANT_BOOL *pfCan){
        // NOTE: NO ONE supports backwords stepping (why not? who knows)
        // so just like everyone else we dont either
        try{
            // Checking args and interfaces 
       
            if(NULL == pfCan){
                // Passed a NULL Pointer
                return E_POINTER;
            }

            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
			}

            //Get a VideoFrameStep Interface
			PQVideoFrameStep pVFS(m_pGraph);
            if(!pVFS){
                // Could Not QI
				return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);          
			}
            
            
#if BACKWARDS_STEPPING // Checking for Backward Stepping should always be 0
            if(fBackwards == VARIANT_TRUE){
                // Backwords Stepping Not Supported Most Likely
                if(pVFS->CanStep(TRUE, NULL)==S_OK){
                    // It is all Good, Can Step Backwords
                    *pfCan = VARIANT_TRUE;
                    return S_OK;
                }
                
                *pfCan = VARIANT_FALSE;
                return S_OK;
            }
#else // Still checking for Backward Stepping
            if(fBackwards == VARIANT_TRUE){
                *pfCan = VARIANT_FALSE;
                return S_OK;
            }
            
#endif // End checking for Backward Stepping          

            // Checking for Forward Stepping 
            else{
                if(pVFS->CanStep(FALSE, NULL)==S_OK){
                    // It is all Good, Can Step Forward
                    *pfCan = VARIANT_TRUE;
                    return S_OK;
                }
                
                else{
                    // Can't Step
                    *pfCan = VARIANT_FALSE;
                    return S_OK;
                }
            }
        }
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;   
        }
    }

//-----------------------------------------------------------------------------------------
// Name: Step(long)
//-----------------------------------------------------------------------------------------
    STDMETHOD(Step)(long lStep){
        try{
            // Checking args and interfaces
            
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }
            
            PQVideoFrameStep pVFS(m_pGraph);
            
            if(!pVFS){
                // Could Not QI
                return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
                
            }
            
#if BACKWARDS_STEPPING // Checking for Backward Stepping should always be 0
            // If backwords stepping set rate or what ever needs to be done
            
            if(lStep < 0){
                // Backwords Stepping Not Supported Most Likely
                if(pVFS->CanStep(TRUE, NULL)==S_OK){
                    // It is all Good, Can Step Backwords
                    CComQIPtr<IMediaPosition> IMPos(m_pGraph);
                    CComQIPtr<IMediaControl> IMCon(m_pGraph);
                    if(IMPos&&IMCon){
                        OAFilterState enterState;
                        IMCon->GetState(INFINITE , &enterState);
                        HRESULT hr = IMPos->put_Rate(1);
                        if(SUCCEEDED(hr)){
                            hr = pVFS->Step((-lStep), NULL);
                            if(SUCCEEDED(hr)){
                                return S_OK;
                            }
                            else{
                                return E_UNEXPECTED;
                            }
                            
                        }
                    }
                }
                // Backwords stepping not supported
                return E_NOTIMPL;
            }
#else // Still checking for Backward Stepping

            if(lStep < 0){
                return E_NOTIMPL;
            }

#endif // End checking for Backward Stepping 
            // Make it step
            return pVFS->Step(lStep, NULL);
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
    
    // note: the following methods control the playback device *NOT* the graph. 
    // if the underlying source filter only supports these functions via 
    // imediacontrol on the graph then this device segment object should return E_NOTIMPL.
    STDMETHOD(Run)() {
        return E_NOTIMPL;
    }
    STDMETHOD(Pause)() {
        return E_NOTIMPL;
    }
    STDMETHOD(Stop)() {
        return E_NOTIMPL;
    }
//-----------------------------------------------------------------------------------------
// Name: put_Rate(double)
//-----------------------------------------------------------------------------------------
    STDMETHOD(put_Rate)(double lRate){
        HRESULT hr = S_OK;
        try{
            /*** Checking args and init'ing interfaces ***/
            
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }

            // Attempt to set the rate using IMediaSeeking
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                return PQIMSeeking->SetRate(lRate);
            }
            
            // If IMediaSeeking FAILS try IMediaPostion
            PQMediaPosition PQIMPos(m_pGraph);
            if(PQIMPos){
                // Change rate
                return PQIMPos->put_Rate((double)lRate);
            }
            
            // Could Not QI Either one set the error
                return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
//-----------------------------------------------------------------------------------------
// Name: get_Rate(double*)
//-----------------------------------------------------------------------------------------
    STDMETHOD(get_Rate)(double *plRate){
        HRESULT hr = S_OK;
        double curRate = 1;
        try{
            /*** Checking args and init'ing interfaces ***/
            if (!plRate){
                return E_POINTER;
            }
            if (!m_pGraph) {
                // graph not valid
                return ImplReportError(__uuidof(T), IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }
            PQMediaSeeking PQIMSeeking(m_pGraph);
            if(PQIMSeeking){
                hr = PQIMSeeking->GetRate(&curRate);
            }
            else{
                PQMediaPosition PQIMPos(m_pGraph);
                if(PQIMPos){
                    // Get rate
                    hr = PQIMPos->get_Rate(&curRate);
                }
                // Could Not QI
                else{
                    return ImplReportError(__uuidof(T), IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
                }
            }
            
            if(SUCCEEDED(hr)){
                *plRate = curRate;
                TRACELSM(TRACE_DETAIL, (dbgDump << "Playbackimpl::get_Rate() rate = " << curRate), "");
            }
            else{
                TRACELSM(TRACE_ERROR, (dbgDump << "Playbackimpl::get_Rate() get_rate failed"), "");
            }
            return hr;
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return ImplReportError(__uuidof(T), IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }

};

}; // namespace

#endif
// end of file - playbackimpl.h