#include "Mix/Tool/Win32/Core/Graphics/SwapChain.h"
#include "Mix/Tool/Win32/Core/Graphics/Manager.h"
#include "Mix/Tool/Win32/Core/Graphics/BackBuffer.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

SwapChain* SwapChain::CreateInstance( HWND hWnd )
{
	return new SwapChain( hWnd );
}

SwapChain::SwapChain( HWND hWnd ) :
m_pD3DSwapChain( NULL ),
m_bDevice( false ),
m_pBackBuffer( Mix::Tool::Win32::Graphics::BackBuffer::CreateInstance() )
{
	::ZeroMemory( &m_D3DPresentParam, sizeof( m_D3DPresentParam ) );
	::ZeroMemory( &m_D3DViewport, sizeof( m_D3DViewport ) );
	::ZeroMemory( &m_SrcRect, sizeof( m_SrcRect ) );
	::ZeroMemory( &m_DstRect, sizeof( m_DstRect ) );

	m_D3DPresentParam.hDeviceWindow = hWnd;
}

SwapChain::~SwapChain( void )
{
	MIX_RELEASE( m_pBackBuffer );
	MIX_RELEASE( m_pD3DSwapChain );
}

bool SwapChain::IsActive( void ) const
{
	if( m_pD3DSwapChain == NULL )
	{
		return false;
	}

	WINDOWPLACEMENT wndpl;

	if( ::GetWindowPlacement( m_D3DPresentParam.hDeviceWindow, &wndpl ) == TRUE )
	{
		if( ( wndpl.showCmd == SW_HIDE ) ||
			( wndpl.showCmd == SW_MINIMIZE ) ||
			( wndpl.showCmd == SW_SHOWMINIMIZED ) ||
			( wndpl.showCmd == SW_SHOWMINNOACTIVE ) )
		{
			return false;;
		}
	}
	else
	{
		return false;
	}

	return true;
}

bool SwapChain::Begin( void )
{
	if( m_pD3DSwapChain == NULL )
	{
		return false;
	}

	IDirect3DDevice9* pD3DDevice;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 転送矩形を求める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	::GetClientRect( m_D3DPresentParam.hDeviceWindow, &m_DstRect );

	m_SrcRect.left = ( static_cast<int>( m_D3DViewport.Width ) - ( m_DstRect.right - m_DstRect.left ) ) / 2;
	m_SrcRect.top = ( static_cast<int>( m_D3DViewport.Height ) - ( m_DstRect.bottom - m_DstRect.top ) ) / 2;
	m_SrcRect.right = ( m_SrcRect.left + ( m_DstRect.right - m_DstRect.left ) );
	m_SrcRect.bottom = ( m_SrcRect.top + ( m_DstRect.bottom - m_DstRect.top ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバイスを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pD3DSwapChain->GetDevice( &pD3DDevice ) != D3D_OK )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pD3DDevice->BeginScene() != D3D_OK )
	{
		MIX_RELEASE( pD3DDevice );
		return false;
	}

	MIX_RELEASE( pD3DDevice );

	return true;
}

void SwapChain::End( void )
{
	if( m_pD3DSwapChain == NULL )
	{
		return;
	}

	IDirect3DDevice9* pD3DDevice;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバイスを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pD3DSwapChain->GetDevice( &pD3DDevice ) != D3D_OK )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 描画終了
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pD3DDevice->EndScene();

	MIX_RELEASE( pD3DDevice );
}

bool SwapChain::Present( void )
{
	if( m_pD3DSwapChain == NULL )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マネージャを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Tool::Win32::Graphics::Manager* pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// プレゼント♪
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pD3DSwapChain->Present( &m_SrcRect, &m_DstRect, NULL, NULL, NULL ) != D3D_OK )
	{
		//マネージャにデバイスがロストしていることを伝える
		pManager->NotifyDeviceLost();
		return false;
	}

	return true;
}

HWND SwapChain::GetWindowHandle( void ) const
{
	return m_D3DPresentParam.hDeviceWindow;
}

const D3DVIEWPORT9& SwapChain::GetViewport( void ) const
{
	return m_D3DViewport;
}

const RECT& SwapChain::GetBounds( void ) const
{
	return m_SrcRect;
}

Mix::Tool::Win32::Graphics::Texture* SwapChain::GetBackBufferPtr( void )
{
	return m_pBackBuffer;
}

bool SwapChain::IsDevice( void ) const
{
	return m_bDevice;
}

void SwapChain::OnInvalidate( void )
{
	MIX_RELEASE( m_pD3DSwapChain );
}

void SwapChain::OnValidate( void )
{
	Mix::Tool::Win32::Graphics::Manager* pManager = Manager::GetInstance();
	if( pManager == NULL )
	{
		return;
	}

	m_pD3DSwapChain = pManager->D3D_CreateSwapChain( m_D3DPresentParam.hDeviceWindow, m_D3DPresentParam, m_bDevice );
	if( m_pD3DSwapChain == NULL )
	{
		return;
	}

	UpdateParam();
}

void SwapChain::UpdateParam( void )
{
	IDirect3DSurface9* pD3DBackBuffer = NULL;

	m_D3DViewport.X = 0;
	m_D3DViewport.Y = 0;
	m_D3DViewport.Width = m_D3DPresentParam.BackBufferWidth;
	m_D3DViewport.Height = m_D3DPresentParam.BackBufferHeight;
	m_D3DViewport.MinZ = 0.0f;
	m_D3DViewport.MaxZ = 1.0f;

	::GetClientRect( m_D3DPresentParam.hDeviceWindow, &m_DstRect );

	m_SrcRect.left = ( static_cast<int>( m_D3DViewport.Width ) - ( m_DstRect.right - m_DstRect.left ) ) / 2;
	m_SrcRect.top = ( static_cast<int>( m_D3DViewport.Height ) - ( m_DstRect.bottom - m_DstRect.top ) ) / 2;
	m_SrcRect.right = ( m_SrcRect.left + ( m_DstRect.right - m_DstRect.left ) );
	m_SrcRect.bottom = ( m_SrcRect.top + ( m_DstRect.bottom - m_DstRect.top ) );

	m_pD3DSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pD3DBackBuffer );
	m_pBackBuffer->SetSurface( pD3DBackBuffer );
	MIX_RELEASE( pD3DBackBuffer );
}

Mix::Tool::Win32::Object::TYPE SwapChain::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__SWAPCHAIN;
}

}}}}
