//******************************************************************************
//
// OGL Utility / OGLRenderer
//
// レンダラクラス
//
// Copyright (C) 2010-2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#include "YNBaseLib.h"
#include "OGLRenderer.h"
#include <new>


//******************************************************************************
// コンストラクタ
//******************************************************************************
OGLRenderer::OGLRenderer()
{
	m_pOGLDevice = NULL;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
OGLRenderer::~OGLRenderer()
{
	Terminate();
}

//******************************************************************************
// 初期化
//******************************************************************************
int OGLRenderer::Initialize(
		MTKView* pView,
		OGLRedererParam rendererParam
	)
{
	int result = 0;
	NSRect baseRect;
	OGLVIEWPORT viewPort;
	
	//ディスプレイデバイス作成
	try {
		m_pOGLDevice = new OGLDevice();
	}
	catch (std::bad_alloc) {
		result = YN_SET_ERR(@"Could not allocate memory.", 0, 0);
		goto EXIT;
	}
	
	//ディスプレイデバイス初期化
	result = m_pOGLDevice->Initialize(pView.device);
	if (result != 0) goto EXIT;
	
	//セマフォ生成：複数同時実行を無効とする
	m_DisplaySemaphore = dispatch_semaphore_create(1);
	
	//ビューポート情報作成
	baseRect = [pView convertRectToBacking:[pView bounds]]; //retina対応
	viewPort.originx = baseRect.origin.x;
	viewPort.originy = baseRect.origin.y;
	viewPort.width   = baseRect.size.width;
	viewPort.height  = baseRect.size.height;
	m_pOGLDevice->SetViewPort(viewPort);
	
	//ビュープロパティ設定
	pView.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
	pView.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
	pView.clearColor = MTLClearColorMake(0, 0, 0, 1);
	pView.clearDepth = 1.0;
	
	//サンプル数設定確認
	m_pOGLDevice->SetSampleCount(pView.sampleCount);
	
	//コマンドキュー生成
	m_CommandQueue = [pView.device newCommandQueue];
	
EXIT:;
	return result;
}

//******************************************************************************
// ビューポートリセット
//******************************************************************************
int OGLRenderer::ResetViewPort(NSView* pView)
{
	int result = 0;
	NSRect baseRect;
	OGLVIEWPORT viewPort;
	
	baseRect = [pView convertRectToBacking:[pView bounds]]; //retina対応
	viewPort.originx = baseRect.origin.x;
	viewPort.originy = baseRect.origin.y;
	viewPort.width   = baseRect.size.width;
	viewPort.height  = baseRect.size.height;
	m_pOGLDevice->SetViewPort(viewPort);
	
	//NSLog(@"ResetViewPort viewPort.width %f", viewPort.width);
	//NSLog(@"ResetViewPort viewPort.height %f", viewPort.height);
	
	return result;
}

//******************************************************************************
// デバイス取得
//******************************************************************************
OGLDevice* OGLRenderer::GetDevice()
{
	return m_pOGLDevice;
}

//*****************************************************************************
// シーン初期化
//******************************************************************************
int OGLRenderer::InitScene()
{
	int result = 0;
	
	//制御情報初期化
	m_pOGLDevice->InitUniforms();
	
EXIT:;
	return result;
}

//*****************************************************************************
// シーン描画
//******************************************************************************
int OGLRenderer::RenderScene(
		MTKView* pView,
		OGLScene* pScene
	)
{
	int result = 0;
	id <MTLCommandBuffer> commandBuffer = nil;
	id <MTLRenderCommandEncoder> commandEncoder = nil;
	MTLRenderPassDescriptor* renderPassDescriptor = nil;
	OGLCOLOR bgcolor;
	__block dispatch_semaphore_t block_semaphore;
	
	//NSLog(@"RenderScene start");
	
	if (pScene == NULL) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//バッファ初期化色の指定：RGBA
	bgcolor = pScene->GetBGColor();
	pView.clearColor = MTLClearColorMake(bgcolor.r, bgcolor.g, bgcolor.b, bgcolor.a);
	
	//セマフォ割り当て待機
	dispatch_semaphore_wait(m_DisplaySemaphore, DISPATCH_TIME_FOREVER);
	
	//コマンドバッファ生成
	commandBuffer = [m_CommandQueue commandBuffer];
	commandBuffer.label = @"OGLRenderer_CommandBuffer";
	
	//コマンドバッファに完了時のイベントハンドラを登録
	block_semaphore = m_DisplaySemaphore;
	[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
		dispatch_semaphore_signal(block_semaphore);
	}];
	
	//ドロウアブル取得
	renderPassDescriptor = pView.currentRenderPassDescriptor;
	if ((renderPassDescriptor != nil)) {
		//コマンドバッファからレンダーエンコーダを取得
		commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
		commandEncoder.label = @"OGLRenderer_CommandEncoder";
		[commandEncoder pushDebugGroup:@"OGLRenderer_RenderScene"];
		m_pOGLDevice->SetCurrentCommandEncoder(commandEncoder);
		
		//描画
		result = pScene->Draw(m_pOGLDevice);
		if (result != 0) {
			//失敗しても描画定型処理を続行する
			YN_SHOW_ERR();
		}
		
		//エンコード終了
		m_pOGLDevice->SetCurrentCommandEncoder(nil);
		[commandEncoder popDebugGroup];
		[commandEncoder endEncoding];
		
		//フレームへの描画を依頼
		[commandBuffer presentDrawable:pView.currentDrawable];
	}
	
	//コマンドバッファコミット
	[commandBuffer commit];
	
EXIT:;
	//NSLog(@"RenderScene end");
	return result;
}

//*****************************************************************************
// 終了処理
//******************************************************************************
void OGLRenderer::Terminate()
{
	if (m_pOGLDevice != NULL) {
		m_pOGLDevice->Release();
		m_pOGLDevice = NULL;
	}
}

