/***************************************************************/
//
//
//		DirectX	[ videorenderer.cpp ]
//
//											Author	kazuki tanaka
//											Date	2017 5/5
/*---------------------------------------------------------------
■Update : 2017/5/5
			videorenderer.cppの作成

/*---------------------------------------------------------------
	Include File
---------------------------------------------------------------*/


//#include <Windows.h>
//#include <stdio.h>
//#include <d3dx9.h>

#define _CRT_SECURE_NO_WARNINMGS_
#define _CRT_NONO_CONFORMING_SWPRINTFS

#define _WIN32_WINNT 0x0400
#define _WIN32_DCOM     // CoInitializeEx関数の呼び出しに必要

#include "videorenderer.h"
#include "dshowTextures.h"

#include <stdlib.h>		// ワイド文字変換に必要
#include <locale.h>		// 日本語ワイド文字変換に必要

#include <strsafe.h>
#include <wxdebug.h>

#include <wchar.h>

#include "game_manager.h"

#include "rendererdx9.h"


#pragma comment (lib,"d3d9.lib")
#pragma comment (lib,"d3dx9.lib")

#pragma comment(lib, "wsdapi.lib")				// v6.0 WindowsSDK library

#pragma comment(lib, "strmiids.lib")

#ifdef _DEBUG
#pragma comment(lib, "strmbasd.lib")			// DSHOWライブラリ( _DEBUG )
#else
#pragma comment(lib, "strmbase.lib")			// DSHOWライブラリ( _RELEASE )
#endif // DEBUG


/*---------------------------------------------------------------
	Macro Difinition
---------------------------------------------------------------*/

#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
#endif // SAFE_RELEASE

/*---------------------------------------------------------------
	Const Number Difinition
---------------------------------------------------------------*/

/*----------------------------------------------------------------
	static member variable Initialize 
----------------------------------------------------------------*/

CVideoRenderer* CVideoRenderer::pVideoRenderer[ VIDEORENDERER_MAX ] = { };



// CVideoRenderer Constructor
CVideoRenderer::CVideoRenderer( )
	:pGraphBuilder( nullptr )
	,pMediaControl( nullptr )
	,pMediaPos( nullptr )
	,pMediaEvent( nullptr )
	,pRenderer( nullptr )
{

	// Member Clear Process



	// Registration Class( VideoRenderer )
	for( int index = 0; index < VIDEORENDERER_MAX; index++ ){
		if( pVideoRenderer[index] == NULL ){
			pVideoRenderer[index] = this;
			break;
		}
	}


}

// CVideoRenderer Destructor
CVideoRenderer::~CVideoRenderer( )
{

	// Do Nothing!!

}

// CVideoRenderer Create Instance 
HRESULT CVideoRenderer::CreateRealtimeRenderTexture( const char* SOURCE_FILE_NAME, LPDIRECT3DTEXTURE9* pTexture )
{

	// Create Class Instance
	CVideoRenderer* pObject;
	pObject = new CVideoRenderer;

	// jp Wide Char Transform Setting
	setlocale( LC_CTYPE, "jpn" );

	// Multibyte Charset -> Wide Char
	wchar_t fileName[100] = {};
	if( !mbstowcs( fileName, SOURCE_FILE_NAME, 100 ) ){
		return E_FAIL;
	}

	// Setting Class Action
	if( FAILED( pObject->InitDShowTextureRenderer( fileName, pTexture ) )){
		pObject->CleanupDShow( );
		SAFE_DELETE( pObject );
        return E_FAIL;
	}

	return S_OK;

}

// InitDShowTextureRenderer : Create DirectShow filter graph and run the graph
HRESULT CVideoRenderer::InitDShowTextureRenderer( const LPCWSTR SOURCE_FILE_NAME, LPDIRECT3DTEXTURE9* pTexture )
{

    HRESULT hr = S_OK;
    SmartPtr<IBaseFilter> pFSrc;						// Source Filter
    SmartPtr<IPin>        pFSrcPinOut;					// Source Filter Output Pin
    CTextureRenderer*     pTextureRenderer = nullptr;	// DirectShow Texture renderer

	// Com Initialize MultiThread
	CoInitializeEx( NULL, COINIT_MULTITHREADED );

    // Create the filter graph
	hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraphBuilder);
    if (FAILED(hr))
        return E_FAIL;

	#ifdef REGISTER_FILTERGRAPH
    // Register the graph in the Running Object Table (for debug purposes)
    AddToROT(g_pGraphBuilder);
	#endif

    // Create the Texture Renderer object
    pTextureRenderer = new CTextureRenderer( NULL, &hr, pTexture );
    if (FAILED(hr) || !pTextureRenderer){
        delete pTextureRenderer;
        Msg(TEXT("Could not create texture renderer object!  hr=0x%x"), hr);
        return E_FAIL;
    }

    // Get a pointer to the IBaseFilter on the TextureRenderer, add it to graph
    pRenderer = pTextureRenderer;
    if (FAILED(hr = pGraphBuilder->AddFilter(pRenderer, L"TEXTURERENDERER"))){

        Msg(TEXT("Could not add renderer filter to graph!  hr=0x%x"), hr);
        return hr;
    }

    // Determine the file to load based on windows directory
    // Use the standard win32 API to do this.

    // Add the source filter to the graph.
    hr = pGraphBuilder ->AddSourceFilter ( SOURCE_FILE_NAME, L"SOURCE", &pFSrc );


    // If the media file was not found, inform the user.
    if (hr == VFW_E_NOT_FOUND ){

        Msg(TEXT("Could not add source filter to graph!  (hr==VFW_E_NOT_FOUND)\r\n\r\n")
            TEXT("This sample reads a media file from your windows directory.\r\n")
            TEXT("This file is missing from this machine."));
        return hr;
    }
    else if(FAILED(hr)){
        Msg(TEXT("Could not add source filter to graph!  hr=0x%x"), hr);
        return hr;
    }

	// Pin Set
    if (FAILED(hr = pFSrc->FindPin(L"Output", &pFSrcPinOut))){
        Msg(TEXT("Could not find output pin!  hr=0x%x"), hr);
        return hr;
    }

	#ifdef NO_AUDIO_RENDERER
    // If no audio component is desired, directly connect the two video pins
    // instead of allowing the Filter Graph Manager to render all pins.

    CComPtr<IPin> pFTRPinIn;      // Texture Renderer Input Pin

    // Find the source's output pin and the renderer's input pin
    if (FAILED(hr = pFTR->FindPin(L"In", &pFTRPinIn))){
        Msg(TEXT("Could not find input pin!  hr=0x%x"), hr);
        return hr;
    }

    // Connect these two filters
    if (FAILED(hr = pGraphBuilder->Connect(pFSrcPinOut, pFTRPinIn))){
        Msg(TEXT("Could not connect pins!  hr=0x%x"), hr);
        return hr;
    }
	#else // NO_AUDIO_RENDERER

    // Render the source filter's output pin.  The Filter Graph Manager
    // will connect the video stream to the loaded CTextureRenderer
    // and will load and connect an audio renderer (if needed).

    if (FAILED(hr = pGraphBuilder->Render(pFSrcPinOut))){
        Msg(TEXT("Could not render source output pin!  hr=0x%x"), hr);
        return hr;
    }
	#endif // NO_AUDIO_RENDERER

    // Get the graph's media control, event & position interfaces
    pGraphBuilder->QueryInterface(&pMediaControl);
    pGraphBuilder->QueryInterface(&pMediaPos);
    pGraphBuilder->QueryInterface(&pMediaEvent);

    // Start the graph running;
    if ( FAILED(hr = pMediaControl->Run( )) ){
        Msg(TEXT("Could not run the DirectShow graph!  hr=0x%x"), hr);
        return hr;
    }


    return S_OK;


}

// CheckMovieStatus: If the movie has ended, rewind to beginning
void CVideoRenderer::CheckMovieStatus( void )
{

    long lEventCode;
    LONG_PTR lParam1, lParam2;
    HRESULT hr;

    if (!pMediaEvent)
        return;

    // Check for completion events
    hr = pMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0 );
    if (SUCCEEDED(hr))
    {
        // If we have reached the end of the media file, reset to beginning
        if (EC_COMPLETE == lEventCode)
        {
            hr = pMediaPos->put_CurrentPosition(0);
        }

        // Free any memory associated with this event
        hr = pMediaEvent ->FreeEventParams(lEventCode, lParam1, lParam2);
    }

}

// CleanupDShow
void CVideoRenderer::CleanupDShow( void )
{

	#ifdef REGISTER_FILTERGRAPH
    // Pull graph from Running Object Table (Debug)
    RemoveFromROT();
	#endif

    // Shut down the graph
    if (!(!pMediaControl)) pMediaControl->Stop( );

    if (!(!pMediaControl)) pMediaControl.Release( );
    if (!(!pMediaEvent)) pMediaEvent.Release( );
    if (!(!pMediaPos)) pMediaPos.Release( );
    if (!(!pGraphBuilder)) pGraphBuilder.Release( );
	if (!(!pRenderer)) pRenderer.Release( );


}

// CVideoRenderer Release Class All
void CVideoRenderer::ReleaseAll( void )
{

	// Release CVideoRenderer All
	for( int index = 0; index < VIDEORENDERER_MAX; index ++ ){
		if( pVideoRenderer[ index ] ){

			pVideoRenderer[ index ] ->CleanupDShow( );
			SAFE_DELETE( pVideoRenderer[ index ] )
		}
	}

	// Com Release
	CoUninitialize();

}

// CVideoRenderer Release Class All
void CVideoRenderer::CheckStatusAll( void )
{

	// Release CVideoRenderer All
	for( int index = 0; index < VIDEORENDERER_MAX; index ++ ){
		if( pVideoRenderer[ index ] ){

			pVideoRenderer[ index ] ->CheckMovieStatus( );
		}
	}

}
