//******************************************************************************
//
// MIDITrail / MTMainViewCtrl
//
// メインビュー制御クラス
//
// Copyright (C) 2012-2021 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "YNBaseLib.h"
#import "MTMainViewCtrl.h"
#import "MTDeviceScreenInfo.h"


//******************************************************************************
// パラメータ定義
//******************************************************************************
//瞳孔間距離(Interpupillary distance) 単位mm
#define MT_VR_IPD_MM		(63.0)

//******************************************************************************
// プライベートメソッド定義
//******************************************************************************
@interface MTMainViewCtrl ()

// OpneGLコンテキスト初期化
- (int)initializeOpenGLContext;

// OpneGLコンテキスト破棄
- (void)terminateOpenGLContext;

//シーン描画
- (void)drawScene:(NSTimer*)timer;

//シーケンサメッセージ処理
- (int)sequencerMsgProc;

//描画処理
- (int)drawProc;

//演奏状態変更通知受信処理
- (int)postPlayStatus:(NSString*)pNotificationName;

//FPS更新
- (void)updateFPS:(uint64_t)startTime;

//タップ
- (void)onTapped;

//通知送信処理
- (int)postNotificationWithName:(NSString*)pName;

//全アイテム非表示
- (void)hideAllItems;

//ボタン表示状態更新
- (void)updateButtonStatus;

//タッチガイド画像表示状態更新
-(void)updateGuideImageStatus;

//デバイス方向更新
- (int)updateDeviceDirection;

//VR用パラメータ初期化
- (int)initializeVRParameters;

//リモートコマンドハンドラ設定
- (void)setRemoteCommandHandlerWithMonitor:(BOOL)isMonitor;

//ロック画面向け演奏情報更新：演奏時間
- (int)setNowPlayingInfoPlaybackTime:(unsigned int)playTimeSec;

@end


@implementation MTMainViewCtrl

//******************************************************************************
// 生成
//******************************************************************************
- (id)initWithNibName:(NSString *)nibNameOrNil
			   bundle:(NSBundle *)nibBundleOrNil
		rendererParam:(OGLRedererParam)rendererParam
{
	int result = 0;
	NSString* pDeviceType = nil;
	NSString* pNibNameHowToView = nil;
	NSString* pPathImgButtonPlayN = nil;
	NSString* pPathImgButtonPlayH = nil;
	NSString* pPathImgButtonPauseN = nil;
	NSString* pPathImgButtonPauseH = nil;
	NSNotificationCenter* pCenter = nil;
	
	//メンバ初期化
	m_ScreenWidth = 0;
	m_ScreenHeight = 0;
	m_ViewBounds =  CGRectMake(0, 0, 0, 0);
	m_pEAGLContext = nil;
	m_MSAAFrameBuffer = 0;
	m_DefaultFrameBuffer = 0;
	m_DefaultColorRenderBuffer = 0;
	m_DefaultDepthRenderBuffer = 0;
	m_MSAAColorRenderBuffer = 0;
	m_MSAADepthRenderBuffer = 0;
	m_Timer = nil;
	m_pScene = NULL;
	m_pMsgQueue = NULL;
	m_isStopScene = false;
	m_LastTouchTime = 0;
	m_PrevDrawTime = 0;
	m_FlameCount = 0.0f;
	m_PrevTouchPoint.x = 0;
	m_PrevTouchPoint.y = 0;
	m_TounchesTotalDistance = 0;
	m_isDisplayButtons = YES;
	m_isMonitor = false;
	m_ScreenType = MTScene::StandardView;
    m_isScreenLocked = false;
	m_isBackground = false;
	m_SkipBackTimeSpanInMsec = 10000;
	m_SkipForwardTimeSpanInMsec = 10000;
	m_isViewActionSheetVisible = false;
	
	//レンダリングパラメータ
	m_RendererParam = rendererParam;
	
	//バンドル初期化
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
		//OpenGLコンテキスト初期化
		result = [self initializeOpenGLContext];
		if (result != 0) YN_SHOW_ERR();
    }
	
	//デバイス種別
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
		//iPhone / iPod touch
		pDeviceType = @"iPhone";
    } else {
		//iPad
		pDeviceType = @"iPad";
    }
	
	//Nibファイル名称
	pNibNameHowToView = [NSString stringWithFormat:@"MTHowToView_%@", pDeviceType];
	
	//ビュー制御生成
	m_pHowToViewCtrl = [[MTHowToViewCtrl alloc] initWithNibName:pNibNameHowToView bundle:nil];
	
	//画像ファイルパス
	pPathImgButtonPlayN  = [NSString stringWithFormat:@"img/%@/Button-Play-N", pDeviceType];
	pPathImgButtonPlayH  = [NSString stringWithFormat:@"img/%@/Button-Play-H", pDeviceType];
	pPathImgButtonPauseN = [NSString stringWithFormat:@"img/%@/Button-Pause-N", pDeviceType];
	pPathImgButtonPauseH = [NSString stringWithFormat:@"img/%@/Button-Pause-H", pDeviceType];
	
	//画像読み込み
	m_pImgButtonPlayN  = [[UIImage imageNamed:pPathImgButtonPlayN] retain];
	m_pImgButtonPlayH  = [[UIImage imageNamed:pPathImgButtonPlayH] retain];
	m_pImgButtonPauseN = [[UIImage imageNamed:pPathImgButtonPauseN] retain];
	m_pImgButtonPauseH = [[UIImage imageNamed:pPathImgButtonPauseH] retain];
	
	//デバイスモーション管理
	m_pMotionManager = [[CMMotionManager alloc] init];
	
	//ゲームパッド制御初期化
	result = m_GamePadCtrl.Initialize(0);
	if (result != 0) goto EXIT;
	
	//ゲームパッド用視点番号
	m_GamePadViewPointNo = 0;
	
	//ゲームコントローラー接続/切断通知先登録
	pCenter = [NSNotificationCenter defaultCenter];
	[pCenter addObserver:self
				selector:@selector(onGameControllerDidConnect)
					name:GCControllerDidConnectNotification
				  object:nil];
	[pCenter addObserver:self
				selector:@selector(onGameControllerDidDisconnect)
					name:GCControllerDidDisconnectNotification
				  object:nil];
	
	//瞳孔間距離に対応するポイント数
	m_IPDPoints = 0;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return self;
}

//******************************************************************************
// 破棄
//******************************************************************************
- (void)dealloc
{
	[m_pImgButtonPlayN release];
	[m_pImgButtonPlayH release];
	[m_pImgButtonPauseN release];
	[m_pImgButtonPauseH release];
	
	//ビュー破棄
	[m_pHowToViewCtrl release];
	
	//OpenGLコンテキスト破棄
	[self terminateOpenGLContext];
	
	[super dealloc];
	
	return;
}

//******************************************************************************
// OpneGLコンテキスト初期化
//******************************************************************************
- (int)initializeOpenGLContext
{
	int result = 0;
	CAEAGLLayer* pEAGLLayer = NULL;
	CGRect frame;
	
	//解像度係数を設定：retinaの場合は2.0
	m_pBaseView.contentScaleFactor = [UIScreen mainScreen].scale;	//retina対応
	
	//現在のビューのサイズを確認する
	frame = self.view.frame;
	frame.origin = CGPointZero;
	m_ViewBounds = [self.view bounds];
	
	//CAEAGLレイヤー作成
	pEAGLLayer = [CAEAGLLayer layer];
	pEAGLLayer.frame = frame;
	pEAGLLayer.contentsScale = [UIScreen mainScreen].scale;	//retina対応
	
	//縦横サイズを覚えておく
	m_ScreenWidth = frame.size.width * [UIScreen mainScreen].scale;		//retina対応
	m_ScreenHeight = frame.size.height * [UIScreen mainScreen].scale;	//retina対応
	
	//CAEAGLレイヤー：不透明
	pEAGLLayer.opaque = YES;
	//CAEAGLレイヤー：プロパティ
	pEAGLLayer.drawableProperties =
					[NSDictionary dictionaryWithObjectsAndKeys:
						//描画結果保存：しない
						[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
						//描画カラーフォーマット：各色8bit
						kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
					nil];
	
	//EAGLコンテキスト生成：OpenGL ES 1.1
	m_pEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
	[EAGLContext setCurrentContext:m_pEAGLContext];
	
	//--------------------------------------------------
	// デフォルトのフレームバッファとレンダーバッファを作成
	//--------------------------------------------------
	//フレームバッファ作成
	glGenFramebuffersOES(1, &m_DefaultFrameBuffer);
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_DefaultFrameBuffer);
	
	//色レンダーバッファ作成
	glGenRenderbuffersOES(1, &m_DefaultColorRenderBuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_DefaultColorRenderBuffer);

	//EAGLコンテキストにストレージ割り当て
	[m_pEAGLContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:pEAGLLayer];
	
	//色レンダーバッファをフレームバッファにアタッチ
	glFramebufferRenderbufferOES(
						GL_FRAMEBUFFER_OES,
						GL_COLOR_ATTACHMENT0_OES,
						GL_RENDERBUFFER_OES,
						m_DefaultColorRenderBuffer);
	
	//深度レンダーバッファ作成
	glGenRenderbuffersOES(1, &m_DefaultDepthRenderBuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_DefaultDepthRenderBuffer);
	glRenderbufferStorageOES(
						GL_RENDERBUFFER_OES,
						GL_DEPTH_COMPONENT16_OES,
						m_ScreenWidth,
						m_ScreenHeight);
	
	//深度レンダーバッファをフレームバッファにつなぐ
	glFramebufferRenderbufferOES(
						GL_FRAMEBUFFER_OES,
						GL_DEPTH_ATTACHMENT_OES,
						GL_RENDERBUFFER_OES,
						m_DefaultDepthRenderBuffer);
	
	//--------------------------------------------------
	// MSAA用のフレームバッファとレンダーバッファを作成
	//--------------------------------------------------
	if (m_RendererParam.isEnableAntialiasing) {
		//フレームバッファ生成
		glGenFramebuffersOES(1, &m_MSAAFrameBuffer);
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_MSAAFrameBuffer);
		
		//色レンダーバッファ生成
		glGenRenderbuffersOES(1, &m_MSAAColorRenderBuffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_MSAAColorRenderBuffer);
		glRenderbufferStorageMultisampleAPPLE(
							GL_RENDERBUFFER_OES,
							m_RendererParam.sampleNum, //サンプル数
							GL_RGBA8_OES,
							m_ScreenWidth,
							m_ScreenHeight);
		
		//色レンダーバッファをフレームバッファにつなぐ
		glFramebufferRenderbufferOES(
							GL_FRAMEBUFFER_OES,
							GL_COLOR_ATTACHMENT0_OES,
							GL_RENDERBUFFER_OES,
							m_MSAAColorRenderBuffer);
		
		//深度レンダーバッファ作成
		glGenRenderbuffersOES(1, &m_MSAADepthRenderBuffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_MSAADepthRenderBuffer);
		glRenderbufferStorageMultisampleAPPLE(
							GL_RENDERBUFFER_OES,
							m_RendererParam.sampleNum, //サンプル数
							GL_DEPTH_COMPONENT16_OES,
							m_ScreenWidth,
							m_ScreenHeight);
		
		//深度レンダーバッファをフレームバッファにつなぐ
		glFramebufferRenderbufferOES(
							GL_FRAMEBUFFER_OES,
							GL_DEPTH_ATTACHMENT_OES,
							GL_RENDERBUFFER_OES,
							m_MSAADepthRenderBuffer);
	}
	
	//CAEAGLレイヤーをビューに追加
	[m_pBaseView.layer addSublayer:pEAGLLayer];
	
	//ベースビューを最背面に移動
	//  xibで最背面に設定していても最前面に表示されてボタンが隠れる場合があるため移動を指示する
	[self.view sendSubviewToBack:m_pBaseView];
	
EXIT:;
	return result;
}

//******************************************************************************
// OpneGLコンテキスト破棄
//******************************************************************************
- (void)terminateOpenGLContext
{
	//フレームバッファ破棄
	glDeleteFramebuffersOES(1, &m_DefaultFrameBuffer);
	glDeleteFramebuffersOES(1, &m_MSAAFrameBuffer);
	
	//レンダリングバッファ破棄
	glDeleteRenderbuffersOES(1, &m_DefaultColorRenderBuffer);
	glDeleteRenderbuffersOES(1, &m_DefaultDepthRenderBuffer);
	glDeleteRenderbuffersOES(1, &m_MSAAColorRenderBuffer);
	glDeleteRenderbuffersOES(1, &m_MSAADepthRenderBuffer);
	
	//EAGLコンテキスト破棄
	[EAGLContext setCurrentContext:nil];
	[m_pEAGLContext release];
	
	return;
}

//******************************************************************************
// 初期化処理
//******************************************************************************
- (int)initialize:(SMMsgQueue*)pMsgQueue
{
	int result = 0;
	
	//メッセージキュー
	m_pMsgQueue = pMsgQueue;
		
	//レンダラ初期化
	result = m_Renderer.Initialize(m_pBaseView, m_RendererParam);
	if (result != 0) goto EXIT;
	
	//バッファクリア
	result = m_Renderer.RenderScene(NULL, false, 0);
	if (result != 0) goto EXIT;
	
	//時間初期化
	result = m_MachTime.Initialize();
	if (result != 0) goto EXIT;
	
	//モニタ状態
	m_isMonitor = NO;
	
	//全アイテム非表示
	[self hideAllItems];
	
	//VR用パラメータ初期化
	result = [self initializeVRParameters];
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 終了処理
//******************************************************************************
- (void)terminate
{	
	//レンダラ終了
	m_Renderer.Terminate();
	
	return;
}

//******************************************************************************
// ビュー境界取得
//******************************************************************************
- (CGRect)getViewBounds
{
	return m_ViewBounds;
}

//******************************************************************************
// デバイス取得
//******************************************************************************
- (OGLDevice*)getDevice
{
	return m_Renderer.GetDevice();
}

//******************************************************************************
// ビュー取得
//******************************************************************************
- (UIView*)getView
{
	return m_pBaseView;
}

//******************************************************************************
// ビュー登録完了
//******************************************************************************
- (void)viewDidLoad
{	
    [super viewDidLoad];
	// Do any additional setup after loading the view.
	
	return;
}

//******************************************************************************
// インターフェース自動回転確認
//******************************************************************************
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
	BOOL isRotate = NO;
	
	//横長のみに対応する
	if ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
	 || (interfaceOrientation == UIInterfaceOrientationLandscapeRight)) {
		isRotate = YES;
	}
	
	return isRotate;
}

//******************************************************************************
// インターフェース自動回転確認（iOS6以降）
//******************************************************************************
- (BOOL)shouldAutorotate
{
	//回転を許可する
	//NSLog(@"shouldAutorotate");
	return YES;
}

//******************************************************************************
// インターフェース自動回転確認：回転方向（iOS6以降）
//******************************************************************************
- (NSUInteger)supportedInterfaceOrientations
{
	//横長のみに対応する
	//NSLog(@"supportedInterfaceOrientations");
	return UIInterfaceOrientationMaskLandscape;
}

//******************************************************************************
// ビュー表示開始
//******************************************************************************
- (void)viewWillAppear:(BOOL)animated
{
	NSString* currSysVer;
	
	//最終タッチ時間
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	//前回描画時間
	m_PrevDrawTime = m_MachTime.GetCurTimeInNanosec();
	m_PrevFPSDrawTime = m_MachTime.GetCurTimeInNanosec();
	
	//描画更新処理間隔設定
	currSysVer = [[UIDevice currentDevice] systemVersion];
	if ([currSysVer compare:@"3.1" options:NSNumericSearch] != NSOrderedAscending) {
		//iOS3.1以降ならDisplayLinkによって描画処理を同期する
		//NSLog(@"Use DisplayLink");
		m_Timer = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self
																	selector:@selector(drawScene:)];
		[m_Timer addToRunLoop:[NSRunLoop currentRunLoop]
					  forMode:NSDefaultRunLoopMode];
	}
	else {
		//iOS3.1未満なら通常のタイマーを使って描画処理間隔を制御する
		//NSLog(@"Use Timer");
		m_Timer = [NSTimer timerWithTimeInterval:(1.0 / 60.0)
										  target:self
										selector:@selector(drawScene:)
										userInfo:nil
										 repeats:YES];
		[[NSRunLoop currentRunLoop] addTimer:m_Timer
									 forMode:NSDefaultRunLoopMode];
	}
	
	return;
}

//******************************************************************************
// ビュー表示終了
//******************************************************************************
- (void)viewDidDisappear:(BOOL)animated
{	
	//タイマー破棄
	[m_Timer invalidate];
	m_Timer = nil;
	
	return;
}

//******************************************************************************
// シーン開始
//******************************************************************************
- (int)startScene:(MTScene*)pScene
		isMonitor:(BOOL)isMonitor
{
	int result = 0;
	
	//シーンオブジェクト
	m_pScene = pScene;
	
	//ダミーのクリックイベントを送ることでタッチ操作を有効化する
	m_pScene->OnWindowClicked(WM_LBUTTONDOWN, 0, 0);
	
	//スクリーン種別設定
	m_pScene->SetScreenType(m_ScreenType);
	
    //ロック状態設定
    m_pScene->LockScreen(m_isScreenLocked);
    
	//モニタフラグ
	m_isMonitor = isMonitor;
	
	//シーンメッセージキューをクリア
	m_SceneMsgQueue.Clear();
	
	//初回描画処理
	result = [self drawProc];
	if (result != 0) goto EXIT;
	
	//最終タッチ時間
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	//VRビューの場合はモーションセンサ利用開始
	if (m_pMotionManager.deviceMotionAvailable) {
		if ((m_ScreenType == MTScene::VRViewSingleLens)
		 || (m_ScreenType == MTScene::VRViewDualLens)) {
			[m_pMotionManager startDeviceMotionUpdates];
		}
	}
	
	//ゲームコントローラー初期化
	[self initializeGameController];
	
	//リモートコマンドハンドラ設定
	[self setRemoteCommandHandlerWithMonitor:isMonitor];
	
	//前回演奏経過時間
	m_PrevPlayTimeSec = 0;
	
EXIT:;
	return result;
}


//******************************************************************************
// シーン停止
//******************************************************************************
- (int)stopScene
{
	int result = 0;
	
	//シーンオブジェクト
	m_pScene = NULL;
	
	//モーションセンサ利用終了
	if (m_pMotionManager.deviceMotionAvailable) {
		[m_pMotionManager stopDeviceMotionUpdates];
	}
	
	return result;
}

//******************************************************************************
// シーン操作：演奏開始
//******************************************************************************
- (int)scene_PlayStart
{
	int result = 0;
	
	result = m_pScene->OnPlayStart();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// シーン操作：演奏終了
//******************************************************************************
- (int)scene_PlayEnd
{
	int result = 0;
	
	result = m_pScene->OnPlayEnd();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// シーン操作：巻き戻し
//******************************************************************************
- (int)scene_Rewind
{
	int result = 0;
	
	result = m_pScene->Rewind();
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// シーン操作：視点リセット
//******************************************************************************
- (int)scene_ResetViewpoint
{
	int result = 0;
	
	m_pScene->ResetViewpoint();
	
	return result;
}

//******************************************************************************
// シーン操作：静的視点移動
//******************************************************************************
- (int)scene_MoveToStaticViewpoint:(unsigned int)viewpointNo
{
	int result = 0;
	
	m_pScene->MoveToStaticViewpoint(viewpointNo);
	
	return result;
}

//******************************************************************************
// シーン操作：視点登録
//******************************************************************************
- (int)scene_SetViewpoint:(MTScene::MTViewParamMap*)pParamMap
{
	int result = 0;
	
	m_pScene->SetViewParam(pParamMap);
	
	return result;
}

//******************************************************************************
// シーン操作：視点取得
//******************************************************************************
- (int)scene_GetViewpoint:(MTScene::MTViewParamMap*)pParamMap
{
	int result = 0;
	
	m_pScene->GetViewParam(pParamMap);
	
	return result;
}

//******************************************************************************
// シーン操作：エフェクト設定
//******************************************************************************
- (int)scene_SetEffect:(MTScene::EffectType)type isEnable:(bool)isEnable
{
	int result = 0;
	
	m_pScene->SetEffect(type, isEnable);
	
	return result;
}

//******************************************************************************
// シーン操作：スクリーン種別設定
//******************************************************************************
- (int)scene_SetScreenType:(MTScene::ScreenType)type;
{
	int result = 0;
	
	m_ScreenType = type;
	m_pScene->SetScreenType(m_ScreenType);
	
	//VRビューの場合はモーションセンサ利用開始
	//通常ビューに戻る場合はモーションセンサ利用終了
	if (m_pMotionManager.deviceMotionAvailable) {
		if ((m_ScreenType == MTScene::VRViewSingleLens)
		 || (m_ScreenType == MTScene::VRViewDualLens)) {
			[m_pMotionManager startDeviceMotionUpdates];
		}
		else {
			[m_pMotionManager stopDeviceMotionUpdates];
		}
	}
	
	return result;
}

//******************************************************************************
// シーン操作：スクリーンロック
//******************************************************************************
- (int)scene_LockScreen:(bool)isLocked;
{
    int result = 0;
    
    m_pScene->LockScreen(isLocked);
    
    return result;
}

//******************************************************************************
// シーン操作：ゲームコントローラー更新イベント
//******************************************************************************
- (int)scene_OnGameControllerChanged
{
	int result = 0;
	
	if (m_pScene != NULL) {
		result = m_pScene->OnGameControllerChanged();
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// シーン描画
//******************************************************************************
- (void)drawScene:(NSTimer*)timer
{
	int result = 0;
	
	if (m_pScene == NULL) goto EXIT;
	
	m_isStopScene = NO;
	
	//シーケンサメッセージ処理
	result = [self sequencerMsgProc];
	if (result != 0) goto EXIT;
	
	//バックグラウンドの場合は描画処理を行わずに終了
	if (m_isBackground) goto EXIT;
	
	//デバイス方向更新
	if ((m_ScreenType == MTScene::VRViewSingleLens)
	 || (m_ScreenType == MTScene::VRViewDualLens)) {
		result = [self updateDeviceDirection];
		if (result != 0) goto EXIT;
	}
	
	//描画
	result = [self drawProc];
	if (result != 0) goto EXIT;
	
	//FPS更新
	[self updateFPS:m_PrevDrawTime];

	//描画処理開始時刻
	m_PrevDrawTime = m_MachTime.GetCurTimeInNanosec();
	
	//ボタン表示状態を更新
	[self updateButtonStatus];
	
	//タッチガイド画像表示状態を更新
	[self updateGuideImageStatus];
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
}

//******************************************************************************
// シーケンサメッセージ処理
//******************************************************************************
- (int)sequencerMsgProc
{
	int result = 0;
	bool isExist = false;
	unsigned int wParam = 0;
	unsigned int lParam = 0;
	SMMsgParser parser;
	
	while (YES) {
		//メッセージ取り出し
		result = m_pMsgQueue->GetMessage(&isExist, &wParam, &lParam);
		if (result != 0) goto EXIT;
		
		//メッセージがなければ終了
		if (!isExist) break;
		
		//メッセージ通知
		result = m_pScene->OnRecvSequencerMsg(wParam, lParam);
		if (result != 0) goto EXIT;	
		
		//演奏状態変更通知への対応
		parser.Parse(wParam, lParam);
		if (parser.GetMsg() == SMMsgParser::MsgPlayStatus) {
			//一時停止
			if (parser.GetPlayStatus() == SMMsgParser::StatusPause) {
				[self postPlayStatus:@"onChangePlayStatusPause"];
			}
			//停止（演奏終了）
			else if (parser.GetPlayStatus() == SMMsgParser::StatusStop) {
				[self postPlayStatus:@"onChangePlayStatusStop"];
			}
		}
		
		//演奏時間
		if (parser.GetMsg() == SMMsgParser::MsgPlayTime) {
			if (m_PrevPlayTimeSec != parser.GetPlayTimeSec()) {
				[self setNowPlayingInfoPlaybackTime:parser.GetPlayTimeSec()];
				m_PrevPlayTimeSec = parser.GetPlayTimeSec();
			}
		}
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 描画処理
//******************************************************************************
- (int)drawProc
{
	int result = 0;
	bool isVRViewDualLens = false;
	const GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES};
	
	if (m_ScreenType == MTScene::VRViewDualLens) {
		isVRViewDualLens = true;
	}
	
	//EAGLコンテキストを設定
	[EAGLContext setCurrentContext:m_pEAGLContext];
	
	//フレームバッファを設定
	if (m_RendererParam.isEnableAntialiasing) {
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_MSAAFrameBuffer);
	}
	
	//レンダリング
	result = m_Renderer.RenderScene((OGLScene*)m_pScene, isVRViewDualLens, (CGFloat)m_IPDPoints);
	if (result != 0) goto EXIT;
	
	//レンダリングバッファを表示
	if (m_RendererParam.isEnableAntialiasing) {
		glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, m_MSAAFrameBuffer);
		glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, m_DefaultFrameBuffer);
		glResolveMultisampleFramebufferAPPLE();
	}
	glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, attachments);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_DefaultColorRenderBuffer);
	[m_pEAGLContext presentRenderbuffer:GL_RENDERBUFFER_OES];
	
EXIT:;
	return result;
}

//******************************************************************************
// 演奏状態変更通知受信処理
//******************************************************************************
- (int)postPlayStatus:(NSString*)pNotificationName
{
	int result = 0;
	NSNotification* pNotification = nil;
	NSNotificationCenter* pCenter = nil;
	
	//通知オブジェクトを作成
    pNotification = [NSNotification notificationWithName:pNotificationName
												  object:self
												userInfo:nil];
	//通知する
	pCenter = [NSNotificationCenter defaultCenter];
	
	//通知に対応する処理を演奏スレッドで処理させる場合
	//[pCenter postNotification:pNotification];
	
	//通知に対応する処理をメインスレッドに処理させる場合
	[pCenter performSelectorOnMainThread:@selector(postNotification:)
							  withObject:pNotification
						   waitUntilDone:NO];
	
	return result;
}

//******************************************************************************
// FPS更新
//******************************************************************************
- (void)updateFPS:(uint64_t)startTime
{
	uint64_t curTime = 0;
	uint64_t deltaTime = 0;
	float fps = 0.0f;
	
	//表示フレーム数カウント
	m_FlameCount += 1.0f;
	
	//現在時刻
	curTime = m_MachTime.GetCurTimeInNanosec();
	
	//前回FPS表示更新からの経過時間（ナノ秒）
	deltaTime = curTime - m_PrevFPSDrawTime;
	
	//1秒おきにFPS表示更新
	if (deltaTime >= (1000 * 1000000)) {
		fps = (double)(1000 * 1000000) / ((double)deltaTime / m_FlameCount);
		m_pScene->SetFPS(fps);
		m_PrevFPSDrawTime = curTime;
		m_FlameCount = 0.0f;
		//NSLog(@"FPS:%.1f", fps);
	}
}

//******************************************************************************
// クローズボタン押下
//******************************************************************************
- (IBAction)onCloseButton
{
	//クローズ通知
	[self postNotificationWithName:@"onCloseButton"];
	
	return;
}

//******************************************************************************
// 再生ボタン押下
//******************************************************************************
- (IBAction)onPlayButton
{	
	//再生通知
	[self postNotificationWithName:@"onPlayButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 停止ボタン押下
//******************************************************************************
- (IBAction)onStopButton
{
	//停止通知
	[self postNotificationWithName:@"onStopButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 後方スキップボタン押下
//******************************************************************************
- (IBAction)onSkipBackwardButton
{
	//後方スキップ通知
	[self postNotificationWithName:@"onSkipBackwardButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 前方スキップボタン押下
//******************************************************************************
- (IBAction)onSkipForwardButton
{
	//前方スキップ通知
	[self postNotificationWithName:@"onSkipForwardButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 再生スピードダウンボタン押下
//******************************************************************************
- (IBAction)onPlaySpeedDownButton
{
	//再生スピードダウン通知
	[self postNotificationWithName:@"onPlaySpeedDownButton"];	
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 再生スピードアップボタン押下
//******************************************************************************
- (IBAction)onPlaySpeedUpButton
{
	//再生スピードアップ通知
	[self postNotificationWithName:@"onPlaySpeedUpButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 自動演奏ボタン押下
//******************************************************************************
- (IBAction)onFolderPlaybackButton
{
	//通知
	[self postNotificationWithName:@"onFolderPlaybackButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// リピートボタン押下
//******************************************************************************
- (IBAction)onRepeatButton
{
	//通知
	[self postNotificationWithName:@"onRepeatButton"];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// ビューボタン押下
//******************************************************************************
- (IBAction)onViewButton
{
	UIAlertController* pAlertController = nil;
	NSString* pLockTitle = nil;
	CGRect buttonRect;
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	//アクションシート生成
	pAlertController = [UIAlertController alertControllerWithTitle:nil
														   message:nil
													preferredStyle:UIAlertControllerStyleActionSheet];
	//スクリーンロックタイトル
	if (m_isScreenLocked) {
		pLockTitle = @"Unlock Screen";
	}
	else {
		pLockTitle = @"Lock Screen";
	}
	
	//アクション登録：Viewpoint 1
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Viewpoint 1"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_MoveToStaticViewpoint:1];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：Viewpoint 2
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Viewpoint 2"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_MoveToStaticViewpoint:2];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：Viewpoint 3
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Viewpoint 3"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_MoveToStaticViewpoint:3];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：Toggle Rotation
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Toggle Rotation"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		m_pScene->OnWindowClicked(WM_MBUTTONDOWN, 0, 0);
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：Standard View
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Standard View"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_SetScreenType:(MTScene::StandardView)];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：VR View (Single Lens)
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"VR View (Single Lens)"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_SetScreenType:(MTScene::VRViewSingleLens)];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：VR View (Dual Lens)
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"VR View (Dual Lens)"
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		[self scene_SetScreenType:(MTScene::VRViewDualLens)];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：スクリーンロック
	[pAlertController addAction:[UIAlertAction actionWithTitle:pLockTitle
														 style:UIAlertActionStyleDefault
													   handler:^(UIAlertAction *action) {
		m_isScreenLocked = m_isScreenLocked ? false : true;
		[self scene_LockScreen:m_isScreenLocked];
		m_isViewActionSheetVisible = false;
	}]];
	//アクション登録：キャンセル
	[pAlertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
														 style:UIAlertActionStyleCancel
													   handler:^(UIAlertAction *action) {
		m_isViewActionSheetVisible = false;
	}]];
	
	//アクションシート表示位置：iPad向け：Viewボタン下辺中央を吹き出し位置とする
	buttonRect = m_pViewButton.frame;
	pAlertController.popoverPresentationController.sourceView = self.view;
	pAlertController.popoverPresentationController.sourceRect = CGRectMake(buttonRect.origin.x + (buttonRect.size.width / 2), //吹き出し位置
																		   buttonRect.origin.y + buttonRect.size.height, //吹き出し位置
																		   0,
																		   0);
	
	//アクションシート表示
	[self presentViewController:pAlertController animated:YES completion:nil];
	m_isViewActionSheetVisible = true;
	
	return;
}

//******************************************************************************
// ヘルプボタン押下
//******************************************************************************
- (IBAction)onHelpButton
{
	//HowToViewヘルプ表示
	m_pHowToViewCtrl.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    [self presentViewController:m_pHowToViewCtrl animated:YES completion:nil];
	
	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// タッチイベント：開始
//******************************************************************************
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event
{
	CGPoint point;
	
	//シーンに通知
	if (m_pScene != NULL) {
		//タッチイベント通知
		m_pScene->OnTouchesBegan(touches);
		
		//タップ判定開始
		point = [[touches anyObject] locationInView:m_pBaseView];
		m_PrevTouchPoint.x = point.x;
		m_PrevTouchPoint.y = point.y;
		m_TounchesTotalDistance = 0;
		//NSLog(@"touchesBegan %f, %f", point.x, point.y);
	}
	
	return;
}

//******************************************************************************
// タッチイベント：移動
//******************************************************************************
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event
{
	CGPoint point;
	
	//メモ：本イベントは指を移動させなくても定期的に発生する（同じ座標で通知される）
	
	//シーンに通知
	if (m_pScene != NULL) {
		//タッチイベント通知
		m_pScene->OnTouchesMoved(touches);
		
		//移動距離加算
		point = [[touches anyObject] locationInView:m_pBaseView];
		m_TounchesTotalDistance += sqrtf(powf(point.x - m_PrevTouchPoint.x, 2)
										 + powf(point.y - m_PrevTouchPoint.y, 2));
		m_PrevTouchPoint = point;
		//NSLog(@"touchesMoved %f, %f", point.x, point.y);
	}
	
	return;
}

//******************************************************************************
// タッチイベント：終了
//******************************************************************************
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event
{
	//シーンに通知
	if (m_pScene != NULL) {
		//タッチイベント通知
		m_pScene->OnTouchesEnd(touches);
		
		//移動距離がゼロの場合はタップとみなす
		//NSLog(@"touchesEnded %f", m_TounchesTotalDistance);
		if (m_TounchesTotalDistance == 0.0f) {
			[self onTapped];
			m_PrevTouchPoint.x = 0;
			m_PrevTouchPoint.y = 0;
			m_TounchesTotalDistance = 0;
		}
	}
	
	return;
}

//******************************************************************************
// タッチイベント：キャンセル
//******************************************************************************
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event
{
	//シーンに通知
	if (m_pScene != NULL) {
		//タッチイベント通知
		m_pScene->OnTouchesCanceled(touches);
		
		//キャンセルが発生した場合はタップ判定解除
		m_PrevTouchPoint.x = 0;
		m_PrevTouchPoint.y = 0;
		m_TounchesTotalDistance = 0;
	}
	
	return;
}

//******************************************************************************
// タップ
//******************************************************************************
- (void)onTapped
{
	//ボタン表示状態を反転
	m_isDisplayButtons = !m_isDisplayButtons;

	//最終タッチ時間更新
	m_LastTouchTime = m_MachTime.GetCurTimeInNanosec();
	
	return;
}

//******************************************************************************
// 通知送信処理
//******************************************************************************
- (int)postNotificationWithName:(NSString*)pName
{
	int result = 0;
	NSNotification* pNotification = nil;
	NSNotificationCenter* pCenter = nil;
	
	//通知オブジェクトを作成
    pNotification = [NSNotification notificationWithName:pName
												  object:self
												userInfo:nil];
	//通知する
	pCenter = [NSNotificationCenter defaultCenter];
	
	//通知に対応する処理を演奏スレッドで処理させる場合
	//[pCenter postNotification:pNotification];
	
	//通知に対応する処理をメインスレッドに処理させる場合
	[pCenter performSelectorOnMainThread:@selector(postNotification:)
							  withObject:pNotification
						   waitUntilDone:NO];
	
	return result;
}

//******************************************************************************
// 全アイテム非表示
//******************************************************************************
- (void)hideAllItems
{
	//ボタン：上段
	m_pCloseButton.hidden = YES;
	m_pFolderPlaybackButton.hidden = YES;
	m_pRepeatButton.hidden = YES;
	m_pViewButton.hidden = YES;
	m_pHelpButton.hidden = YES;
	
	//ボタン：下段
	m_pPlayButton.hidden = YES;
	m_pStopButton.hidden = YES;
	m_pSkipBackwardButton.hidden = YES;
	m_pSkipForwardButton.hidden = YES;
	m_pPlaySpeedDownButton.hidden = YES;
	m_pPlaySpeedUpButton.hidden = YES;
	
	//ガイド
	m_pGuideImageUD.hidden = YES;
	m_pGuideImageFB.hidden = YES;
	m_pGuideImageLR.hidden = YES;
	m_pGuideImageRT.hidden = YES;
	
	//インジケータ
	m_pActivityIndicator.hidden = YES;
	m_pLabel.hidden = YES;
	
	return;
}

//******************************************************************************
// ボタン表示状態更新
//******************************************************************************
- (void)updateButtonStatus
{
	uint64_t elapsedTime = 0;
	
	//最終タッチ時刻から5秒経過したらボタンを隠す
	if (m_isDisplayButtons) {
		elapsedTime = m_MachTime.GetCurTimeInNanosec() - m_LastTouchTime;
		if (elapsedTime > (5ULL * 1000ULL * 1000000ULL)) {
			m_isDisplayButtons = NO;
		}
	}
	
	//ボタンの表示更新
	m_pCloseButton.hidden = !m_isDisplayButtons;
	m_pFolderPlaybackButton.hidden = !m_isDisplayButtons;
	m_pRepeatButton.hidden = !m_isDisplayButtons;
	m_pViewButton.hidden = !m_isDisplayButtons;
	m_pHelpButton.hidden = !m_isDisplayButtons;
	m_pPlayButton.hidden = !m_isDisplayButtons;
	m_pStopButton.hidden = !m_isDisplayButtons;
	m_pSkipBackwardButton.hidden = !m_isDisplayButtons;
	m_pSkipForwardButton.hidden = !m_isDisplayButtons;
	m_pPlaySpeedDownButton.hidden = !m_isDisplayButtons;
	m_pPlaySpeedUpButton.hidden = !m_isDisplayButtons;

	//MIDI IN モニタでは一部のボタンを表示しない
	if (m_isMonitor) {
		m_pFolderPlaybackButton.hidden = YES;
		m_pRepeatButton.hidden = YES;
		m_pPlayButton.hidden = YES;
		m_pStopButton.hidden = YES;
		m_pSkipBackwardButton.hidden = YES;
		m_pSkipForwardButton.hidden = YES;
		m_pPlaySpeedDownButton.hidden = YES;
		m_pPlaySpeedUpButton.hidden = YES;
	}
	
	//タッチガイド表示中はすべてのボタンを表示しない
	if ((m_pScene->GetCurOperationType() == MTTouchCtrl::OperationMoveUD)
	 || (m_pScene->GetCurOperationType() == MTTouchCtrl::OperationMoveFB)
	 || (m_pScene->GetCurOperationType() == MTTouchCtrl::OperationMoveLR)
	 || (m_pScene->GetCurOperationType() == MTTouchCtrl::OperationRotate)) {
		m_pCloseButton.hidden = YES;
		m_pFolderPlaybackButton.hidden = YES;
		m_pRepeatButton.hidden = YES;
		m_pViewButton.hidden = YES;
		m_pHelpButton.hidden = YES;
		m_pPlayButton.hidden = YES;
		m_pStopButton.hidden = YES;
		m_pSkipBackwardButton.hidden = YES;
		m_pSkipForwardButton.hidden = YES;
		m_pPlaySpeedDownButton.hidden = YES;
		m_pPlaySpeedUpButton.hidden = YES;
	}
	
	return;
}

//******************************************************************************
// タッチガイド画像表示状態更新
//******************************************************************************
-(void)updateGuideImageStatus
{
	m_pGuideImageUD.hidden = YES;
	m_pGuideImageFB.hidden = YES;
	m_pGuideImageLR.hidden = YES;
	m_pGuideImageRT.hidden = YES;
	
	if (m_pScene == NULL) goto EXIT;
	
	//操作中のガイドのみを表示
	switch (m_pScene->GetCurOperationType()) {
		case (MTTouchCtrl::OperationNone):
			break;
		case (MTTouchCtrl::OperationEyeDir):
			break;
		case (MTTouchCtrl::OperationMoveUD):
			m_pGuideImageUD.hidden = NO;
			break;
		case (MTTouchCtrl::OperationMoveFB):
			m_pGuideImageFB.hidden = NO;
			break;
		case (MTTouchCtrl::OperationMoveLR):
			m_pGuideImageLR.hidden = NO;
			break;
		case (MTTouchCtrl::OperationRotate):
			m_pGuideImageRT.hidden = NO;
			break;
		default:
			break;
	}
	
EXIT:;
	return;
}

//******************************************************************************
// フォルダ演奏状態設定
//******************************************************************************
- (void)setFolderPlaybackStatus:(BOOL)isFolderPlayback
{
	if (isFolderPlayback) {
		[m_pFolderPlaybackButton setSelected:YES];
	}
	else {
		[m_pFolderPlaybackButton setSelected:NO];
	}
	
	return;
}

//******************************************************************************
// リピート状態設定
//******************************************************************************
- (void)setRepeatStatus:(BOOL)isRepeat
{
	if (isRepeat) {
		[m_pRepeatButton setSelected:YES];
	}
	else {
		[m_pRepeatButton setSelected:NO];
	}
	
	return;
}

//******************************************************************************
// インジケータアニメーション開始
//******************************************************************************
- (void)startActivityIndicator
{
	[self hideAllItems];
	
	m_pActivityIndicator.hidden = NO;
	m_pLabel.hidden = NO;
	
	[m_pActivityIndicator startAnimating];
	
	return;
}

//******************************************************************************
// インジケータアニメーション停止
//******************************************************************************
- (void)stopActivityIndicator
{
	m_pActivityIndicator.hidden = YES;
	m_pLabel.hidden = YES;
	
	[m_pActivityIndicator stopAnimating];
	
	return;
}

//******************************************************************************
// カレントコンテキスト設定
//******************************************************************************
- (void)setCurrentContext
{
	//OpenGLを利用するスレッドを切り替えるときは
	//当該スレッドにて本メソッドを呼ぶこと
	[EAGLContext setCurrentContext:m_pEAGLContext];
	
	return;
}

//******************************************************************************
// 演奏状態設定
//******************************************************************************
- (void)setPlayStatus:(PlayStatus)status
{
	//再生ボタンの画像を初期化
	[m_pPlayButton setImage:m_pImgButtonPlayN forState:UIControlStateNormal];
	[m_pPlayButton setImage:m_pImgButtonPlayH forState:UIControlStateHighlighted];
	
	switch(status) {
		//データなし
		case NoData:
			break;
		//停止状態
		case Stop:
			break;
		//再生中
		case Play:
			//再生ボタンの画像を一時停止アイコンに置き換える
			[m_pPlayButton setImage:m_pImgButtonPauseN forState:UIControlStateNormal];
			[m_pPlayButton setImage:m_pImgButtonPauseH forState:UIControlStateHighlighted];
			break;
		//一時停止
		case Pause:
			break;	
		//モニタ停止
		case MonitorOFF:
			break;
		//モニタ中
		case MonitorON:
			break;
		default:
			break;
	}
	
	return;
}

//******************************************************************************
// ステータスバー非表示
//******************************************************************************
- (BOOL)prefersStatusBarHidden
{
    return YES;
}

//******************************************************************************
// デバイス方向更新
//******************************************************************************
- (int)updateDeviceDirection
{
	int result = 0;
	CMQuaternion quaternion;
	OGLQuaternion q;
	
	//モーションセンサが有効でなければ何もしない
	if (!m_pMotionManager.deviceMotionAvailable) goto EXIT;
	
	//デバイス方向（クォータニオン）取得
	quaternion = m_pMotionManager.deviceMotion.attitude.quaternion;
	q.x = quaternion.x;
	q.y = quaternion.y;
	q.z = quaternion.z;
	q.w = quaternion.w;
	
	//デバイス方向設定
	if (m_pScene != NULL) {
		m_pScene->SetDeviceDirection(q);
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// VR用パラメータ初期化
//******************************************************************************
- (int)initializeVRParameters
{
	int result = 0;
	MTDeviceScreenInfo deviceScreenInfo;
	
	//デバイススクリーン情報初期化
	deviceScreenInfo.Initialize();
	
	//瞳孔間距離(Interpupillary distance)ポイント数を算出
	if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
		//iPhone / iPod touch
		m_IPDPoints = MT_VR_IPD_MM / deviceScreenInfo.GetMmPerPoint();
	}
	else {
		//iPadの場合は無効値を設定
		m_IPDPoints = 0;
	}
	
	return result;
}

//******************************************************************************
// ゲームコントローラー接続通知
//******************************************************************************
- (void)onGameControllerDidConnect
{
	int result = 0;
	
	NSLog(@"Game Controller connected");
	
	//ゲームコントローラー初期化
	result = [self initializeGameController];
	if (result != 0) goto EXIT;
	
	//ゲームコントローラー更新をシーンに通知
	result = [self scene_OnGameControllerChanged];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// ゲームコントローラー切断通知
//******************************************************************************
- (void)onGameControllerDidDisconnect
{
	int result = 0;
	
	NSLog(@"Game Controller disconnected");
	
	//ゲームコントローラー初期化
	result = [self initializeGameController];
	if (result != 0) goto EXIT;
	
	//ゲームコントローラー更新をシーンに通知
	result = [self scene_OnGameControllerChanged];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// ゲームコントローラー初期化
//******************************************************************************
- (int)initializeGameController
{
	int result = 0;
	GCController* pController = nil;
	
	//ゲームパッド制御初期化
	result = m_GamePadCtrl.Initialize(0);
	if (result != 0) goto EXIT;
	
	//ゲームコントローラーが存在しなければ何もせず終了
	pController = m_GamePadCtrl.GetController();
	if (pController == nil) goto EXIT;
	
	//ポーズボタン：演奏開始／一時停止
	pController.controllerPausedHandler
	= ^(GCController* controller) {
		[self onPlayButton];
	};
	//Aボタン：演奏開始／一時停止
	pController.gamepad.buttonA.valueChangedHandler
	= ^(GCControllerButtonInput* pButton, float value, BOOL isPressed) {
		m_GamePadCtrl.UpdateState();
		if (m_GamePadCtrl.DidPressNow_A()){
			[self onPlayButton];
		}
	};
	//Bボタン：演奏停止
	pController.gamepad.buttonB.valueChangedHandler
	= ^(GCControllerButtonInput* pButton, float value, BOOL isPressed) {
		m_GamePadCtrl.UpdateState();
		if (m_GamePadCtrl.DidPressNow_B()){
			[self onStopButton];
		}
	};
	//拡張ゲームパッドでない場合
	if (pController.extendedGamepad == nil) {
		//L ショルダーボタン：視点切り替え
		pController.gamepad.leftShoulder.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_LShoulder()){
				[self changeViewPoint:-1];
			}
		};
		//R ショルダーボタン：視点切り替え
		pController.gamepad.rightShoulder.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_RShoulder()){
				[self changeViewPoint:+1];
			}
		};
	}
	//拡張ゲームパッドの場合
	else {
		//L1 ショルダーボタン：視点切り替え
		pController.extendedGamepad.leftShoulder.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_LShoulder()){
				[self changeViewPoint:-1];
			}
		};
		//R1 ショルダーボタン：視点切り替え
		pController.extendedGamepad.rightShoulder.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_RShoulder()){
				[self changeViewPoint:+1];
			}
		};
		//L2 トリガーボタン：再生リワインド
		pController.extendedGamepad.leftTrigger.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_LTrigger()){
				[self onSkipBackwardButton];
			}
		};
		//R2 トリガーボタン：再生スキップ
		pController.extendedGamepad.rightTrigger.valueChangedHandler
		= ^(GCControllerButtonInput* pButtonInput, float value, BOOL isPressed) {
			m_GamePadCtrl.UpdateState();
			if (m_GamePadCtrl.DidPressNow_RTrigger()){
				[self onSkipForwardButton];
			}
		};
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// ゲームコントローラーボタンイベントに関するメモ
//******************************************************************************
// ボタンを1回押すだけでもvalueの異なる複数の通知が連続発生する
// ボタンを半押しで押し続けるとvalueが異なる通知が発生し続ける
// ボタンを全押しするとvalueが1.0となり通知が止まる
// ボタンを離すとvalueが0.0の通知が発生し、次にボタンを押すまで通知が発生しない

//******************************************************************************
// 視点切り替え
//******************************************************************************
- (void)changeViewPoint:(int)step
{
	//ゲームパッド用視点番号更新
	m_GamePadViewPointNo += step;
	
	if (m_GamePadViewPointNo < 0) {
		m_GamePadViewPointNo = 2;
	}
	else if (m_GamePadViewPointNo > 2) {
		m_GamePadViewPointNo = 0;
	}
	
	//視点切り替え
	switch (m_GamePadViewPointNo) {
		case 0:
			[self scene_ResetViewpoint];
			break;
		case 1:
			[self scene_MoveToStaticViewpoint:2];
			break;
		case 2:
			[self scene_MoveToStaticViewpoint:3];
			break;
		default:
			break;
	}
	
	return;
}

//******************************************************************************
// バックグラウンド状態設定
//******************************************************************************
- (void)setBackground:(bool)isBackground
{
	m_isBackground = isBackground;
}

//******************************************************************************
// リワインド／スキップ設定
//******************************************************************************
- (void)setSkipTimeSpanWithBack:(int)backTimeSpanInMsec
						 foward:(int)forwardTimeSpanInMSec
{
	m_SkipBackTimeSpanInMsec = backTimeSpanInMsec;
	m_SkipForwardTimeSpanInMsec = forwardTimeSpanInMSec;
}

//******************************************************************************
// リモートコマンドハンドラ設定
//******************************************************************************
- (void)setRemoteCommandHandlerWithMonitor:(BOOL)isMonitor
{
	MPRemoteCommandCenter* pCommandCenter = nil;
	bool isEnablePlayPause = true;
	
	pCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
	
	//MIDI IN モニタの場合は再生と一時停止は操作不可
	if (isMonitor) {
		isEnablePlayPause = false;
	}
	
	//対応コマンド
	pCommandCenter.playCommand.enabled = isEnablePlayPause;				//再生
	pCommandCenter.pauseCommand.enabled = isEnablePlayPause;			//一時停止
	pCommandCenter.togglePlayPauseCommand.enabled = isEnablePlayPause;	//再生/一時停止：ヘッドホンセンターボタン
	pCommandCenter.stopCommand.enabled = isEnablePlayPause;				//停止
	pCommandCenter.skipBackwardCommand.enabled = isEnablePlayPause;		//後方スキップ
	pCommandCenter.skipForwardCommand.enabled = isEnablePlayPause;		//前方スキップ
	
	//スキップ時間間隔
	[pCommandCenter.skipBackwardCommand setPreferredIntervals:@[@(m_SkipBackTimeSpanInMsec/1000)]];
	[pCommandCenter.skipForwardCommand setPreferredIntervals:@[@(m_SkipForwardTimeSpanInMsec/1000)]];
	
	//非対応コマンド
	pCommandCenter.seekBackwardCommand.enabled = false;					//後方シーク
	pCommandCenter.seekForwardCommand.enabled = false;					//前方シーク
	pCommandCenter.changePlaybackPositionCommand.enabled = false;		//再生位置
	pCommandCenter.changePlaybackRateCommand.enabled = false;			//再生速度
	pCommandCenter.previousTrackCommand.enabled = false;				//前トラック
	pCommandCenter.nextTrackCommand.enabled = false;					//次トラック
	
	//コマンドハンドラ登録
    [pCommandCenter.playCommand addTarget:self action:@selector(onPlayCommand:)];
    [pCommandCenter.pauseCommand addTarget:self action:@selector(onPauseCommand:)];
	[pCommandCenter.togglePlayPauseCommand addTarget:self action:@selector(onTogglePlayPauseCommand:)];
	[pCommandCenter.stopCommand addTarget:self action:@selector(onStopCommand:)];
	[pCommandCenter.skipBackwardCommand addTarget:self action:@selector(onSkipBackwardCommand:)];
	[pCommandCenter.skipForwardCommand addTarget:self action:@selector(onSkipForwardCommand:)];
	
	//非対応
	//[pCommandCenter.seekBackwardCommand addTarget:self action:@selector(onSeekBackwardCommand:)];
	//[pCommandCenter.seekForwardCommand addTarget:self action:@selector(onSeekForwardCommand:)];
	//[pCommandCenter.changePlaybackPositionCommand addTarget:self action:@selector(onChangePlaybackPositionCommand:)];
	//[pCommandCenter.changePlaybackRateCommand addTarget:self action:@selector(onChangePlaybackRateCommand:)];
	//[pCommandCenter.changePlaybackRateCommand addTarget:self action:@selector(onChangePlaybackRateCommand:)];
	//[pCommandCenter.previousTrackCommand addTarget:self action:@selector(onPreviousTrackCommand:)];
	//[pCommandCenter.nextTrackCommand addTarget:self action:@selector(onNextTrackCommand:)];
	
	return;
}

//******************************************************************************
// リモートコマンドハンドラ：再生
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onPlayCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onPlayCommand");
	
	[self onPlayButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// リモートコマンドハンドラ：一時停止
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onPauseCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onPauseCommand");
	
	[self onPlayButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// リモートコマンドハンドラ：再生/一時停止トグル
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onTogglePlayPauseCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onTogglePlayPauseCommand");
	
	[self onPlayButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// リモートコマンドハンドラ：停止
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onStopCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onStopCommand");
	
	[self onStopButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// リモートコマンドハンドラ：後方スキップ
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onSkipBackwardCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onSkipBackwardCommand");
	
	[self onSkipBackwardButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// リモートコマンドハンドラ：前方スキップ
//******************************************************************************
- (MPRemoteCommandHandlerStatus)onSkipForwardCommand:(MPRemoteCommandEvent*)pCommandEvent
{
	NSLog(@"onSkipForwardCommand");
	
	[self onSkipForwardButton];
	
	return MPRemoteCommandHandlerStatusSuccess;
}

//******************************************************************************
// 演奏情報更新：演奏時間
//******************************************************************************
- (int)setNowPlayingInfoPlaybackTime:(unsigned int)playTimeSec
{
	int result = 0;
	NSMutableDictionary* pNowPlayingInfo = nil;
	MPNowPlayingInfoCenter* pNowPlayingInfoCenter = nil;
		
	//現在の演奏情報を取得
	pNowPlayingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
	pNowPlayingInfo = [[NSMutableDictionary alloc] initWithDictionary:pNowPlayingInfoCenter.nowPlayingInfo];
	
	//経過時間
	[pNowPlayingInfo setObject:[NSNumber numberWithInt:playTimeSec] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
	
	//演奏情報を更新
	[pNowPlayingInfoCenter setNowPlayingInfo:pNowPlayingInfo];
	
	[pNowPlayingInfo release];
	return result;
}

//******************************************************************************
// サブビュー表示状態取得
//******************************************************************************
- (bool)isSubViewDisplayed
{
	bool isDisplayed = false;
	
	//HowToViewヘルプ
	if (m_pHowToViewCtrl.isViewLoaded && (m_pHowToViewCtrl.view.window != nil)) {
		isDisplayed = true;
	}
	
	//Viewアクションシート
	if (m_isViewActionSheetVisible) {
		isDisplayed = true;
	}
	
	return isDisplayed;
}

@end

