//******************************************************************************
//
// MIDITrail / MTBackgroundImage
//
// 背景画像描画クラス
//
// Copyright (C) 2017-2021 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#include "YNBaseLib.h"
#include "MTParam.h"
#include "MTConfFile.h"
#include "MTBackgroundImage.h"
#include "MTSecurityScopedBookmark.h"


//******************************************************************************
// コンストラクタ
//******************************************************************************
MTBackgroundImage::MTBackgroundImage(void)
{
	m_pView = nil;
	m_isEnable = true;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
MTBackgroundImage::~MTBackgroundImage(void)
{
	Release();
}

//******************************************************************************
// 背景画像生成
//******************************************************************************
int MTBackgroundImage::Create(
		OGLDevice* pOGLDevice,
		NSView* pView
	)
{
	int result = 0;
	unsigned int vertexNum = 0;
	unsigned int indexNum = 0;
	MTBACKGROUNDIMAGE_VERTEX* pVertex = NULL;
	unsigned int* pIndex = NULL;
	
	Release();
	
	m_pView = pView;
	m_ViewRect = [pView bounds];
	
	//テクスチャ読み込み
	result = _LoadTexture(pOGLDevice);
	if (result != 0) goto EXIT;
	
	//テクスチャを生成しなかった場合は何もしない
	if (m_Texture.GetWidth() == 0) goto EXIT;
	
	//プリミティブ初期化
	result = m_Primitive.Initialize(
					sizeof(MTBACKGROUNDIMAGE_VERTEX),	//頂点サイズ
					_GetFVFFormat(),			//頂点FVFフォーマット
					GL_TRIANGLE_STRIP			//プリミティブ種別
				);
	if (result != 0) goto EXIT;
	
	//頂点バッファ生成
	vertexNum = 4;
	result = m_Primitive.CreateVertexBuffer(pOGLDevice, vertexNum);
	if (result != 0) goto EXIT;
	
	//インデックスバッファ生成
	indexNum = 4;
	result = m_Primitive.CreateIndexBuffer(pOGLDevice, indexNum);
	if (result != 0) goto EXIT;
	
	//バッファのロック
	result = m_Primitive.LockVertex((void**)&pVertex);
	if (result != 0) goto EXIT;
	result = m_Primitive.LockIndex(&pIndex);
	if (result != 0) goto EXIT;
	
	//バッファに頂点とインデックスを書き込む
	result = _CreateVertexOfBackground(
					pVertex,		//頂点バッファ書き込み位置
					pIndex			//インデックスバッファ書き込み位置
				);
	if (result != 0) goto EXIT;
	
	//バッファのロック解除
	result = m_Primitive.UnlockVertex();
	if (result != 0) goto EXIT;
	result = m_Primitive.UnlockIndex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 描画
//******************************************************************************
int MTBackgroundImage::Draw(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	GLboolean isEnableDepthTest = false;
	OGLTransMatrix transMatrix;
	OGLVIEWPORT viewPort;
	GLint depthFunc = 0;
	NSRect rect;
	
	if (!m_isEnable) goto EXIT;
	if (m_Texture.GetWidth() == 0) goto EXIT;
	
	//ウィンドウサイズに変化が生じた場合は背景画像頂点をリセットする
	rect = [m_pView bounds];
	if ((m_ViewRect.size.width != rect.size.width) || (m_ViewRect.size.height != rect.size.height)) {
		m_ViewRect = [m_pView bounds];
		result = _RestVertexOfBackground();
		if (result != 0) goto EXIT;
	}
	
	//Zバッファを一時的に無効化する
	glGetBooleanv(GL_DEPTH_TEST, &isEnableDepthTest);
	glDisable(GL_DEPTH_TEST);
	
	//テクスチャステージ設定
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
	//  カラー演算：引数1を使用  引数1：テクスチャ
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
	// アルファ演算：引数1を使用  引数1：テクスチャ
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
	glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);
	
	//テクスチャフィルタ
	//OGLTexture::BindTexture()で対応
	
	//正射影
	pOGLDevice->GetViewPort(&viewPort);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, viewPort.width, viewPort.height, 0, -1, 1);
	
	//深度テストを無効化
	glGetIntegerv(GL_DEPTH_FUNC, &depthFunc);
	glDepthFunc(GL_ALWAYS);
	
	//描画
	result = m_Primitive.Draw(pOGLDevice, &m_Texture);
	if (result != 0) goto EXIT;
	
	//深度テストを戻す
	glDepthFunc(depthFunc);
	
	//Zバッファ有効化状態を戻す
	if (isEnableDepthTest) {
		glEnable(GL_DEPTH_TEST);
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 解放
//******************************************************************************
void MTBackgroundImage::Release()
{
	m_Primitive.Release();
}

//******************************************************************************
// 背景画像頂点生成
//******************************************************************************
int MTBackgroundImage::_CreateVertexOfBackground(
		MTBACKGROUNDIMAGE_VERTEX* pVertex,
		unsigned int* pIndex
	)
{
	int result = 0;
	CGRect rect;
	unsigned int i = 0;
	unsigned int cw = 0;
	unsigned int ch = 0;
	float ratio_cwh = 0.0f;
	float ratio_iwh = 0.0f;
	float x1 = 0.0f;
	float x2 = 0.0f;
	float y1 = 0.0f;
	float y2 = 0.0f;
	
	//クライアント領域のサイズを取得
	rect = [m_pView bounds];
	cw = rect.size.width * [[NSScreen mainScreen] backingScaleFactor]; //retina対応
	ch = rect.size.height * [[NSScreen mainScreen] backingScaleFactor]; //retina対応
	
	ratio_cwh = (float)cw / (float)ch;
	ratio_iwh = (float)m_Texture.GetWidth() / (float)m_Texture.GetHeight();
	
	// クライアント領域より画像の方が横長の場合
	//     |----- cw -----|
	//  ---0--------------+-- +x
	//   | |              |
	//   | +--------------+
	//  ch |    image     |
	//   | +--------------+
	//   | |              |
	//  ---+--------------+
	//     |
	//    +y
	if (ratio_cwh < ratio_iwh) {
		x1 = 0.0f;
		x2 = (float)cw;
		y1 = ((float)ch - ((float)cw / ratio_iwh)) / 2.0f;
		y2 = (float)ch - y1;
	}
	// クライアント領域より画像の方が縦長の場合
	//     |----- cw -----|
	//  ---0--+--------+--+-- +x
	//   | |  |        |  |
	//   | |  |        |  |
	//  ch |  | image  |  |
	//   | |  |        |  |
	//   | |  |        |  |
	//  ---+--+--------+--+
	//     |
	//    +y
	else {
		x1 = ((float)cw - ((float)ch * ratio_iwh)) / 2.0f;
		x2 = (float)cw - x1;
		y1 = 0.0f;
		y2 = (float)ch;
	}
	
	//頂点座標
	pVertex[0].p = OGLVECTOR3(x1, y1, 0.0f);
	pVertex[1].p = OGLVECTOR3(x2, y1, 0.0f);
	pVertex[2].p = OGLVECTOR3(x1, y2, 0.0f);
	pVertex[3].p = OGLVECTOR3(x2, y2, 0.0f);
	
	//各頂点のディフューズ色
	for (i = 0; i < 4; i++) {
		//各頂点のディフューズ色
		pVertex[i].c = OGLCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	}
	
	//各頂点のテクスチャ座標
	pVertex[0].t = OGLVECTOR2(0.0f, 0.0f);
	pVertex[1].t = OGLVECTOR2(1.0f, 0.0f);
	pVertex[2].t = OGLVECTOR2(0.0f, 1.0f);
	pVertex[3].t = OGLVECTOR2(1.0f, 1.0f);
	
	//インデックス：TRIANGLESTRIP
	pIndex[0] = 0;
	pIndex[1] = 1;
	pIndex[2] = 2;
	pIndex[3] = 3;
	
EXIT:;
	return result;
}

//******************************************************************************
// テクスチャ画像読み込み
//******************************************************************************
int MTBackgroundImage::_LoadTexture(
		OGLDevice* pOGLDevice
	)
{
	int result = 0;
	YNUserConf* pUserConf = nil;
	NSString* pImgFilePath = nil;
	NSFileManager* pFileManager = nil;
	NSURL* pImageFileURL = nil;
	BOOL isAccessingResource = NO;
	BOOL isStale = NO;
	
	//ユーザ設定初期化
	pUserConf = [[YNUserConf alloc] init];
	if (pUserConf == nil) {
		result = YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
	//ビットマップファイル名
	[pUserConf setCategory:MT_CONF_CATEGORY_GRAPHIC];
	[pUserConf setSection:MT_CONF_SECTION_BGIMG];
	pImgFilePath = [pUserConf strValueForKey:@"ImageFilePath" defaultValue:@""];
	
	//ファイル未指定なら何もしない
	if ([pImgFilePath length] == 0) goto EXIT;
	
	//ファイルが存在しない場合は何もしない
	pFileManager = [NSFileManager defaultManager];
	if (![pFileManager fileExistsAtPath:pImgFilePath]) goto EXIT;
	
	//Security Scoped ブックマーク取得
	result = MTSecurityScopedBookmark::GetBookmark(
					&pImageFileURL,
					MT_CONF_SSBM_BGIMG_IMGFILEURL,
					&isStale
				);
	if (result != 0) goto EXIT;
	
	//ブックマークした後で別のファイルで差し替えられた場合
	if (isStale) {
		//特になにもしない
	}
	
	//リソースアクセス開始
	result = MTSecurityScopedBookmark::StartAccessingResource(pImageFileURL);
	if (result != 0) goto EXIT;
	isAccessingResource = YES;
	
	//テクスチャ画像として読み込み
	result = m_Texture.LoadImageFile(pImgFilePath);
	if (result != 0) goto EXIT;
	
EXIT:;
	//リソースアクセス終了
	if (isAccessingResource) {
		MTSecurityScopedBookmark::StopAccessingResource(pImageFileURL);
	}
	[pUserConf release];
	return result;
}

//******************************************************************************
// 表示設定
//******************************************************************************
void MTBackgroundImage::SetEnable(
		bool isEnable
	)
{
	m_isEnable = isEnable;
}

//******************************************************************************
// 背景画像頂点リセット
//******************************************************************************
int MTBackgroundImage::_RestVertexOfBackground()
{
	int result = 0;
	MTBACKGROUNDIMAGE_VERTEX* pVertex = NULL;
	unsigned int* pIndex = NULL;
	
	//バッファのロック
	result = m_Primitive.LockVertex((void**)&pVertex);
	if (result != 0) goto EXIT;
	result = m_Primitive.LockIndex(&pIndex);
	if (result != 0) goto EXIT;
	
	//バッファに頂点とインデックスを書き込む
	result = _CreateVertexOfBackground(
					pVertex,		//頂点バッファ書き込み位置
					pIndex			//インデックスバッファ書き込み位置
				);
	if (result != 0) goto EXIT;
	
	//バッファのロック解除
	result = m_Primitive.UnlockVertex();
	if (result != 0) goto EXIT;
	result = m_Primitive.UnlockIndex();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

