//******************************************************************************
//
// MIDITrail / MIDITrailApp
//
// MIDITrail アプリケーションクラス
//
// Copyright (C) 2012 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "MIDITrailApp.h"
#import "YNBaseLib.h"
#import "MTParam.h"
#import "MTConfFile.h"
#import "MTSceneTitle.h"
#import "MTScenePianoRoll3D.h"
#import "MTScenePianoRoll2D.h"
#import "MTScenePianoRollRain.h"
#import "MTScenePianoRoll3DLive.h"
#import "MTScenePianoRoll2DLive.h"
#import "MTScenePianoRollRainLive.h"


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

//ビュー生成
- (int)createViews;

//通知受信設定
- (int)initializeNotification;
//ワーカースレッド：シーン生成
- (void)thread_CreateScene;

//設定ファイル初期化
- (int)initConfFile;

//シーン種別読み込み
- (void)loadSceneType;

//視点読み込み
- (int)loadViewpoint;

//視点保存
- (int)saveViewpoint;

//プレーヤー設定読み込み
- (int)loadPlayerConf;

//表示効果反映
- (int)updateEffect;

//演奏状態変更
- (int)changePlayStatus:(PlayStatus)status;

//メインビュー表示
- (void)openMainView;

//メインビュー消去
- (void)closeMainView;

//シーン生成
- (int)createSceneWithType:(SceneType)type SeqData:(SMSeqData*)pSeqData;

//ポートデバイス登録
- (int)setPortDev:(SMSequencer*)pSequencer;

//MIDI IN モニタ情報登録
- (int)setMonitorPortDev:(SMLiveMonitor*)pLiveMonitor scene:(MTScene*)pScene;

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

@end


@implementation MIDITrailApp

//******************************************************************************
// 初期化
//******************************************************************************
- (id)init
{
	//オブジェクト初期化
    self = [super init];
    if (self == nil) {
		goto EXIT;
	}
	
	//エラー制御初期化
	[YNErrCtrl initOnThreadStart];
	
	//ウィンドウ系
	m_pWindow = nil;
	m_pTabBarCtrl = nil;
	m_pFileViewCtrl = nil;
	m_pMonitorViewCtrl = nil;
	m_pSettingViewCtrl = nil;
	m_pHelpViewCtrl = nil;
	
	//レンダリング系
	m_pScene = NULL;
	memset(&m_RendererParam, 0, sizeof(OGLRedererParam));
	
	//演奏状態
	m_PlayStatus = NoData;
	m_isRepeat = false;
	m_isRewind = false;
	m_PlaySpeedRatio = 100;
	
	//表示状態
	m_isEnablePianoKeyboard = true;
	m_isEnableRipple = true;
	m_isEnablePitchBend = true;
	m_isEnableStars = true;
	m_isEnableCounter = true;
	
	//シーン種別
	m_SceneType = Title;
	m_SelectedSceneType = PianoRoll3D;
	
	//ユーザ設定
	m_pUserConf = nil;
	
	//アプリケーションアクティブ状態
	m_isAppActive = true;
	
	//リワインド／スキップ制御
	m_SkipBackTimeSpanInMsec = 10000;
	m_SkipForwardTimeSpanInMsec = 10000;
	
	//演奏スピード制御
	m_SpeedStepInPercent = 1;
	m_MaxSpeedInPercent = 400;
		
EXIT:;
    return self;
}

//******************************************************************************
// 初期化
//******************************************************************************
- (int)initialize
{
	int result = 0;
	
	//設定ファイル初期化
	result = [self initConfFile];
	if (result != 0) goto EXIT;
	
	//プレーヤー設定読み込み
	result = [self loadPlayerConf];
	if (result != 0) goto EXIT;
	
	//メッセージキュー初期化
	result = m_MsgQueue.Initialize(10000);
	if (result != 0) goto EXIT;
	
	//シーケンサ初期化
	//  演奏開始時に毎回初期化するので本来はここで実行する必要はないが、
	//  プロセス起動直後の初回演奏開始に限って約2秒止まる現象を回避するため実施する。
	//  当該現象の原因は、MIDI出力制御の初期化処理でCoreMIDI APIである
	//  MIDIGetNumberOfDevices()の呼び出しに1秒以上かかることにある。
	//  プロセス起動時にこの処理を実施して演奏開始時のユーザへのストレスを低減する。
	result = m_Sequencer.Initialize(&m_MsgQueue);
	if (result != 0) goto EXIT;
	
	//ネットワークセッション初期化
	result = m_NetworkSession.Initialize();
	if (result != 0) goto EXIT;
	
	//ネットワークセッション有効化
	//  CoreMIDIデバイスの検索でネットワークコネクションを表示させる
	m_NetworkSession.Enable();
	
	//ビュー生成
	result = [self createViews];
	if (result != 0) goto EXIT;
	
	//演奏状態変更
	result = [self changePlayStatus:NoData];
	if (result != 0) goto EXIT;
	
	//通知受信設定
	result = [self initializeNotification];
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// ビュー生成
//******************************************************************************
- (int)createViews
{
	int result = 0;
	NSString* pDeviceType = nil;
	NSString* pNibNameMTFileView = nil;
	NSString* pNibNameMTMonitorView = nil;
	NSString* pNibNameMTSettingView = nil;
	NSString* pNibNameMTHelpView = nil;
	NSString* pNibNameMTMainView = nil;
	NSArray* pViewArray = nil;
	
	//ウィンドウ生成
	m_pWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	m_pWindow.backgroundColor = [UIColor whiteColor];
	
	//デバイス種別
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
		//iPhone / iPod touch
		pDeviceType = @"iPhone";
    } else {
		//iPad
		pDeviceType = @"iPad";
    }
	
	//Nibファイル名称
	pNibNameMTFileView    = [NSString stringWithFormat:@"MTFileView_%@", pDeviceType];
	pNibNameMTMonitorView = [NSString stringWithFormat:@"MTMonitorView_%@", pDeviceType];
	pNibNameMTSettingView = [NSString stringWithFormat:@"MTSettingView_%@", pDeviceType];
	pNibNameMTHelpView    = [NSString stringWithFormat:@"MTHelpView_%@", pDeviceType];
	pNibNameMTMainView    = [NSString stringWithFormat:@"MTMainView_%@", pDeviceType];
	
	//ビュー制御生成
	m_pFileViewCtrl    = [[MTFileViewCtrl alloc] initWithNibName:pNibNameMTFileView bundle:nil];
	m_pMonitorViewCtrl = [[MTMonitorViewCtrl alloc] initWithNibName:pNibNameMTMonitorView bundle:nil];
	m_pSettingViewCtrl = [[MTSettingViewCtrl alloc] initWithNibName:pNibNameMTSettingView bundle:nil];
	m_pHelpViewCtrl    = [[MTHelpViewCtrl alloc] initWithNibName:pNibNameMTHelpView bundle:nil];
	m_pMainViewCtrl    = [[MTMainViewCtrl alloc] initWithNibName:pNibNameMTMainView bundle:nil
												   rendererParam:m_RendererParam];
	
	//MTMainViewCtrl生成時にOpenGLコンテキストが初期化される
	//これ以降OpenGLのAPIが利用できる
	
	//ナビゲーション制御の生成
	m_pFileNaviCtrl    = [[UINavigationController alloc] initWithRootViewController:m_pFileViewCtrl];
	m_pMonitorNaviCtrl = [[UINavigationController alloc] initWithRootViewController:m_pMonitorViewCtrl];
	m_pSettingNaviCtrl = [[UINavigationController alloc] initWithRootViewController:m_pSettingViewCtrl];
	m_pHelpNaviCtrl    = [[UINavigationController alloc] initWithRootViewController:m_pHelpViewCtrl];
	
	//ナビゲーションバーの設定
	m_pFileNaviCtrl.navigationBar.barStyle = UIBarStyleBlack;
	m_pMonitorNaviCtrl.navigationBar.barStyle = UIBarStyleBlack;
	m_pSettingNaviCtrl.navigationBar.barStyle = UIBarStyleBlack;
	m_pHelpNaviCtrl.navigationBar.barStyle = UIBarStyleBlack;
	
	//タブバー制御生成
	m_pTabBarCtrl = [[MTTabBarCtrl alloc] init];
	
	//タブバー制御にビューを登録
	pViewArray = [NSArray arrayWithObjects:m_pFileNaviCtrl,
											m_pMonitorNaviCtrl,
											m_pSettingNaviCtrl,
											m_pHelpNaviCtrl,
											nil];
	m_pTabBarCtrl.viewControllers = pViewArray;
	
	return result;
}

//******************************************************************************
// 通知受信設定
//******************************************************************************
- (int)initializeNotification
{
	int result = 0;
	NSNotificationCenter* pCenter = nil;
	
	//通知先登録：演奏状態変更通知
	pCenter = [NSNotificationCenter defaultCenter];
	
	//ファイル選択
	[pCenter addObserver:self
				selector:@selector(onSelectFile:)
					name:@"onSelectFile"
				  object:nil];
	//クローズボタン押下
	[pCenter addObserver:self
				selector:@selector(onCloseButton:) 
					name:@"onCloseButton"
				  object:nil];
	//再生ボタン押下
	[pCenter addObserver:self
				selector:@selector(onPlayButton:)
					name:@"onPlayButton"
				  object:nil];
	//停止ボタン押下
	[pCenter addObserver:self
				selector:@selector(onStopButton:)
					name:@"onStopButton"
				  object:nil];
	//後方スキップボタン押下
	[pCenter addObserver:self
				selector:@selector(onSkipBackwardButton:)
					name:@"onSkipBackwardButton"
				  object:nil];
	//前方スキップボタン押下
	[pCenter addObserver:self
				selector:@selector(onSkipForwardButton:)
					name:@"onSkipForwardButton"
				  object:nil];
	//再生スピードダウンボタン押下
	[pCenter addObserver:self
				selector:@selector(onPlaySpeedDownButton:)
					name:@"onPlaySpeedDownButton"
				  object:nil];
	//再生スピードアップボタン押下l
	[pCenter addObserver:self
				selector:@selector(onPlaySpeedUpButton:)
					name:@"onPlaySpeedUpButton"
				  object:nil];
	//リピートボタン押下
	[pCenter addObserver:self
				selector:@selector(onRepeatButton:)
					name:@"onRepeatButton"
				  object:nil];
	//再生状態変化通知：一時停止
	[pCenter addObserver:self
				selector:@selector(onChangePlayStatusPause:)
					name:@"onChangePlayStatusPause"
				  object:nil];
	//再生状態変化通知：停止
	[pCenter addObserver:self
				selector:@selector(onChangePlayStatusStop:)
					name:@"onChangePlayStatusStop"
				  object:nil];
	//モニタ開始
	[pCenter addObserver:self
				selector:@selector(onStartMonitoring:)
					name:@"onStartMonitoring"
				  object:nil];
	//シーン生成完了通知
	[pCenter addObserver:self
				selector:@selector(onFinishCreateScene:)
					name:@"onFinishCreateScene"
				  object:nil];
	
	return result;
}

//******************************************************************************
// 実行
//******************************************************************************
- (int)run
{
	int result = 0;
	
	//ルートビュー登録
	m_pWindow.rootViewController = m_pTabBarCtrl;

	//キーウィンドウ表示
	[m_pWindow makeKeyAndVisible];
	
	return result;
}

//******************************************************************************
// 停止
//******************************************************************************
- (int)terminate
{
	int result = 0;
	
	[m_pMainViewCtrl release];
	[m_pFileViewCtrl release];
	[m_pMonitorViewCtrl release];
	[m_pSettingViewCtrl release];
	[m_pHelpViewCtrl release];
	[m_pTabBarCtrl release];
	[m_pWindow release];
	
	m_pMainViewCtrl = nil;
	m_pFileViewCtrl = nil;
	m_pMonitorViewCtrl = nil;
	m_pSettingViewCtrl = nil;
	m_pHelpViewCtrl = nil;
	m_pTabBarCtrl = nil;
	m_pWindow = nil;
	
	//エラー制御終了
	[YNErrCtrl termOnThreadEnd];
	
	return result;
}

//******************************************************************************
// ファイル選択イベント
//******************************************************************************
- (void)onSelectFile:(NSNotification*)pNotification
{
	int result = 0;
	
	//ファイル読み込み時に再生スピードを100%に戻す：_CreateSceneでカウンタに反映
	m_PlaySpeedRatio = 100;
	
	//メッセージキュークリア
	m_MsgQueue.Clear();
	
	//メインビュー初期化
	result = [m_pMainViewCtrl initialize:&m_MsgQueue];
	if (result != 0) goto EXIT;
	
	//メインビュー表示
	[self openMainView];
	
	//インジケータアニメーション開始
	[m_pMainViewCtrl startActivityIndicator];
	
	//シーン生成スレッド起動
	[NSThread detachNewThreadSelector:@selector(thread_CreateScene) 
							 toTarget:self
						   withObject:nil];
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// ワーカースレッド：シーン生成
//******************************************************************************
- (void)thread_CreateScene
{
	int result = 0;
	NSString* path = nil;
	SMFileReader smfReader;
	NSDictionary* pUserInfo = nil;
	NSAutoreleasePool* pool;
	id pErrInfo = [NSNull null];
	
	//NSLog(@"MIDITrailApp::thread_CreateScene start");
	
	//スレッド開始処理
	pool = [[NSAutoreleasePool alloc]init];
	[YNErrCtrl initOnThreadStart];
	
	//カレントコンテキスト設定
	//OpenGLの処理を担当するスレッドを切り替える
	[m_pMainViewCtrl setCurrentContext];
	
	//ファイルパス取得
	path = [m_pFileViewCtrl selectedFilePath];
	
	//ファイル読み込み
	smfReader.SetEncodingId([m_pSettingViewCtrl selectedEncodingId]);
	result = smfReader.Load(path, &m_SeqData);
	if (result != 0) goto EXIT;
	
	//シーン種別読み込み
	[self loadSceneType];
	
	//シーン生成
	result = [self createSceneWithType:m_SceneType SeqData:&m_SeqData];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) {
		//アラートは呼び出し側スレッドで表示させる
		//本スレッドに登録されているエラー情報を取り出して呼び出し側スレッドに渡す
		pErrInfo = [[YNErrCtrl errInfo] retain];
		
		//呼び出し側に戻ってすぐにメインウィンドウを閉じようとしてもウィンドウが消えない
		//メインウィンドウの表示時間をある程度確保しないとウィンドウが正常に消えないと思われる
		[NSThread sleepForTimeInterval:0.5];
	}
	
	//シーン生成完了通知
	pUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
					[[NSNumber alloc] initWithInt:result], @"result",
					pErrInfo, @"errInfo",
					nil
				 ];
	[self postNotificationWithName:@"onFinishCreateScene" userInfo:pUserInfo];
	
	//スレッド終了処理
	[YNErrCtrl termOnThreadEnd];
	[pool release];
	
	//NSLog(@"MIDITrailApp::thread_CreateScene end");
	return;
}

//******************************************************************************
// シーン生成完了通知
//******************************************************************************
- (void)onFinishCreateScene:(NSNotification*)pNotification
{
	int result = 0;
	NSNumber* pSceneResult = nil;
	YNErrInfo* pErrInfo = nil;
	
	//NSLog(@"MIDITrailApp::onFinishCreateScene");
	
	//カレントコンテキスト設定
	//OpenGLの処理を担当するスレッドを切り替える
	[m_pMainViewCtrl setCurrentContext];
	
	//インジケータアニメーション停止
	[m_pMainViewCtrl stopActivityIndicator];
	
	//シーン生成失敗時は何もせずに終了する
	pSceneResult = [[pNotification userInfo] objectForKey:@"result"];
	if ([pSceneResult intValue] != 0) {
		//メインビュー消去
		[self closeMainView];
		
		//シーン生成スレッドから渡されたエラー情報を本スレッドのエラーとして登録した上でアラート表示する
		pErrInfo = [[pNotification userInfo] objectForKey:@"errInfo"];
		if ((NSNull*)pErrInfo != [NSNull null]) {
			[YNErrCtrl setErr:[pErrInfo errLevel]
					   lineNo:[pErrInfo lineNo]
					 fileName:[pErrInfo fileName]
					  message:[pErrInfo message]
					 errInfo1:[pErrInfo errInfo1]
					 errInfo2:[pErrInfo errInfo2]];
			YN_SHOW_ERR();
			[pErrInfo release];
		}
		goto EXIT;
	}
	
	//演奏状態変更
	result = [self changePlayStatus:Stop];
	if (result != 0) goto EXIT;
	
	m_isRewind = false;
	
	//リピート状態設定
	[m_pMainViewCtrl setRepeatStatus:m_isRepeat];
	
	//シーン開始
	result = [m_pMainViewCtrl startScene:m_pScene isMonitor:NO];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// クローズボタン押下
//******************************************************************************
- (void)onCloseButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onCloseButton");
	
	//演奏を止める
	if ((m_PlayStatus == Play) || (m_PlayStatus == Pause)) {
		m_Sequencer.Stop();
		//シーケンサ側のスレッド終了を待ち合わせるべきだが手を抜く
		[NSThread sleepForTimeInterval:0.1];
	}
	else if (m_PlayStatus == MonitorON) {
		m_LiveMonitor.Stop();
		//厳密にはコールバック関数終了を待ち合わせるべきだが手を抜く
		[NSThread sleepForTimeInterval:0.1];
	}
	
	//演奏状態変更
	result = [self changePlayStatus:NoData];
	if (result != 0) goto EXIT;
	
	//視点保存
	result = [self saveViewpoint];
	if (result != 0) goto EXIT;
	
	//描画停止
	[m_pMainViewCtrl stopScene];

	//メインビュー消去
	[self closeMainView];
	
	//シーン破棄
	if (m_pScene != NULL) {
		m_pScene->Release();
		delete m_pScene;
		m_pScene = NULL;
	}
	
EXIT:;
	return;
}

//******************************************************************************
// 再生ボタン押下
//******************************************************************************
- (void)onPlayButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onPlayButton");
	
	if (m_PlayStatus == Stop) {
		//シーケンサ初期化
		result = m_Sequencer.Initialize(&m_MsgQueue);
		if (result != 0) goto EXIT;
		
		//シーケンサにポート情報を登録
		result = [self setPortDev:&m_Sequencer];
		if (result != 0) goto EXIT;
		
		//シーケンサにシーケンスデータを登録
		result = m_Sequencer.SetSeqData(&m_SeqData);
		if (result != 0) goto EXIT;
		
		//巻き戻し
		if (m_isRewind) {
			m_isRewind = false;
			result = [m_pMainViewCtrl scene_Rewind];
			if (result != 0) goto EXIT;
		}
		
		//シーンに演奏開始を通知
		result = [m_pMainViewCtrl scene_PlayStart];
		if (result != 0) goto EXIT;
		
		//演奏速度
		m_Sequencer.SetPlaySpeedRatio(m_PlaySpeedRatio);
		
		//演奏開始
		result = m_Sequencer.Play();
		if (result != 0) goto EXIT;
		
		//演奏状態変更
		result = [self changePlayStatus:Play];
		if (result != 0) goto EXIT;
	}
	else if (m_PlayStatus == Play) {
		//演奏一時停止
		m_Sequencer.Pause();
		if (result != 0) goto EXIT;
		
		//演奏状態変更
		result = [self changePlayStatus:Pause];
		if (result != 0) goto EXIT;
	}
	else if (m_PlayStatus == Pause) {
		//演奏再開
		result = m_Sequencer.Resume();
		if (result != 0) goto EXIT;
		
		//演奏状態変更
		result = [self changePlayStatus:Play];
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 停止ボタン押下
//******************************************************************************
- (void)onStopButton:(NSNotification*)pNotification
{	
	//NSLog(@"MIDITrailApp::onStopButton");
	
	if ((m_PlayStatus == Play) || (m_PlayStatus == Pause)) {
		//演奏停止要求
		m_Sequencer.Stop();
		
		//演奏状態通知が届くまで再生中とみなす
		//ここでは演奏状態を変更しない
		
		//終了後に巻き戻す
		m_isRewind = true;
	}
	
	return;
}

//******************************************************************************
// 後方スキップボタン押下
//******************************************************************************
- (void)onSkipBackwardButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onSkipBackwardButton");
	
	result = m_Sequencer.Skip((-1) * m_SkipBackTimeSpanInMsec);
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 前方スキップボタン押下
//******************************************************************************
- (void)onSkipForwardButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onSkipForwardButton");
	
	result = m_Sequencer.Skip((+1) * m_SkipForwardTimeSpanInMsec);
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 再生スピードダウンボタン押下
//******************************************************************************
- (void)onPlaySpeedDownButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onPlaySpeedDownButton");
	
	//演奏状態確認
	if ((m_PlayStatus == Stop) || (m_PlayStatus == Play) || (m_PlayStatus == Pause)) {
		//変更OK
	}
	else {
		//変更NG
		goto EXIT;
	}
	
	//演奏速度ダウン
	m_PlaySpeedRatio -= m_SpeedStepInPercent;
	
	//リミット
	if (m_PlaySpeedRatio < m_SpeedStepInPercent) {
		m_PlaySpeedRatio = m_SpeedStepInPercent;
	}
	
	//演奏速度設定
	m_Sequencer.SetPlaySpeedRatio(m_PlaySpeedRatio);
	m_pScene->SetPlaySpeedRatio(m_PlaySpeedRatio);
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 再生スピードアップボタン押下
//******************************************************************************
- (void)onPlaySpeedUpButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onPlaySpeedUpButton");
	
	//演奏状態確認
	if ((m_PlayStatus == Stop) || (m_PlayStatus == Play) || (m_PlayStatus == Pause)) {
		//変更OK
	}
	else {
		//変更NG
		goto EXIT;
	}	
	
	//演奏速度アップ
	m_PlaySpeedRatio += m_SpeedStepInPercent;
	
	//リミット 400%
	if (m_PlaySpeedRatio > m_MaxSpeedInPercent) {
		m_PlaySpeedRatio = m_MaxSpeedInPercent;
	}
	
	//演奏速度設定
	m_Sequencer.SetPlaySpeedRatio(m_PlaySpeedRatio);
	m_pScene->SetPlaySpeedRatio(m_PlaySpeedRatio);
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// リピートボタン押下
//******************************************************************************
- (void)onRepeatButton:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onRepeatButton");
	
	//リピート切り替え
	m_isRepeat = m_isRepeat ? false : true;
	
	//リピート状態設定
	[m_pMainViewCtrl setRepeatStatus:m_isRepeat];
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 演奏状態変更通知：一時停止
//******************************************************************************
- (void)onChangePlayStatusPause:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onChangePlayStatusPause");
	
	//演奏状態変更
	result = [self changePlayStatus:Pause];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 演奏状態変更通知：停止（演奏終了）
//******************************************************************************
- (void)onChangePlayStatusStop:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onChangePlayStatusStop");
	
	//演奏状態変更
	result = [self changePlayStatus:Stop];
	if (result != 0) goto EXIT;
	
	//視点保存
	result = [self saveViewpoint];
	if (result != 0) goto EXIT;
	
	//シーンに演奏終了を通知
	if (m_pScene != NULL) {
		result = [m_pMainViewCtrl scene_PlayEnd];
		if (result != 0) goto EXIT;
	}
	
	//ユーザーの要求によって停止した場合は巻き戻す
	if ((m_isRewind) && (m_pScene != NULL)) {
		m_isRewind = false;
		result = [m_pMainViewCtrl scene_Rewind];
		if (result != 0) goto EXIT;
	}
	//通常の演奏終了の場合は次回の演奏時に巻き戻す
	else {
		m_isRewind = true;
		//リピート有効なら再生開始
		if (m_isRepeat) {
			[self onPlayButton:nil];
			if (result != 0) goto EXIT;
		}
	}
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// モニタ開始イベント
//******************************************************************************
- (void)onStartMonitoring:(NSNotification*)pNotification
{
	int result = 0;
	
	//NSLog(@"MIDITrailApp::onStartMonitoring");
	
	//演奏状態確認
	if ((m_PlayStatus == NoData) || (m_PlayStatus == Stop) || (m_PlayStatus == MonitorOFF)) {
		//モニタ開始OK
	}
	else {
		//モニタ開始NG
		goto EXIT;
	}
	
	//メッセージキュークリア
	m_MsgQueue.Clear();
	
	//メインビュー初期化
	result = [m_pMainViewCtrl initialize:&m_MsgQueue];
	if (result != 0) goto EXIT;
	
	//シーン種別読み込み
	[self loadSceneType];
	
	//シーン生成
	result = [self createSceneWithType:m_SceneType SeqData:NULL];
	if (result != 0) goto EXIT;
	
	//シーン開始
	result = [m_pMainViewCtrl startScene:m_pScene isMonitor:YES];
	if (result != 0) goto EXIT;
	
	//ライブモニタ初期化
	result = m_LiveMonitor.Initialize(&m_MsgQueue);
	if (result != 0) goto EXIT;
	
	result = [self setMonitorPortDev:&m_LiveMonitor scene:m_pScene];
	if (result != 0) goto EXIT;
	
	//メインビュー表示
	[self openMainView];
	
	//シーンに演奏開始を通知
	result = [m_pMainViewCtrl scene_PlayStart];
	if (result != 0) goto EXIT;	
	
	//ライブモニタ開始
	result = m_LiveMonitor.Start();
	if (result != 0) goto EXIT;
	
	//演奏状態変更
	result = [self changePlayStatus:MonitorON];
	if (result != 0) goto EXIT;
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 非アクティブ状態遷移
//******************************************************************************
- (void)onApplicationWillResignActive
{
	int result = 0;
	
	if (m_PlayStatus == Play) {
		//演奏一時停止
		m_Sequencer.Pause();
		if (result != 0) goto EXIT;
		
		//演奏状態変更
		result = [self changePlayStatus:Pause];
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
}

//******************************************************************************
// バックグラウンド状態遷移
//******************************************************************************
- (void)onApplicationDidEnterBackground
{
	int result = 0;
	
	if (m_PlayStatus == Play) {
		//演奏一時停止
		m_Sequencer.Pause();
		if (result != 0) goto EXIT;
		
		//演奏状態変更
		result = [self changePlayStatus:Pause];
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	if (result != 0) YN_SHOW_ERR();
	return;
}

//******************************************************************************
// 設定ファイル初期化
//******************************************************************************
- (int)initConfFile
{
	int result = 0;
	
	//ユーザ設定初期化
	m_pUserConf = [[YNUserConf alloc] init];
	if (m_pUserConf == nil) {
		YN_SET_ERR(@"Program error.", 0, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// シーン種別読み込み
//******************************************************************************
- (void)loadSceneType
{
	NSString* pType = nil;
	
	//カテゴリ／セクション設定
	[m_pUserConf setCategory:MT_CONF_CATEGORY_VIEW];
	[m_pUserConf setSection:MT_CONF_SECTION_SCENE];
	
	//ユーザ設定値取得：シーン種別
	pType = [m_pUserConf strValueForKey:@"Type" defaultValue:@""];
	
	if ([pType isEqualToString:@"PianoRoll3D"]) {
		m_SceneType = PianoRoll3D;
	}
	else if ([pType isEqualToString:@"PianoRoll2D"]) {
		m_SceneType = PianoRoll2D;
	}
	else if ([pType isEqualToString:@"PianoRollRain"]) {
		m_SceneType = PianoRollRain;
	}
	else {
		m_SceneType = PianoRoll3D;
	}
	
	return;
}

//******************************************************************************
// 視点読み込み
//******************************************************************************
- (int)loadViewpoint
{
	int result = 0;
	MTScene::MTViewParamMap defParamMap;
	MTScene::MTViewParamMap viewParamMap;
	MTScene::MTViewParamMap::iterator itr;
	NSString* pSection = nil;
	NSString* pKey = nil;
	float param = 0.0f;
	
	//シーンからデフォルトの視点を取得
	m_pScene->GetDefaultViewParam(&defParamMap);
	
	//カテゴリ／セクション設定
	[m_pUserConf setCategory:MT_CONF_CATEGORY_VIEW];
	pSection = [NSString stringWithFormat:@"%@%@", MT_CONF_SECTION_VIEWPOINT, m_pScene->GetName()];
	[m_pUserConf setSection:pSection];
	
	//パラメータをユーザデフォルトから取得
	for (itr = defParamMap.begin(); itr != defParamMap.end(); itr++) {
		pKey = [NSString stringWithCString:(itr->first).c_str() encoding:NSASCIIStringEncoding];
		param = [m_pUserConf floatValueForKey:pKey defaultValue:(itr->second)];
		
		if (result != 0) goto EXIT;
		viewParamMap.insert(MTScene::MTViewParamMapPair((itr->first).c_str(), param));
	}
	
	//シーンに視点を登録
	m_pScene->SetViewParam(&viewParamMap);
	
EXIT:;
	return result;
}

//******************************************************************************
// 視点保存
//******************************************************************************
- (int)saveViewpoint
{
	int result = 0;
	MTScene::MTViewParamMap viewParamMap;
	MTScene::MTViewParamMap::iterator itr;
	NSString* pSection = nil;
	NSString* pKey = nil;
	
	//シーンから現在の視点を取得
	[m_pMainViewCtrl scene_GetViewpoint:&viewParamMap];
	
	//カテゴリ／セクション設定
	[m_pUserConf setCategory:MT_CONF_CATEGORY_VIEW];
	pSection = [NSString stringWithFormat:@"%@%@", MT_CONF_SECTION_VIEWPOINT, m_pScene->GetName()];
	[m_pUserConf setSection:pSection];
	
	//パラメータを設定ファイルに登録
	for (itr = viewParamMap.begin(); itr != viewParamMap.end(); itr++) {
		pKey = [NSString stringWithCString:(itr->first).c_str() encoding:NSASCIIStringEncoding];
		[m_pUserConf setFloat:itr->second forKey:pKey];
	}
	
	//視点が切り替えられたことをシーンに伝達
	[m_pMainViewCtrl scene_SetViewpoint:&viewParamMap];
	
EXIT:;
	return result;
}

//******************************************************************************
// プレーヤー設定読み込み
//******************************************************************************
- (int)loadPlayerConf
{
	int result = 0;
	MTConfFile confFile;
	int timeSpan = 400;
	
	result = confFile.Initialize(@"Player");
	if (result != 0) goto EXIT;
	
	//----------------------------------
	//リワインド／スキップ制御
	//----------------------------------
	result = confFile.SetCurSection(@"SkipControl");
	if (result != 0) goto EXIT;
	result = confFile.GetInt(@"SkipBackTimeSpanInMsec", &m_SkipBackTimeSpanInMsec, 10000);
	if (result != 0) goto EXIT;
	result = confFile.GetInt(@"SkipForwardTimeSpanInMsec", &m_SkipForwardTimeSpanInMsec, 10000);
	if (result != 0) goto EXIT;
	result = confFile.GetInt(@"MovingTimeSpanInMsec", &timeSpan, 400);
	if (result != 0) goto EXIT;
	
	//シーケンサにリワインド／スキップ移動時間を設定
	m_Sequencer.SetMovingTimeSpanInMsec(timeSpan);
	
	//----------------------------------
	//演奏スピード制御
	//----------------------------------
	result = confFile.SetCurSection(@"PlaybackSpeedControl");
	if (result != 0) goto EXIT;
	result = confFile.GetInt(@"SpeedStepInPercent", &m_SpeedStepInPercent, 1);
	if (result != 0) goto EXIT;
	result = confFile.GetInt(@"MaxSpeedInPercent", &m_MaxSpeedInPercent, 400);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// 表示効果反映
//******************************************************************************
- (int)updateEffect
{
	int result = 0;
	int value = 0;
	
	m_isEnablePianoKeyboard = false;
	m_isEnableRipple = false;
	m_isEnablePitchBend = false;
	m_isEnableStars = false;
	m_isEnableCounter = false;
	
	//表示項目の表示設定を取得
	[m_pUserConf setCategory:MT_CONF_CATEGORY_VIEW];
	[m_pUserConf setSection:MT_CONF_SECTION_DISPLAY];
	
	value = [m_pUserConf intValueForKey:@"PianoKeyboard" defaultValue:1];
	if (value == 1) m_isEnablePianoKeyboard = true;
	
	value = [m_pUserConf intValueForKey:@"Ripple" defaultValue:1];
	if (value == 1) m_isEnableRipple = true;
	
	value = [m_pUserConf intValueForKey:@"PitchBendMotion" defaultValue:1];
	if (value == 1) m_isEnablePitchBend = true;
	
	value = [m_pUserConf intValueForKey:@"Stars" defaultValue:1];
	if (value == 1) m_isEnableStars = true;
	
	value = [m_pUserConf intValueForKey:@"Counter" defaultValue:1];
	if (value == 1) m_isEnableCounter = true;
	
	//シーンに反映
	m_pScene->SetEffect(MTScene::EffectPianoKeyboard, m_isEnablePianoKeyboard);
	m_pScene->SetEffect(MTScene::EffectRipple, m_isEnableRipple);
	m_pScene->SetEffect(MTScene::EffectPitchBend, m_isEnablePitchBend);
	m_pScene->SetEffect(MTScene::EffectStars, m_isEnableStars);
	m_pScene->SetEffect(MTScene::EffectCounter, m_isEnableCounter);
	
EXIT:;
	return result;
}

//******************************************************************************
// 演奏状態変更
//******************************************************************************
- (int)changePlayStatus:(PlayStatus)status
{
	int result = 0;
	
	//演奏状態変更
	m_PlayStatus = status;
	
	//演奏状態設定
	[m_pMainViewCtrl setPlayStatus:status];
	
	//自動ロック抑止
	//  演奏中またはモニタ中は自動ロックを抑止する
	if ((status == Play) || (status == MonitorON)) {
		[UIApplication sharedApplication].idleTimerDisabled = YES;
	}
	else {
		[UIApplication sharedApplication].idleTimerDisabled = NO;		
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// メインビュー表示
//******************************************************************************
- (void)openMainView
{
	//ステータスバー消去
	[[UIApplication sharedApplication] setStatusBarHidden:YES];
	
	//メインビュー表示
	m_pMainViewCtrl.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
	[m_pTabBarCtrl presentModalViewController:m_pMainViewCtrl animated:YES];
	
	return;
}

//******************************************************************************
// メインビュー消去
//******************************************************************************
- (void)closeMainView
{
	//メインビュー消去
	m_pMainViewCtrl.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
	[m_pMainViewCtrl dismissModalViewControllerAnimated:YES];
	
	//ステータスバー表示
	[[UIApplication sharedApplication] setStatusBarHidden:NO];
	
	//ナビゲーションバーの配置を更新する
	//これを行わないとナビゲーションバーがステータスバーに重なってしまう
	[m_pFileNaviCtrl setNavigationBarHidden:YES];
	[m_pFileNaviCtrl setNavigationBarHidden:NO];
	[m_pMonitorNaviCtrl setNavigationBarHidden:YES];
	[m_pMonitorNaviCtrl setNavigationBarHidden:NO];
	
	return;
}

//******************************************************************************
// シーン生成
//******************************************************************************
- (int)createSceneWithType:(SceneType)type
				   SeqData:(SMSeqData*)pSeqData  //ライブモニタ時はNULL
{
	int result = 0;
	OGLDevice* pDevice = NULL;
	
	//シーン破棄
	if (m_pScene != NULL) {
		m_pScene->Release();
		delete m_pScene;
		m_pScene = NULL;
	}
	
	//シーンオブジェクト生成
	try {
		if (type == Title) {
			m_pScene = new MTSceneTitle();
		}
		else {
			//プレイヤ用シーン生成
			if (pSeqData != NULL) {
				if (type == PianoRoll3D) {
					m_pScene = new MTScenePianoRoll3D();
				}
				else if (type == PianoRoll2D) {
					m_pScene = new MTScenePianoRoll2D();
				}
				else if (type == PianoRollRain) {
					m_pScene = new MTScenePianoRollRain();
				}
			}
			//ライブモニタ用シーン生成
			else {
				if (type == PianoRoll3D) {
					m_pScene = new MTScenePianoRoll3DLive();
				}		
				else if (type == PianoRoll2D) {
					m_pScene = new MTScenePianoRoll2DLive();
				}		
				else if (type == PianoRollRain) {
					m_pScene = new MTScenePianoRollRainLive();
				}
			}
		}
	}
	catch (std::bad_alloc) {
		result = YN_SET_ERR(@"Could not allocate memory.", type, 0);
		goto EXIT;
	}
	
	if (m_pScene == NULL) {
		result = YN_SET_ERR(@"Program error.", type, 0);
		goto EXIT;
	}
	
	//シーンの生成	
	pDevice = [m_pMainViewCtrl getDevice];
	result = m_pScene->Create([m_pMainViewCtrl getView], pDevice, pSeqData);
	if (result != 0) goto EXIT;
	
	//保存されている視点をシーンに反映する
	if (type != Title) {
		result = [self loadViewpoint];
		if (result != 0) goto EXIT;
	}
	
	//表示項目読み込み
	[self updateEffect];
	
	//演奏速度設定
	m_pScene->SetPlaySpeedRatio(m_PlaySpeedRatio);
	
EXIT:;
	return result;
}

//******************************************************************************
// ポートデバイス登録
//******************************************************************************
- (int)setPortDev:(SMSequencer*)pSequencer
{
	int result = 0;
	NSString* pDevIdName = nil;
	
	//MIDI OUTの設定を取得
	[m_pUserConf setCategory:MT_CONF_CATEGORY_MIDI];
	[m_pUserConf setSection:MT_CONF_SECTION_MIDIOUT];
	pDevIdName = [m_pUserConf strValueForKey:@"PortA" defaultValue:@""];
	
	//ポートAに対するMIDI OUTでバイスを登録
	result = m_Sequencer.SetPortDev(0, pDevIdName);
	if (result != 0) goto EXIT;
	
EXIT:;
	return result;
}

//******************************************************************************
// MIDI IN モニタ情報登録
//******************************************************************************
- (int)setMonitorPortDev:(SMLiveMonitor*)pLiveMonitor
				   scene:(MTScene*)pScene
{
	int result = 0;
	NSString* pDevIdName = nil;
	NSString* pDevDisplayName = nil;
	int checkMIDITHRU = 0;
	bool isMIDITHRU = false;
	
	//--------------------------------------
	// MIDI IN
	//--------------------------------------
	//カテゴリ／セクション設定
	[m_pUserConf setCategory:MT_CONF_CATEGORY_MIDI];
	[m_pUserConf setSection:MT_CONF_SECTION_MIDIIN];
	
	//設定ファイルからユーザ選択デバイス名を取得してシーケンサに登録
	pDevIdName = [m_pUserConf strValueForKey:@"PortA" defaultValue:@""];
	checkMIDITHRU = [m_pUserConf intValueForKey:@"MIDITHRU" defaultValue:1];
	if (checkMIDITHRU > 0) {
		isMIDITHRU = true;
	}
	if ([pDevIdName length] > 0) {
		result = pLiveMonitor->SetInPortDev(pDevIdName, isMIDITHRU);
		if (result != 0) goto EXIT;
	}
	
	//シーンに MIDI IN デバイス名を登録
	pDevDisplayName = pLiveMonitor->GetInPortDevDisplayName(pDevIdName);
	result = pScene->SetParam(@"MIDI_IN_DEVICE_NAME", pDevDisplayName);
	if (result != 0) goto EXIT;	
	
	//--------------------------------------
	// MIDI OUT (MIDITHRU)
	//--------------------------------------
	//カテゴリ／セクション設定
	[m_pUserConf setCategory:MT_CONF_CATEGORY_MIDI];
	[m_pUserConf setSection:MT_CONF_SECTION_MIDIOUT];
	
	//設定ファイルからユーザ選択デバイス名を取得してシーケンサに登録
	pDevIdName = [m_pUserConf strValueForKey:@"PortA" defaultValue:@""];
	if (result != 0) goto EXIT;
	
	if (([pDevIdName length] > 0) && (isMIDITHRU)) {
		result = pLiveMonitor->SetOutPortDev(pDevIdName);
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	return result;
}

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


@end

