/**
 * Live2D SDK for DirectX 
 *
 *  You can modify and use this source freely
 *  only for the development of application related Live2D.
 *
 *  (c) Live2D Inc. All rights reserved.
 */

#include <windows.h>
#include <d3d9.h>
#include "Live2D.h"
#include "Live2DModelD3D.h"
#include "util/UtSystem.h"
 

/***********************************************************
	必要なライブラリをリンクする
************************************************************/
#pragma comment( lib, "d3d9.lib" )
#pragma comment( lib, "d3dx9d.lib" )

/***********************************************************
	グローバル変数(アプリケーション関連)
************************************************************/

//#define		USE_LIVE2D						1
#define		ENABLE_CLIPPING_FEATURE			1		// Cubism SDK2.1クリッピング機能の有効化フラグ

LPDIRECT3D9				g_pDirect3D = NULL;
LPDIRECT3DDEVICE9		g_pDirect3D_Device = NULL;
D3DPRESENT_PARAMETERS	g_D3DPP;				//  D3DDeviceの設定
LPDIRECT3DTEXTURE9		g_pMaskTexture       = NULL; //  クリッピングマスク用のテクスチャ
LPDIRECT3DSURFACE9		g_pMaskBufferSurface = NULL; //  クリッピングマスク用のサーフェース

#define FAILED(hr) (((HRESULT)(hr)) < 0)
#define SAFE_RELEASE(x)  { if(x) { (x)->Release(); (x)=NULL; } }

live2d::Live2DModelD3D*	live2DModel ;


LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
VOID RenderLive2D();
HRESULT SetupClippingMask();
VOID CleanupClippingMask();

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine,
                   int nShow)
{
	MSG msg;
	
	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_VREDRAW|CS_HREDRAW|CS_OWNDC, 
					WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), 
					NULL, "DX9_TUTORIAL1_CLASS", NULL};

	RegisterClassEx(&wc);
	
	// ウィンドウ作成
	HWND hMainWnd = CreateWindow("DX9_TUTORIAL1_CLASS",
								"Live2D SDK Simple", 
								WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 800,
								NULL, NULL, hInstance, NULL);
	
	// DirectXの初期化
	g_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
	memset(&g_D3DPP, 0, sizeof(D3DPRESENT_PARAMETERS));
	
	g_D3DPP.Windowed = TRUE;
	g_D3DPP.SwapEffect = D3DSWAPEFFECT_DISCARD;
	
	g_pDirect3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hMainWnd,
							D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3DPP,
							&g_pDirect3D_Device);

	// クリッピングモデル向けに設定
	SetupClippingMask();

	//  ビューポートの設定
	D3DVIEWPORT9 vp;
	vp.X		= 0;
	vp.Y		= 0;
	vp.Width	= 800;
	vp.Height	= 800;
	vp.MinZ		= 0.0f;
	vp.MaxZ		= 1.0f;
	g_pDirect3D_Device->SetViewport(&vp);


	// ウィンドウの表示
	ShowWindow(hMainWnd, nShow);
	UpdateWindow(hMainWnd);


	
	// Live2Dの初期化
	live2d::Live2D::init();

	// デバイスの設定(モデル間で共有される)
	live2d::Live2DModelD3D::setDevice(g_pDirect3D_Device);

#if ENABLE_CLIPPING_FEATURE
	// クリッピングマスクの設定(モデル間で共有される)
	live2d::Live2DModelD3D::setMaskTexture(g_pMaskTexture);
	live2d::Live2DModelD3D::setMaskSurface(g_pMaskBufferSurface);
#endif	

	const LPCWSTR TEXTURES[] = { 
		L"res\\epsilon\\Epsilon2.1.2048\\texture_00.png",
		NULL ,
	} ;


	// モデルのロード
	live2DModel = live2d::Live2DModelD3D::loadModel("res\\epsilon\\Epsilon2.1.moc") ; // Cubism SDK2.1モデル

	// テクスチャのロード
	for( int i = 0 ; i < 1000 ; i++ ){
		if( ! TEXTURES[i] ) break ;

		LPDIRECT3DTEXTURE9 tex ;
		
		// テクスチャ画像をDirextXでの表示用に変換
		if( FAILED( D3DXCreateTextureFromFileExW( g_pDirect3D_Device
					, TEXTURES[i]
					, 0	//width 
					, 0	//height
					, 0	//mipmap //( 0なら完全なミップマップチェーン）
					, 0	//Usage
					, D3DFMT_A8R8G8B8                
					, D3DPOOL_MANAGED
					, D3DX_FILTER_LINEAR
					, D3DX_FILTER_BOX
					, 0			  
					, NULL
					, NULL
					, &tex ) ) )
		{
			live2d::UtDebug::print( "Could not create texture" );
			return FALSE ;
		}
		else
		{
			// 変換が成功したらセット
			live2DModel->setTexture( i , tex ) ;
		}
	}

	// メッセージ・ループ
	do
	{

		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			// アイドル処理
			RenderLive2D();
		}
	} while (msg.message != WM_QUIT);

	CleanupClippingMask();
	g_pDirect3D_Device->Release();
	g_pDirect3D->Release();
	delete live2DModel;
	live2d::Live2D::dispose();
	return(0);
}
 

LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return(0);
		
	case WM_PAINT:
		RenderLive2D();
		return(0);
	}
	
	return(DefWindowProc(hwnd, msg, wParam, lParam));
}


// モデルの描画
VOID RenderLive2D()
{
	// モデルが初期化されていなければ何もしない
	if(live2DModel == NULL) return;


	g_pDirect3D_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0);
	g_pDirect3D_Device->BeginScene();
	
	
	// Set up world matrix
	D3DXMATRIXA16 matWorld;
	D3DXMatrixIdentity( &matWorld );

	D3DXMATRIX Ortho2D;     
	D3DXMATRIX Identity;
	
	int w , h ;
	w = 800 ;
	h = 800 ;
	
	float modelWidth = live2DModel->getModelImpl()->getCanvasWidth() ;
	float modelHeight = live2DModel->getModelImpl()->getCanvasHeight() ;

	D3DXMatrixOrthoLH(&Ortho2D, modelHeight ,- modelHeight *h/w, -1.0f, 1.0f);
	D3DXMatrixIdentity(&Identity);
	
	g_pDirect3D_Device->SetTransform(D3DTS_PROJECTION, &Ortho2D);
	g_pDirect3D_Device->SetTransform(D3DTS_WORLD, &Identity);
	g_pDirect3D_Device->SetTransform(D3DTS_VIEW , &Identity);
	
	
	// --- 座標変換
	// 現在のLive2Dモデルは、画像のように下向きに(0,0,w,h)に配置される
	// センタリングと上下逆転が必要
	D3DXMATRIXA16 world, scale, trans ;
	g_pDirect3D_Device->GetTransform(D3DTS_WORLD, &world);
	D3DXMatrixTranslation(&trans , -modelWidth/2, -modelHeight/2, 0) ;
	world = trans *world;

	g_pDirect3D_Device->SetTransform(D3DTS_WORLD, &world);

	// --- モデルのパラメータを直接設定するサンプル
	double t = (live2d::UtSystem::getUserTimeMSec()/1000.0) * 2 * 3.14  ;// 1秒ごとに2π(1周期)増える
	double cycle=3.0;// パラメータが一周する時間(秒)
	double value=sin( t/cycle );// -1から1の間を周期ごとに変化する
	live2DModel->setParamFloat( "PARAM_ANGLE_X" , (float) (30 * value) ) ;// PARAM_ANGLE_Xのパラメータが[cycle]秒ごとに-30から30まで変化する
	live2DModel->setParamFloat("PARAM_EYE_L_OPEN",(float)(1.0*sin(t/3)));
	live2DModel->setParamFloat("PARAM_EYE_R_OPEN",(float)(1.0*sin(t/3)));

	// --- 描画
	live2DModel->update() ;
	live2DModel->draw() ;
	
	
	g_pDirect3D_Device->EndScene();
	g_pDirect3D_Device->Present(NULL, NULL, NULL, NULL);

	return;
}

/************************************************************
	Setup Clipping Mask
************************************************************/
HRESULT SetupClippingMask()
{
#if ENABLE_CLIPPING_FEATURE
	// バックバッファと同じサイズでかつアルファ値のみのテクスチャを作成する
	HRESULT hr = D3DXCreateTexture(g_pDirect3D_Device, g_D3DPP.BackBufferWidth, g_D3DPP.BackBufferHeight, 
		D3DX_DEFAULT, D3DUSAGE_RENDERTARGET, D3DFMT_A8, D3DPOOL_DEFAULT, &g_pMaskTexture);
	if (FAILED(hr))
	{
		live2d::UtDebug::print( "SetupClippingMask CreateMaskTexture failed" );
		return hr;
	}

	hr = g_pMaskTexture->GetSurfaceLevel(0, &g_pMaskBufferSurface);
	if(FAILED(hr))
	{
		live2d::UtDebug::print( "SetupClippingMask CreateMaskTextureSurface failed" );
		return hr;
	}
#endif
	return S_OK;
}

/************************************************************
	Cleanup Clipping Mask
************************************************************/
VOID CleanupClippingMask()
{
#if ENABLE_CLIPPING_FEATURE
	SAFE_RELEASE(g_pMaskBufferSurface);
	SAFE_RELEASE(g_pMaskTexture);	
#endif
}
