//
//  RecViewController.m
//  ZeoRec/Komadori
//
//  Created by AIG-Soft on 2011/03/02.
//  Copyright 2011 AIG-Soft. All rights reserved.
//

//#define TEST

#import "AppDelegate.h"
#import "RecViewController.h"
#import "RootViewController.h"
#import	"Setup.h"
#import "libs.h"
#import "click.h"
#import	"NowDate.h"
#import	"LimitedSymbol.h"
#import "IntervalRec.h"
#import	"PlaySound.h"
#import	"AntiSleep.h"
#import "Log.h"				// これはUSE_LOGにかかわらず読み込むこと
#import "main.h"
#import "avi.h"
#ifdef USE_BLUETOOTH
#import	"Bluetooth.h"
#endif
#ifdef USE_BATTERY
#import "Battery.h"
#import "BatteryCheck.h"
#endif
#ifdef USE_EXITDELAY
#import	"exitdelay.h"
#endif
#if defined(USE_ALARM)||defined(USE_NSHOT)
#import	"PostTimer.h"
#endif
#if defined(USE_ALARM)
#import	"RotationControl.h"
#import	"OneShotTimer.h"
#endif
#import "UIScreen+is568screen.h"
#import "UIActivityIndicatorView+show.h"

@interface RecViewController()
{
	AppDelegate *app;
	Setup *setup;
	IBOutlet	__weak	UIButton *btnBluetoothStatus;
}
@end

@implementation RecViewController

//
enum { // 下の*Yの要素番号
	Y_TATE=0
,	Y_YOKO
	//
,	MAX_Y
};
CGFloat labelY[MAX_Y],buttonY[MAX_Y],dateY[MAX_Y],buttonY2[MAX_Y],progressY[MAX_Y];
#ifdef USE_BATTERY
CGFloat percentY[MAX_Y];
#endif

//--------------------------------------

#ifdef USE_BATTERY
// 電池容量不足の時に呼び出される
-(void)batterTooLow:(id)sender
{
	printSelf();
	setLog(LOG_NAME_NOBATT);
	// 録画停止（画面も戻る）
	[self recEnd]; // この中から再度ここが呼ばれる可能性がある
}
#endif

//--------------------------------------
// 初期化
//--------------------------------------

// 初期化
// mainViewが必要なので、viewDidLoadに入れられない
// nib読み込み指示直後に呼び出す
// ここで設定する項目はdeallocで開放してはいけない
-(void)initRecView
{
	printSelf();

	// 変数初期化;viewDidLoadに移動しないこと
	// imgPicker=nilはしてはいけない;initImagePickerが誤動作する可能性があるから
	_frec=REC_NONE;	// 録画中でない
}

//--------------------------------------
// オーバーレイでは、これらは呼び出されない
// オーバーレイをやめてaddSubviewを使うことで呼び出されるようになった

// recViewControllerのnibが読み込み終わった(delegate)
// ここに来るときはオリジナル＝縦画面の座標で来る
-(void)viewDidLoad
{
	printSelf();
	[super viewDidLoad];
	
	// アドレス参照用
	app=getAppDelegate();
	setup=app.setup;
	nslog(@"setup=%@",setup);
	
	self.delegate=app.rootViewController;
	
	// オーバーレイの回転時座標を算出
	printFrame(@"buttonY"  ,_btnCancelTATE.frame);
	printFrame(@"dateY"    ,_labelNowDate.frame	);
	printFrame(@"buttonY2" ,_btnCameraFR.frame	);
#if defined(USE_ALARM)
	printFrame(@"labelY"   ,_labelAlarm.frame	);
#endif
#ifdef USE_BATTERY
	printFrame(@"progressY",_batteryMeter.frame	);
	printFrame(@"percentY" ,_labelPercent0.frame);
#endif
#ifdef KOMADORI
	// こまどり君のときは不要な要素を消す
	// RecViewControllerを共通化するため
	_labelDate.hidden=YES;
#endif
#if defined(USE_ALARM)
	_labelAlarm.hidden=NO;
#else
	_labelAlarm.hidden=YES;
#endif
#ifndef USE_NSHOT
	_btnRecNShot.hidden=YES;	// NShotがないときはボタンも無効
#endif
#ifndef USE_BATTERY
	_batteryMeter.hidden=
	_labelPercent0.hidden=
	_labelPercent100.hidden=YES;
#endif
	CGFloat width =self.view.bounds.size.width;
	CGFloat height=self.view.bounds.size.height;
	nslog(@"width=%f,height=%f",width,height);
	CGRect frame;
	// 下寄せ要素はマイナス、上寄せ要素はプラスする
	frame =_labelDate.frame;		// _labelAlarmと_labelDateのyは同じとする
	labelY[Y_TATE]=frame.origin.y;
	labelY[Y_YOKO]=labelY[Y_TATE]-(height-width)/2;			// 縦横の幅の差を上下に分けるから/2
	nslog(@"labelY[Y_TATE]=%f",labelY[Y_TATE]);
	nslog(@"labelY[Y_YOKO]=%f",labelY[Y_YOKO]);
	//
	frame =_btnCancelTATE.frame; // ボタンのyはすべて同じとする
	buttonY[Y_TATE]=frame.origin.y;
	buttonY[Y_YOKO]=buttonY[Y_TATE]-(height-width)/2;
	nslog(@"buttonY[Y_TATE]=%f",buttonY[Y_TATE]);
	nslog(@"buttonY[Y_YOKO]=%f",buttonY[Y_YOKO]);
	//
	frame =_labelNowDate.frame;
	dateY[Y_TATE]=frame.origin.y;
	dateY[Y_YOKO]=dateY[Y_TATE]-(height-width)/2;			// 縦横の幅の差を上下に分けるから/2
	nslog(@"dateY[Y_TATE]=%f",dateY[Y_TATE]);
	nslog(@"dateY[Y_YOKO]=%f",dateY[Y_YOKO]);
	//
#ifdef USE_BATTERY
	frame =_labelPercent0.frame;
	percentY[Y_TATE]=frame.origin.y;
	percentY[Y_YOKO]=percentY[Y_TATE]-(height-width)/2;		// 縦横の幅の差を上下に分けるから/2
	nslog(@"percentY[Y_TATE]=%f",percentY[Y_TATE]);
	nslog(@"percentY[Y_YOKO]=%f",percentY[Y_YOKO]);
	//
	progressY[Y_TATE]=_batteryMeter.frame.origin.y;
	progressY[Y_YOKO]=progressY[Y_TATE]-(height-width)/2;	// 縦横の幅の差を上下に分けるから/2
	nslog(@"progressY[Y_TATE]=%f",progressY[Y_TATE]);
	nslog(@"progressY[Y_YOKO]=%f",progressY[Y_YOKO]);
#endif
	//
	frame =_btnCameraFR.frame;
	buttonY2[Y_TATE]=frame.origin.y;
	buttonY2[Y_YOKO]=buttonY2[Y_TATE]+(height-width)/2;		// 縦横の幅の差を上下に分けるから/2
	nslog(@"buttonY2[Y_TATE]=%f",buttonY2[Y_TATE]);
	nslog(@"buttonY2[Y_YOKO]=%f",buttonY2[Y_YOKO]);
	//
	// 各クラス初期化
	nowDate     =[[NowDate       alloc]initWithLabel:_labelNowDate target:self selector:@selector(isntRecOn:)];
	alertSymbol =[[LimitedSymbol alloc]initWithImageView:imageAlert];
	antiSleep   =[[AntiSleep     alloc]initWithTarget:self selector:@selector(antiSleepMain:)];
	intervalRec =[[IntervalRec   alloc]initWithTarget:self selector:@selector(rec1Picture:)];
#ifdef USE_BATTERY
	batteryCheck=[[BatteryCheck  alloc]initWithLabels:_batteryMeter label0:_labelPercent0 label100:_labelPercent100
											   target:self selector:@selector(batterTooLow:)];
#endif
#if	defined(USE_ALARM)||defined(USE_NSHOT)
	postTimer   =[[PostTimer     alloc]initWithTarget:self selector:@selector(endOfAlarm:)];
#endif
#if defined(USE_ALARM)
	alarmLimit	=[[OneShotTimer	 alloc]initWithTarget:self selector:@selector(limitOfAlarm:)];
#endif
	///
	if ([UIScreen is568screen]) {
		nslog(@"*568画面の時はボタン類を下げる");
		// 本当はボタン下領域(が黒ベタになるので)に何か置きたいが、回転時に面倒なので、今回は省略
		
		CGRect frame=self.view.frame;
		frame.size.height=568;
		self.view.frame=frame;

		CGFloat dSize=(568.0-480.0);
		labelY[ Y_TATE]	+=dSize;
		buttonY[Y_TATE]	+=dSize;
		dateY[  Y_TATE]	+=dSize;
#ifdef USE_BATTERY
		percentY[ Y_TATE]+=dSize;
		progressY[Y_TATE]+=dSize;
#endif
		/// 横画面用も補正しなければならない
		/// なぜならば、画面回転のtransformは320*568で行われるため、ボタンが上に上がってしまうので
		dSize/=2; /// 正しくないかもしれないがこれで実位置が良さげなるのでok
		labelY[ Y_YOKO]	+=dSize;
		buttonY[Y_YOKO]	+=dSize;
		dateY[  Y_YOKO]	+=dSize;
		buttonY2[Y_YOKO]+=dSize; // 568画面ではこれも移動
#ifdef USE_BATTERY
		percentY[ Y_YOKO]+=dSize;
		progressY[Y_YOKO]+=dSize;
#endif
		nslog(@"578画面補正座標");
		nslog(@"labelY[Y_TATE]=%f"		,labelY[Y_TATE]);
		nslog(@"buttonY[Y_TATE]=%f"		,buttonY[Y_TATE]);
		nslog(@"dateY[Y_TATE]=%f"		,dateY[Y_TATE]);
#ifdef USE_BATTERY
		nslog(@"percentY[Y_TATE]=%f"	,percentY[Y_TATE]);
#endif
		nslog(@"progressY[Y_TATE]=%f"	,progressY[Y_TATE]);
		nslog(@"buttonY2[Y_TATE]=%f"	,buttonY2[Y_TATE]);
	}
	// とりあえず縦方向で画像サイズを設定
	[self.delegate setImageSize:ImageSizeTATE];
	
	// 中央に置く
	indicatorView.center=imageAlert.center;
	
	//
	// 578画面への対応のみ
//	[self rotationOverlay:UIDeviceOrientationPortrait fall:YES fTranfsorm:NO];
}

-(void)viewDidAppear:(BOOL)animated
{
	printSelf();
	[super viewDidAppear:animated];
	[self rotationOverlay:[UIDevice currentDevice].orientation fall:YES fTranfsorm:YES];
}

// カメラFRボタンの表示設定
-(void)btnCameraFRSetting
{
	printSelf();
	// カメラ切り替えが無効の時は切り替えボタンそのものを消す
	if (CheckCameraDevice(UIImagePickerControllerCameraDeviceFront)!=UIImagePickerControllerCameraDeviceFront) {
		nslog(@"非表示");
		_btnCameraFR.hidden=YES;
	} else {
		nslog(@"表示");
		_btnCameraFR.hidden=NO;
	}
}

// ボタンの設定
-(void)setButtons
{
	printSelf();
	BOOL f=(_frec==REC_PREPARE||_frec==REC_EXITDELAY);
	_btnCancelTATE.hidden=_btnRecStartTATE.hidden=(!f);
	_btnRecEndTATE.hidden=f;
	if (_frec==REC_ON1SHOT) {
		_btnRec1Shot.hidden=NO;
	} else {
		_btnRec1Shot.hidden=YES;
	}
#if defined(USE_NSHOT)
	if (!f && [setup isSettingNShot]) {
		_btnRecNShot.hidden=NO;
	} else {
		_btnRecNShot.hidden=YES;
	}
#endif
}

-(void)viewWillAppear:(BOOL)animated
{
	printSelf();
	[super viewWillAppear:animated];
	
	// ボタンの表示変更はこの位置でしないとダメ
	[self setButtons]; 

	// 初期表示
	[nowDate displayWithColor];	// 現在時刻色で表示
	_labelDate.text=_labelNowDate.text; // 現在日時の表示＝録画に入れた時間が初期表示
	//
	[nowDate start];		// 現在日時表示用タイマー起動
#ifdef USE_BATTERY
	[batteryCheck start];	// 電池チェック開始
#endif
#if defined(USE_ALARM)
	labelAlarmDisplay(_labelAlarm,EVENT_NONE);
#endif
	[self btnCameraFRSetting]; // カメラFRボタンの表示設定
#ifdef USE_BLUETOOTH
	if (bluetooth.fconnect==BT_CONNECT_OK) {
		btnBluetoothStatus.hidden=NO;	// 通信中マーク表示
	} else {
		btnBluetoothStatus.hidden=YES;	// 通信中マーク消去
	}
#endif
}

//--------------------------------------
// Bluetooth
//--------------------------------------

#ifdef USE_BLUETOOTH
// コマンド受信関数
-(void)bluetoothDidRecieve:(NSData *)data peer:(MCPeerID *)peerID
{
	printSelf();
	unichar c=[bluetooth readReceiveBuff];
	nslog(@"com=%c($%04x)",c,c);
#ifdef TEST
	Alert1(@"受信",[NSString stringWithFormat:@"com=%c($%04x)",c,c],nil);
#else
	switch (c) {
		case BT_COMMAND_NSHOT:
			nslog(@"NSHOTシャッターON");
#ifdef USE_NSHOT
			if ([setup isSettingNShot]) {
				nslog(@"NShot実行");
				// Nショット
				[self recStartNShot:nil];
				break;
			}
#endif
			// NShotの設定がないときは普通のシャッターと同じに振る舞う
			
		case BT_COMMAND_SHUTTER:
			nslog(@"シャッターON");
#ifdef USE_1SHOT
			if ([setup isSetting1Shot]) {
				nslog(@"1ショット実行");
				// 1ショット
				[self recStart1Shot:nil];
			}
	#ifdef USE_NSHOT
			else { // 1ショットが無効の時
				nslog(@"1ショットは無効の時");
				if ([setup isSettingNShot]) { // nショットがあればnショットとして使う
					nslog(@"NShot実行");
					// Nショット
					[self recStartNShot:nil];
					break;
				}
			}
	#endif
#endif
#if defined(USE_ALARM)
			else {
				if ([setup getTmValues:TMAlarmInterval]!=None)
				{
					nslog(@"アラーム");
					// アラーム ok
					[self Alarm:@"Bluetooth Shutter"];
				} // アラームない(常時録画のみの時)ときは無視する
			}
#endif
			break;
		case BT_COMMAND_RECEND:
			nslog(@"録画停止");
			switch (_frec) {
				case REC_PREPARE: // 録画準備中
					[self recCancel:nil]; // この中でクリック音がなる
					break;
				case REC_ON1SHOT:
				case REC_ON:
					[self recEnd]; // この中でクリック音がなる
					break;
			}
			break;
	}
#endif
	// 応答を返す
	[bluetooth send:BT_COMMAND_RESULT];
}

// Bluetooth処理中処理
// ここは、bluetoothクラス内から呼び出されるため、この中での送受信はできない
// ここはメインスレッドではないので注意
- (void)bluetoothDidConnected:(Bluetooth *)bt
{
	printSelfs(@"fconnect=%d",bt.fconnect);
	switch (bt.fconnect) {
		case BT_CONNECT_OK:
			nslog(@"Bluetooth相互接続完了");
//			btnBluetoothStatus.hidden=NO; // 通信中マーク表示
			[self performSelectorOnMainThread:@selector(setImageBluetoothInMainThread:) withObject:@(NO) waitUntilDone:NO];
			break;
		case BT_CONNECT_CANCEL:
			nslog(@"Bluetoothキャンセル");
			[self disconnectBluetooth:NO]; // close処理は走らない
		default:
			break;
	}
}

// Bluetooth接続時は接続解除
-(void)disconnectBluetooth:(BOOL)fwithCommand
{
	if (bluetooth!=nil) {
		nslog(@"Bluetooth実接続解除");
		if (fwithCommand) {
			[bluetooth send:BT_COMMAND_DISCONNECT]; // 接続解除命令を発行
		}
		// 接続開放の印をつけるだけ；インスタンス自体はそのままにしておく
		bluetooth.fconnect=BT_CONNECT_NONE;
//		btnBluetoothStatus.hidden=YES; // 通信中マーク消去
		[self performSelectorOnMainThread:@selector(setImageBluetoothInMainThread:) withObject:@(YES) waitUntilDone:NO];
	}
}

#ifdef USE_BLUETOOTH
-(IBAction)buttonBluetooth:(id)sender
{
	printSelf();
	if (bluetooth.fconnect!=BT_CONNECT_NONE) {
		// 接続時は接続終了
		nslog(@"Bluetooth接続解除ボタン押下");
		[self disconnectBluetooth:YES];
	}
}
#endif

-(void)setImageBluetoothInMainThread:(NSNumber *)num
{
	printSelf();
	BOOL flag=[num boolValue];
	btnBluetoothStatus.hidden=flag;
}


// Bluetooth設定
// 外部から呼び出される
-(void)setBluetooth:(Bluetooth *)bluetooth_
{
	printSelf();
	bluetooth=bluetooth_;
	bluetooth.delegate=self;
}

// コマンド送信
-(void)sendCommand:(unichar)cmd
{
	if (bluetooth.fremote==BT_REMOTE_NO) { // レコーダーモード時
		[bluetooth send:cmd];
	}
}

-(void)bluetoothSelectShow:(MCBrowserViewController *)mvc
{
	[self presentViewController:mvc animated:YES completion:nil];
}

#endif	// USE_BLUETOOTH

//--------------------------------------
//
//--------------------------------------

// ImagePicker初期化
// viewDidLoadより先に呼び出される可能性もあることに注意
-(UIImagePickerController *)startImagePicker
{
	printSelf();
	app=getAppDelegate();
	setup=app.setup;
	nslog(@"setup=%@",setup);

	imgPicker=[[UIImagePickerController alloc]init]; // 実体
	nslog(@"imgPicker=%@",imgPicker);
#ifdef DEBUG
	if (imgPicker==nil) {
		nslog(@"imgPickerが確保できない");
	}
#endif
	imgPicker.sourceType		= UIImagePickerControllerSourceTypeCamera; // カメラを使う
	// 以下↓の設定は、↑この設定の後でなければならない
	nslog(@"setup=%@",setup);
	imgPicker.delegate			= self;
	imgPicker.cameraDevice		= setup.cameraDevice;
	imgPicker.cameraFlashMode	= setup.cameraFlashMode;
	// 静止画は常に使えるので確認不要
	imgPicker.cameraCaptureMode	= UIImagePickerControllerCameraCaptureModePhoto; // 静止画
	imgPicker.showsCameraControls	= NO; // デフォルトのカメラコントロールを非表示にする
	// オーバーレイをやめる:addSubviewしている(Root)
//	imgPicker.cameraOverlayView	= self.view;
	nslog(@"startImagePicker:ftakePicture=TAKE_PICTURE_ENABLE");
	ftakePicture=TAKE_PICTURE_ENABLE;	// 最初は撮影可能
	return (imgPicker); // アドレスのみ返す
}

-(void)closeImgPicker
{
	printSelf();
	if (isJIKKI()) {
///		[imgPicker dismissModalViewControllerAnimated:YES];
		[imgPicker dismissViewControllerAnimated:YES completion:NULL];
		imgPicker=nil;
	}
}

//--------------------------------------
// １枚録画
//--------------------------------------

// 撮像終了処理(delegate)
// iOSが写真をしたら並行動作で呼び出され、この中を走っている間、メインも走り続ける
-(void)imagePickerController:(UIImagePickerController *)picker_ didFinishPickingMediaWithInfo:(NSDictionary *)info
{
	if (ftakePicture==TAKE_PICTURE_ANTI_SLEEP) {
		nslog(@"Sleep回避のためのダミー撮影だった");
		// Anti-Sleepタイマーはそのままリピート
		clickSound(); // 区別できるようにクリック音を鳴らす
	} else {
		nslog(@"imagePickerController(撮像終了処理)");
		// 撮影後処理
		// 日時(←ラベル経由でコピー),イベント名
		NSString *nameEvent=@""; // 警告回避のため
		switch (fevent) {
		#ifdef DEBUG
			default:
				nslog(@"異常：fevent=%d",(int)fevent);
				break;
		#endif
		#ifdef USE_1SHOT
			case EVENT_1SHOT:
				nameEvent=NSLocalStr(@"1SHOT");
				break;
		#endif
		#ifdef USE_CONSTANT
			case EVENT_CONSTANT:
				nameEvent=NSLocalStr(CONSTANT); // ZeroRec/Komadoriで変わる
				break;
		#endif
		#if defined(USE_ALARM)||defined(USE_NSHOT)
			case EVENT_ALARM:
			case EVENT_ALARM1:
				#if defined(USE_NSHOT)
				if (leftPic==0) {
					nslog(@"算出ポスト時間ではなぜか+1枚録画されることがあるので回避");
					goto IPC1;
				}
				#endif
				nameEvent=NSLocalStr(ALARM);
				break;
				// EVENT_NONEではここに来ない
		#endif
		}
		//
		NSString *mes=[[NSString alloc]initWithFormat:@"%@ %@",_labelDate.text,nameEvent];
		nslog(@"mes=%@",mes);
//
		nslog(@"----------delegate AVIfileWrite");
		[self.delegate AVIfileWrite:info[UIImagePickerControllerOriginalImage] text:mes];
//

		// 枚数/回数は実取得終了時にカウントする
		nslog(@"枚数/回数は実取得終了時にカウントする");
		switch (fevent) {
		#ifdef USE_1SHOT
			case EVENT_1SHOT:
				nslog(@"1Shotで");
				// 1shotは常時録画の枚数を使う
			// 1SHOTでも使うので、USE_CONSTANTによらず処理を有効にする
		#endif
		#ifdef USE_CONSTANT
			case EVENT_CONSTANT:
				nslog(@"常時画像取得終了");
				[setup incPictures:PICTURE_CONSTANT];
				break;
		#endif
		#if defined(USE_ALARM)||defined(USE_NSHOT)
			case EVENT_ALARM1:
				// アラーム1枚ではポスト処理が走ってないので、撮影後に強制的にポスト終了後処理を走らせる必要がある
				nslog(@"アラーム1枚ではポスト処理が走ってないので、撮影後に強制的にポスト終了後処理を走らせる必要がある");
				ftakePicture=TAKE_PICTURE_ENABLE; // アラーム1枚ではこの時点ですでにキャプチャー終了している
				[postTimer startYokushi:[setup getTmValues:TMAlarmYokushi]];
			case EVENT_ALARM:
				nslog(@"アラーム画像取得終了");
				[setup incPictures:PICTURE_ALARM];
#if defined(USE_NSHOT)
				leftPic--;
				[self printLeftPic];
#endif
				break;
		#endif
		}
	} // if (ftakePicture)
#ifdef USE_NSHOT
IPC1:;
#endif
	// 次の撮影可能
//	[imgPicker.view setNeedsDisplay]; // これでもだめ!!
	nslog(@"imagePickerController:ftakePicture=TAKE_PICTURE_ENABLE");
	ftakePicture=TAKE_PICTURE_ENABLE;
	//
	nslog(@"imagePickerController end");
}

// Anti-Sleep用撮影命令処理
// 充電中はスリープがかからないが、電池駆動中は絶対に5分前にスリープがかかるので、それの解除用
-(void)antiSleepMain:(id)sender
{
	printSelf();
	ftakePicture=TAKE_PICTURE_ANTI_SLEEP;
	[imgPicker takePicture];
}

// 1枚録画開始
// fevent=EVENT*;常時録画,アラームのポストの期間内,１ショット録画
-(void)rec1Picture:(id)sender
{
	printSelf();
	if (checkDeviceOrientationOK()==NO) {
		nslog(@"上下向きなので録画禁止（盗撮防止）");
		[alertSymbol display:TM_ALERT_DISPLAY];
		return;
	}
	
	// 撮像日時/イベントの表示＆保存
	_labelDate.text=_labelNowDate.text; // 現在日時の表示＝録画時間
	nslog(@"_labelDate=%@/_labelNowDate=%@",_labelDate.text,_labelNowDate.text);

	// 最新日時を記録
	// copyを使うので、前の領域を解放する必要がある
	if (setup.latestDate!=nil) {
		setup.latestDate=nil;
	}
	setup.latestDate=[_labelDate.text copy];
	
#if defined(USE_ALARM)
	setup.latestEvent=labelAlarmDisplay(_labelAlarm,fevent);
#endif
	
	// 画質設定
	// AVIでは、ここで設定する画質は実録画時に設定されるもののテーブルの変更のみなので、処理中の画像には影響しない
	// takePictureまでの待ち時間を長くすることで、前の画像取得処理が終わっている期待値を高める
	switch (fevent) {
	#if defined(USE_ALARM)||defined(USE_NSHOT)
		case EVENT_ALARM:
		case EVENT_ALARM1:
			nslog(@"<アラーム録画>");
			[self.delegate setQuality:tblQuality[setup.alarmQuality]];
			break;
	#endif
	#ifdef USE_1SHOT
		case EVENT_1SHOT:
			nslog(@"<1ショット>で");
			// 1shotは常時録画の画質を使う
		// 1SHOTでも使うので、USE_CONSTANTによらず処理を有効にする
	#endif
	#ifdef USE_CONSTANT
		case EVENT_CONSTANT:
			nslog(@"<常時録画>");
			[self.delegate setQuality:tblQuality[setup.constantQuality]];
			break;
	#endif
	}

	if (isSIMULATOR()) return; // シミュレーターではここまで
	// 以下実機のみ

	// 撮影
	switch (ftakePicture) { // 現在の待ち状態を確認
		case TAKE_PICTURE_ANTI_SLEEP: // AntiSleep用撮影待ち中
			// それを通常撮影に切り替える
			nslog(@"takePicture:AntiSleepから差し替え");
			ftakePicture=TAKE_PICTURE_INTERVAL; // 通常撮影に変更;解除はdidFinishPickingMediaWithInfo
			return;
		case TAKE_PICTURE_INTERVAL: // 撮影中
			// 前回の撮影指示がまだ終了していないときは、前の撮影をキャンセルして今回のキャンセルにしてしまう
			// どのみち多重処理は出来無いのこれで構わない
			// 多重撮影命令発行をすると
			// "ignoring request to take picture; image is already being captured or camera not yet ready."が出てしまう
			nslog(@"takePicture:前回の撮影を差し替え");
			return;
		default: // 警告回避用
			break;
	}
	nslog(@"ftakePicture=TAKE_PICTURE_INTERVAL");
	ftakePicture=TAKE_PICTURE_INTERVAL; // 次の撮影を不可にしてから撮影;解除はdidFinishPickingMediaWithInfo
	[antiSleep reset];	// Anti-Sleepタイマーリセット
	nslog(@"takePicture");
	[imgPicker takePicture];		// 静止画撮影
	// この先マルチタスクになる＝実撮影タイミングはここではない
}

//--------------------------------------
// 録画チェックタイマー
//--------------------------------------

// 録画時間内に入った/出るときの処理
-(BOOL)recActive:(NSUInteger)factive fafterAlarm:(BOOL)fafterAlarm
{
	nslog(@"recActive:fafterAlarm=%d",fafterAlarm);
	if (factive==YES) {
		nslog(@"録画時間開始%@",fafterAlarm? @":アラーム後":@"");
		[nowDate setColor]; // 現在時刻色設定
		if (!fafterAlarm) {
	#if defined(USE_ALARM)||defined(USE_NSHOT)
			// アラーム検知を開始する
			if ([setup isAlarmRec]) { // アラーム設定がある(1shotのときは=Noneなので通らない)
				// アラームセンサON
				[self AlarmSensor:YES];
			}
	#endif
			// ファイルオープン
			[self.delegate AVIfileOpen];
		}
		// else { // アラーム直後
			// アラームはすでに動いているので再設定不要
			// ファイルもオープンされているので再オープン不要
		//　}

		// 常時録画用インターバルを開始する;この中で即rec1Pictureが呼び出されるようなので、先にファイルを開いておく
		fevent=EVENT_CONSTANT; // システムとして常時録画がないときも普段はアラーム以外にしておかなければならない
	#ifdef USE_CONSTANT
		NSTimeInterval tm=[setup getTmValues:TMConstantInterval];
	#endif
	#ifdef KOMADORI
		if ([setup isSetting1Shot]) tm=None; // Komadoriの１Shot指定の時は、インターバルなし
	#endif
		// 次からのインターバル設定(interval=Noneのときはタイマーは設定されない)
		if ([intervalRec start:tm]) {
			// 常時録画設定があるとき
			// 最初の１枚を撮影
			[self rec1Picture:nil];
		}
	} else { // factive==NO
		// 録画時間を抜ける
		nslog(@"録画時間終了");
		[nowDate setColor]; // 現在時刻色設定
	#if defined(USE_ALARM)||defined(USE_NSHOT)
		// アラーム発報を止める
		if ([setup isAlarmRec]) { // アラーム設定がある
			// アラームセンサOFF
			[self AlarmSensor:NO];
			// これで新規発報は抑えられるが、現在アラームポスト中の時は、それは完遂させる。だからfeventはここではEVENT_NONEにしない
			// fevent=EVENT_NONE;
			// タイマーOFFにかかったときはリトリガーは効かない(V1.0の仕様)
		}
	#endif
		[self.delegate AVIfileClose];	// ファイルクローズ
		[intervalRec stop];			// 録画用インターバルを停止する
	} // factive
	return (factive);
}

-(BOOL)recActive:(NSUInteger)factive
{
	return [self recActive:factive fafterAlarm:NO];
}


#ifdef USE_TIMER
// 5分に1回のタイマーのチェック；通常タイマーチェック間隔
-(void)checkRecTimer5min:(id)sender
{
	nslog(@"checkTimer5min:sender=%@",sender);
#if defined(USE_ALARM)
	if (fnowActiveTimer) {
		// 有効期間中
		switch (fevent) {
			case EVENT_ALARM:
			case EVENT_ALARM1:
				// アラームポスト中(endOfAlarmを呼び出すまで)はタイマー(終了)チェックを延期する
				nslog(@"アラーム中なのでタイマーチェック延期");
				// ただし、アラームポスト終了直後に再度呼び出すこと
				return;
		}
	}
#endif
	BOOL factive=[setup checkRecTimer];
	nslog(@"factive=%d:%d",factive,fnowActiveTimer);
	if (factive!=fnowActiveTimer || sender==nil) {
		// 状態が変化した / 初期起動時は強制設定
		fnowActiveTimer=[self recActive:factive]; // 状態を記録
	} // 状態変化なしの時は何もしない
}

-(void)stopRecTimer
{
	printSelf();
	if (recTimer != nil)  {
		[recTimer invalidate];
		recTimer=nil;
	}
}

// 録画タイマーチェックを起動する
-(void)startCheckRecTimer
{
	printSelf();
	[self stopRecTimer]; // 前のタイマーがあれば停止する
	// 現在の時刻を得る
	NSDate *date=[NSDate date];
	NSUInteger sec;
	NSUInteger minute=getHourMinSec(date,NULL,&sec);
	nslog(@"現在(min:sec)=%d:%d",(int)minute,(int)sec);
	// 次の5分単位までの差分を取る
	minute=((minute+MIN_STEP-1)/MIN_STEP)*MIN_STEP-minute;
	NSTimeInterval tm=tMin(minute);
	if (minute>=1) {
		// 1分以上の時は、最後の1分は秒単位で調整
		tm-=((NSTimeInterval)sec);
	}
	nslog(@"現在からの差分：%d分;%f秒",(int)minute,tm);
	// minute==0のときはすぐ起動
	
	// 差分後のタイマーを作る
	NSDate *nextDate=[NSDate dateWithTimeIntervalSinceNow:tm]; // 現在の日時から指定秒後
	nslog(@"次タイマー：%@(%f)",nextDate,tm);
	// そのタイマーをインスタンスに生成する
	nslog(@"recTimer=%@",recTimer);
	recTimer = [[NSTimer alloc]initWithFireDate:nextDate
									   interval:tMin(MIN_STEP)
                                         target:self
                                       selector:@selector(checkRecTimer5min:)
                                       userInfo:nil
                                        repeats:YES]; // 録画タイマーチェックタイマーはリピート
#ifdef DEBUG
	nslog(@"fireDate=%@",[recTimer fireDate]);
	if (recTimer == nil) {
		// これをチェックするなら、本当は例外を発生させる必要がある
		nslog(@"タイマー起動失敗");
	}
#endif
	AddTimerToRunLoop(recTimer); // 実行ループにタイマーを追加する
}
#endif	// USE_TIMER


#if defined(USE_ALARM)||defined(USE_NSHOT)
// アラーム直後のタイマーチェック再開
-(void)checkRecTimer5minAfterAlarm
{
	printSelf();
#ifdef USE_TIMER
	BOOL factive=[setup checkRecTimer];
#else
	BOOL factive=YES; // タイマーがないときは常に即時録画ON
#endif
	nslog(@"factive=%d:%d",factive,fnowActiveTimer);
	fnowActiveTimer=[self recActive:factive fafterAlarm:YES]; // 状態を記録
}
#endif

//--------------------------------------
// 画面回転実処理
//--------------------------------------

// 回転&Rec状態別ボタン設定
// PickerView表示中は画面上のViewは(オーバーレイを含めすべて)回転しないので、
// 変形を使って回転しているように見せかけている
// 一旦録画を開始すると回転させない（録画中に画像の縦横サイズを変更できないため）
-(void)rotationOverlay:(UIDeviceOrientation)orientation fall:(BOOL)fall fTranfsorm:(BOOL)fTranfsorm
{
	printSelfs(@"%d",(int)orientation);
	CGAffineTransform transform=CGAffineTransformIdentity; // 元に戻す
	if (fTranfsorm) {
		// 一旦変形をキャンセル
		self.view.transform=transform;
		if (_frec!=REC_PREPARE) {
			nslog(@"録画準備中以外は無視");
			return;
		}
	}
	// 縦横を設定できるのは準備中のみ
	// 録画を開始したら固定される(AVI内では画像幅の変更ができないから)
	//
	NSUInteger y;
	switch (orientation) {
		default: // 上向き(=5)、下向き(=6)、不明(=0)の時に此処に来る
			if (!fall) return; // この向きは処理しない
			// 一応縦化する
			goto Portrait;
		//
		case UIDeviceOrientationPortraitUpsideDown: // =2
			nslog(@"UIDeviceOrientationPortraitUpsideDown");
			if (fTranfsorm) transform=CGAffineTransformRotate(transform,M_PI);
			goto Portrait;
		case UIDeviceOrientationPortrait: // =1
			nslog(@"UIDeviceOrientationPortrait");
		Portrait:;
			// 画像サイズを設定
			[self.delegate setImageSize:ImageSizeTATE];
			y=Y_TATE;
			nslog(@"オーバーレイ縦設定:%d",(int)orientation);
			break;
		// 横
		case UIDeviceOrientationLandscapeLeft: // =3
			nslog(@"UIDeviceOrientationLandscapeLeft");
			if (fTranfsorm) transform=CGAffineTransformRotate(transform,M_PI/2.0);
			goto LandscapeButtons;
		case UIDeviceOrientationLandscapeRight: // =4
			nslog(@"UIDeviceOrientationLandscapeRight");
			if (fTranfsorm) transform=CGAffineTransformRotate(transform,3.0*M_PI/2.0);
		LandscapeButtons:
			// 画像サイズを設定
			[self.delegate setImageSize:ImageSizeYOKO];
			y=Y_YOKO;
			nslog(@"オーバーレイ横設定:%d",(int)orientation);
			break;
	}
	if (fTranfsorm) {
		nslog(@"縦横変形");
		self.view.transform=transform;
	}
	
	// frameは構造体なので、~.frame.origin.x=?という式が使えない。そのためこんなまどろっこしい書き方をしている
	nslog(@"labelY=%f,buttonY=%f,dateY=%f,buttonY2=%f",labelY[y],buttonY[y],dateY[y],buttonY2[y]);
	CGRect frame;
	frame=_labelDate.frame;			frame.origin.y=labelY[y];		_labelDate.frame	 =frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	frame=_labelNowDate.frame;		frame.origin.y=dateY[y];		_labelNowDate.frame	 =frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	frame=_btnCancelTATE.frame;		frame.origin.y=buttonY[y];		_btnCancelTATE.frame =frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	frame=_btnRecStartTATE.frame;	frame.origin.y=buttonY[y];		_btnRecStartTATE.frame=frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	frame=_btnRecEndTATE.frame;		frame.origin.y=buttonY[y];		_btnRecEndTATE.frame =frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	frame=_btnRec1Shot.frame;		frame.origin.y=buttonY[y];		_btnRec1Shot.frame	 =frame;	nslog(@"frame=%@",NSStringFromCGRect(frame));
	if ([setup isSettingNShot]) {
		// NShotがある
		if ([setup isSetting1Shot]) {
			// 1Shotもある
			// １Shotの上に並べる
			frame.origin.y-=44;	// btn1Shotの上；本当はちゃんと計算で算出したいが手抜き
			// 横画面では1Shotの左側にボタンを配置したかったが、ここは座標的に負数領域であり、そこにおいたボタンは無効になるので置けなかった
		}
		// NShot設定があり、1Shot設定がないときは、NShotボタンを1Shotの位置にする
	} // NShotがないときは設定はするが無効
	_btnRecNShot.frame=frame;
	
	frame=_btnCameraFR.frame;		frame.origin.y=buttonY2[y];		_btnCameraFR.frame	 =frame;
	frame=btnBluetoothStatus.frame;		frame.origin.y=buttonY2[y]-4;	btnBluetoothStatus.frame =frame;	// 本当は別変数を持って制御したいけど、手抜き
#ifdef USE_NSHOT
	frame=labelLeftPic.frame;		frame.origin.y=buttonY2[y]+4;	labelLeftPic.frame	 =frame;	// 同上
#endif
#if defined(USE_ALARM)
	frame=_labelAlarm.frame;		frame.origin.y=labelY[y];		_labelAlarm.frame	 =frame;
#endif
#ifdef USE_BATTERY
	nslog(@"percentY=%f,progressY=%f",percentY[y],progressY[y]);
	frame=_labelPercent0.frame;		frame.origin.y=percentY[y];		_labelPercent0.frame =frame;
	frame=_labelPercent100.frame;	frame.origin.y=percentY[y];		_labelPercent100.frame=frame;
	frame=_batteryMeter.frame;		frame.origin.y=progressY[y];	_batteryMeter.frame	 =frame;
#endif
	/// ここで<Error>: CGAffineTransformInvert: singular matrix.が出てるが、実行には支障がないようなので無視
}

//--------------------------------------
// 録画開始・終了
//--------------------------------------

-(void)hideLeftPic
{
#if defined(USE_NSHOT)
	leftPic=0;
	labelLeftPic.hidden=YES;
#endif
}

-(void)printLeftPic
{
#if defined(USE_NSHOT)
	labelLeftPic.text=[NSString stringWithFormat:NSLocalStr(@"LEFTPIC"),leftPic];
#endif
}

// Rec中？
-(BOOL)isntRecOn:(id)sender
{
	return(!(_frec==REC_ON||_frec==REC_ON1SHOT));
}

// 録画開始/終了メイン
// ボタンからの録画開始/終了
-(IBAction)recStartEnd:(id)sender
{
	printSelf();
	[self hideLeftPic];
	if (_frec==REC_PREPARE) { // 録画準備中から開始へ
		nslog(@"rec開始");

#ifdef USE_BLUETOOTH
		// 録画準備→録画開始でリモコン側もシャッターを有効にする
		[self sendCommand:BT_COMMAND_RECSTART];
#endif
		// AVI用ワークの確保
		ULong maxPics=[setup getTmValues:TMMaxPics];
		nslog(@"maxpics=%ld",maxPics);
		if (maxPics==0) maxPics=500; // setupの値がおかしい時のため；バージョンアップで値がずれた時への対策
		InitAVI(maxPics);
		// 此処から先はカメラの切り替え禁止
		// 実際；撮影中に切り替えるとエラーが発生するため;UIImagePickerController: ignoring request to take picture; camera is changing modes.
		// ExitDelay中は許可したかったが、アラート表示中は他のボタンは無効になるようなので無意味なので消す。
		_btnCameraFR.hidden=YES;
		
		// これ以降、RecEndまでスリープさせない(ExitDelay中も含む)
		SetNonSleep(YES);
//		UIApplication *thisApp=[UIApplication sharedApplication];
//		thisApp.idleTimerDisabled=YES;
		if (setup.fantiSleep) {
			[antiSleep start]; // Anti-Sleepタイマー起動
		}
#ifdef USE_EXITDELAY
		// ExitDelayチェック;1shotの時は実行されない
		nslog(@"recStartEnd:ExitDelay前");
		ExitDelay *exitdelay=[[ExitDelay alloc]initWithView:self.view];
	#ifdef	USE_BATTERY
		BOOL f=[exitdelay check:batteryCheck.battery];
	#else
		BOOL f=[exitdelay check:nil];
	#endif
		if (f) {
			// キャンセル
			SetNonSleep(NO);
			//thisApp.idleTimerDisabled=NO; //  スリープ許可
			nslog(@"recStartEnd:ExitDelayでキャンセル");
			[self btnCameraFRSetting]; // カメラFRボタンの表示設定;先に消しているので復帰
			return;
		}
		nslog(@"recStartEnd:ExitDelay後");
#endif	// EXITDELAY
		// 録画中
		_frec=REC_ON;

		// 初期状態設定
		fevent=EVENT_NONE;		// アラーム中でない
		fnowActiveTimer=NO;		// タイマー有効期間内でない
#if defined(USE_ALARM)||defined(USE_NSHOT)
		postTimer.fyokushi=NO;	// アラーム抑止中でない
#endif
#ifdef USE_TIMER
		recTimer=nil;			// recTimerなし
#endif
		//
		clickSound(); // クリック音
		if ([setup isSetting1Shot]) {
			// 常時録画もアラームもOFFのときは1Shot録画にする(Komadoriの場合はアラームは関係なしはここには来ない)
			nslog(@"1shot録画");
			_frec=REC_ON1SHOT; // 1shot録画中
			fnowActiveTimer=[self recActive:YES]; // タイマーにかかわらず即時有効
		} else { // 1shotではない
			nslog(@"1shot録画ではない");
		#if defined(USE_ALARM)||defined(USE_NSHOT)
			if ([setup isSettingConstant]) {
				nslog(@"常時録画があるときは１アラーム１ファイル化できない");
				setup.falarm1file=NO; // 常時録画があるときは１アラーム１ファイル化できない 
			}
		#endif
		#ifdef KOMADORI
			fnowActiveTimer=[self recActive:YES]; // タイマーがないのいで即時有効
		#endif	
		}
		// ボタンの名称を変える
		nslog(@"ボタン再配置");
		[self rotationOverlay:[UIDevice currentDevice].orientation fall:YES fTranfsorm:NO];  // 変形なしでボタン位置のみ変更する
		[self setButtons];	

		// 今回の撮影枚数初期化
		[setup setPictures:0 valno:PICTURE_LATEST];

		// 録画開始ログ
		setLog(LOG_NAME_RECON);
	#ifdef USE_TIMER
		if (_frec!=REC_ON1SHOT) {
			// タイマーチェック起動
			// 初回タイマーチェック；現在がすでにタイマー期間中ならすぐに録画を開始する
			[self checkRecTimer5min:nil]; // ここからすぐマルチタスクする

			// タイマーチェック用タイマー起動
			[self startCheckRecTimer];
			// 最初のファイルオープンはタイマーON時に行われる
		}
	#endif
		nslog(@"recStartEnd 終了");
	} else { // 録画中から終了へ
		[self recEnd]; // この中でクリック音がなる
#ifdef USE_BLUETOOTH
		// 録画→録画終了でリモコン側もシャッターを無効にする
		[self sendCommand:BT_COMMAND_RECEND];
#endif
	}
}

#ifdef USE_1SHOT
// 1shot録画
-(IBAction)recStart1Shot:(id)sender
{
	printSelf();
	fevent=EVENT_1SHOT;
	[self hideLeftPic];
	[self rec1Picture:nil];
}
#endif

#ifdef USE_NSHOT
// nShot録画のボタン押下によるアラーム
// 設定でNShotが有効になってないときはここには来ない
-(IBAction)recStartNShot:(id)sender
{
	printSelf();
	//
	labelLeftPic.hidden=NO;
	// アラーム実体処理へ
	[self alarmDetection];
}
#endif

// 録画準備からキャンセル
-(IBAction)recCancel:(id)sender
{
	printSelf();

	// 以下の処理は重たいので、ActivityIndicatorを表示する
	
	UIActivityIndicatorView *indicator=[UIActivityIndicatorView show:indicatorView style:UIActivityIndicatorViewStyleWhiteLarge fbackground:YES];
#ifdef USE_BLUETOOTH
	[self sendCommand:BT_COMMAND_RECEND]; // REC終了またはキャンセルを他方に知らせる
#endif
	nslog(@"antiSleep stop");
	[antiSleep stop]; // Anti-Sleepタイマー停止

	fnowActiveTimer=NO;
	
	// 時計更新を止める
	nslog(@"nowDate stop");
	[nowDate stop];
#ifdef USE_BATTERY
	// 電池チェック終了
	[batteryCheck stop];
#endif
#if defined(USE_ALARM)
	nslog(@"setup.falarmRotation");
	if (setup.falarmRotation) {
		// 回転の通知を止める(開始はRootViewController:buttonRecにある)
		[[NSNotificationCenter defaultCenter]removeObserver:self
													   name:UIDeviceOrientationDidChangeNotification
													 object:nil];
	}
	[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; // これはNortification専用なので、通常の画面回転はそのまま動く
#endif
	// これ以降、スリープ許可
	SetNonSleep(NO);
//	UIApplication *thisApp=[UIApplication sharedApplication];
//	thisApp.idleTimerDisabled=NO;
	
	_frec=REC_NONE;
	clickSound(); // クリック音
	//
	[self closeImgPicker]; // カメラ終了
	DeallocAVI(); // AVI用ワークの開放
	[indicator dismiss]; // ActivityIndicatorを消す
	[self.view removeFromSuperview]; // ビューを消去
}

// 録画終了(一部処理はキャンセルと同じ)
-(void)recEnd
{
	printSelfs(@"%d",(int)_frec);
	if (_frec==REC_NONE) {
		nslog(@"no rec");
		return; // すでに録画が停止しているときは何もしない
	}
	// 以下の処理は重たいので、ActivityIndicatorを表示する
	UIActivityIndicatorView *indicator=[UIActivityIndicatorView show:indicatorView style:UIActivityIndicatorViewStyleWhiteLarge fbackground:YES];
	
	// 終了処理
	nslog(@"AVIfileClose");
	[self.delegate AVIfileClose]; // ファイルを閉じる
	nslog(@"savePictures");
	NSUInteger pics=[setup getPictures:PICTURE_LATEST];
	if (pics>0) { // 今回の録画において撮影があったとき
		// 最新画像状態を保存
		[self.delegate setLabelDateAndAlarm:_labelDate.text Alarm:
											#if defined(USE_ALARM)
															_labelAlarm.text
											#else
															nil
											#endif
		 ];
		[self.delegate saveLatestPicture];
	}
	//
#if defined(USE_ALARM)||defined(USE_NSHOT)
	if ([setup isAlarmRec]) { // アラーム設定がある
		// アラームセンサOFF
		[self AlarmSensor:NO];
		// アラーム中でない;ここに来るときはポストも強制停止する
		fevent=EVENT_NONE;
	}
#endif
	//
	nslog(@"すべてのタイマーを止める");
	[intervalRec stop];				// 録画用インターバルを停止する
#if defined(USE_ALARM)||defined(USE_NSHOT)
	if ([postTimer isInPost]) {		// ポスト中の時はアラーム終了処理を実行する
		nslog(@"ポスト中なので強制アラーム終了処理");
		[self endOfAlarm:nil];
	}
	[postTimer stop];				// ポストタイマーも停止する
#endif
#if defined(USE_ALARM)
	[postTimer stopYokushi:nil];	// アラーム抑止も停止する
#endif
#ifdef USE_TIMER
	[self stopRecTimer];
#endif
	[self.delegate AVIwaitAlbumCopyEnd];
	[indicator dismiss]; // ActivityIndicatorを消す

	// 録画終了ログ
	setLog(LOG_NAME_RECOFF);
	// 他の終了処理
	[self recCancel:nil];
}

//--------------------------------------

// カメラ前後切り替え
// 前面カメラが無効の時は、ボタンそのものを無効にして呼び出させないこと
-(IBAction)cameraFR:(id)sender
{
	printSelf();
	nslog(@"前setup.cameraDevice=%d",(int)setup.cameraDevice);
	switch (setup.cameraDevice) {
		case UIImagePickerControllerCameraDeviceRear:
			nslog(@"後→前");
			setup.cameraDevice=UIImagePickerControllerCameraDeviceFront;
			break;
		case UIImagePickerControllerCameraDeviceFront:
			nslog(@"前→後");
			setup.cameraDevice=UIImagePickerControllerCameraDeviceRear;
			break;
	}
	nslog(@"後setup.cameraDevice=%d",(int)setup.cameraDevice);
	
	imgPicker.cameraDevice = setup.cameraDevice;
	clickSound(); // クリック音
}

//--------------------------------------
// アラーム処理
//--------------------------------------

#if defined(USE_ALARM)||defined(USE_NSHOT)
// アラーム実体処理
// アラーム設定がないときにはここには来ない
-(void)alarmDetection
{
	printSelf();
	BOOL fretrigger=NO;
	NSTimeInterval tm=0.0; // 代入はAnalyzeの警告回避のため
	if (postTimer.fyokushi) {
		nslog(@"アラーム抑止中");
		[alertSymbol display:TM_ALERT_DISPLAY]; // ×表示
		return;
	}
	switch (fevent) {
		case EVENT_ALARM:
		case EVENT_ALARM1:
			// すでにアラーム中の時
			#if defined(USE_ALARM)
			if (setup.falarmRetrigger==NO) {
				// リトリガーOFFなら無視
				nslog(@"リトリガーOFFなら無視");
				return;
			}
			// リトリガーが有効なら、アラーム再発報
			nslog(@"リトリガーON");
			_labelAlarm.text=SYMBOL_ALARM_RETRIGGER; // 文字だけ換える（イベントを変更させないため）
			fretrigger=YES;
			#endif
			break;
		
		default:
			// 他からアラームになるとき
			// アラームログ
			setLog(LOG_NAME_ALARM);

			// 新規アラーム録画開始
			fevent=EVENT_ALARM;
			
			// 録画間隔＆ポストタイマー設定
			#if defined(USE_ALARM)||defined(USE_NSHOT)
			if (setup.falarm1file) {
				// 前のアルバムコピーが終わってなかったら、ここで待つ。
				nslog(@"前のアルバムコピーが終わってなかったら、ここで待つ。");
				// アルバムコピー指示直後に待つ方法もあるが、次のアラームまでに時間がある場合無駄になるので
				// 抑止があればなお効果的
				[self.delegate AVIwaitAlbumCopyEnd];
			}
			#endif
			#if defined(USE_ALARM)
			// アラームリミット開始;Noneの時はタイマーそのものがかからない
			// リミットは初回のみ設定され、リトリガーでは再設定してはいけない（それでは意味が無い）
			[alarmLimit start:[setup getTmValues:TMAlarmLimit]];
			#endif
			nslog(@"録画間隔設定");
			tm=[setup getTmValues:TMAlarmInterval];
			// リトリガー時は現在の値を使えばいいので、再取得しない
			break;
	}

	nslog(@"ポストタイマー設定");
	// 先にポストタイマーを設定
	NSTimeInterval postTm	=[setup getTmValues:TMAlarmPost];
	NSTimeInterval yokushiTm=[setup getTmValues:TMAlarmYokushi];
#ifdef USE_NSHOT
	// nSHOT時はpostTimeは枚数で指定されているので、intervalからポスト時間(秒)を計算する
	nslog(@"録画間隔:%f秒",tm);
	nslog(@"ポスト枚数:%f枚",postTm);
	leftPic=(int)postTm;
	[self printLeftPic];
	// 最初に1枚撮影されているから、ポスト時間はその分引いておくと良い。でもまだまれに+1枚されてしまうが、それは実記録をなしにすることで対処する。
	postTm=postTm*tm-1.25; // 最小録画間隔の2.5s/2
	nslog(@"ポスト時間:%f秒",postTm);
	yokushiTm=None;
#endif
	if (![postTimer start:postTm yokushi:yokushiTm]) {
		nslog(@"ポスト1枚:RecViewController");
		// ポスト1枚なので録画間隔もない
		tm=None;
		fevent=EVENT_ALARM1;
//		[self.view setNeedsDisplay]; //!!test!!->だめ
	}
	
	if (fretrigger==NO) {
		// すぐ最初の１枚を撮る
		[self rec1Picture:nil];
		// 後はインターバル
		[intervalRec start:tm];
	} // リトリガー時は前回の設定に従って動作させるので、ここでは何もしない
}
#endif

#if defined(USE_ALARM)
// Shakeの検知(Alarm1)
// アラーム設定がないときにはここには来ない
-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
	nslog(@"Shake発生:%d;%@",(int)motion,event);
	// アラーム実体処理へ
	[self alarmDetection];
}
#endif

/* 静止画と動画で処理の呼び出され順が異なる。
 静止画時
	基本
	(rec1Picture takePicture -> imagePickerController:didFinishPickingMediaWithInfo ) * ポスト時間 -> endOfAlarm
		しかし、理論的には、ポスト時間終了判定(endOfAlarm)後にimagePickerControllerが来る可能性もある
 	(rec1Picture takePicture -> imagePickerController:didFinishPickingMediaWithInfo ) * ポスト時間 -> endOfAlarm -> imagePickerController:didFinishPickingMediaWithInfo
 
 動画時
	rec1Picture startVideoCapture 〜ポスト時間〜 endOfAlarm stopVideoCapture -> imagePickerController:didFinishPickingMediaWithInfo

 発報時の処理手順 
	alarmDetection(発報)〜	postTimer；リトリガーはここでカウンターリセット	〜	endOfAlarm↓
							alarmLimit								〜	↑limitOfAlarm
 */

#if defined(USE_ALARM)||defined(USE_NSHOT)
// アラーム終了処理
// postTimer(ポスト)終了時に呼び出される
-(void)endOfAlarm:(id)sender
{
	printSelf();
	// 最後は撮像終了(didFinishPickingMediaWithInfo)を待つ
	// これをしないと上記タイミングのズレが発生し、処理が複雑になりすぎるのでここで終了を待つ
	nslog(@"撮影終了待ち:ftakePicture=%d",(int)ftakePicture);
	while (ftakePicture!=TAKE_PICTURE_ENABLE) {
		// まだ前の撮影処理が終了していないので待つ
		//nslog(@"endOfAlarm:まだ前の撮影処理が終了していないので待つ");
		EnterRunLoop(TM_CHECK_OPERATION);
	}
	nslog(@"撮影終了待ち終了");
	#if defined(USE_ALARM)
	[alarmLimit stop];
	#endif
	//
	fevent=EVENT_NONE;
#if defined(USE_ALARM)||defined(USE_NSHOT)
	if (setup.falarm1file) {
		nslog(@"個別ファイル化(endOfAlarm)");
		[self.delegate AVIfileClose]; // ファイルクローズ→アルバムコピー
		[self.delegate AVIfileOpen];
	}
#endif
	if (sender!=nil) {
		// タイマー再チェック（常時録画再開も兼ねる）
		[self checkRecTimer5minAfterAlarm];
	}
	nslog(@"endOfAlarm終了");
}
#endif

#if defined(USE_ALARM)
// アラームリミット処理
// alarmLimit終了時に呼び出される
-(void)limitOfAlarm:(id)sender
{
	printSelf();
	// 実際にリミットがある時のみここに来る
	// アラームポストを強制終了する
	[postTimer stop];
	[self endOfAlarm:nil]; // ここでalarmLimitも止まる
	if (setup.falarm3D) {
		switch (setup.falarm3DLevel) {
			case LEVEL_FORCE_CANCEL:
				nslog(@"レベル判定：強制連続解除（次回発報で新規）");
				setup.rotationControl.levelCount=0;
				break;
			case LEVEL_WAIT_DISCONTINUE:
				nslog(@"レベル判定：連続解除待ち");
				// 実は何もしなくて良い
				break;
			case LEVEL_OFF:
				nslog(@"レベル判定：なし");
				break;
		} // switch
	} // if
}

// 3Dアラーム発生
-(void)alarm3Ddetection:(NSNotification *)notification
{
	printSelfs(@"notification=%@",notification);
	if ([[notification object]isEqualToString:NAME_ALARM_LEVEL]) {
		nslog(@"with レベル判定");
		// ポストを再設定する＝ポストの延長
		// 3Dアラームのサンプリングは100ms＜＜ポスト時間なのでこれでOK
		[postTimer restart]; // 再スタート
		return;
	}
	// アラーム実体処理へ
	// リトリガーもここで判定
	[self alarmDetection];
}
#endif

#if defined(USE_ALARM)||defined(USE_NSHOT)
// アラームセンサーのON/OFF
// アラーム録画がONの時だけ設定すること（特に3Dアラームは電池消耗が大きいので）
-(void)AlarmSensor:(BOOL)onoff
{
	printSelfs(@"%d",onoff);
	if (onoff==YES) { // ON
		#if defined(USE_ALARM)
		if (setup.falarmShake) {
			#ifdef DEBUG
			BOOL f=
			#endif
			[self becomeFirstResponder];
			nslog(@"becomeFirstResponder=%d",f);
		}
		nslog(@"falarm3D=%d",setup.falarm3D);
		if (setup.falarm3D) {
			// 3Dアラームを使うよう指示されているので、設定を読み込んでみる
			[setup.rotationControl initialize:SAMPLING]; // dataSourceはrotationControlが内蔵しているものを使うので設定不要
			[setup.rotationControl readSettings:GRAPHFILE];
			if ([setup.rotationControl checkAllThreshold]) {
				// 実際にしきい値設定がある時のみ
				[setup.rotationControl setAlarm3DNortification:self selector:@selector(alarm3Ddetection:)]; // 通知設定
				[setup.rotationControl setAlarm3DLevel:setup.falarm3DLevel]; // レベル判定状態を設定
				[setup.rotationControl selectData:setup.selectData]; // データ源設定
				[setup.rotationControl startSampling];
				nslog(@"3D Alarm on");
			}
		}
		#endif
		#if defined(USE_NSHOT)
		_btnRecNShot.hidden=NO;
		#endif
	} else { // OFF
		#if defined(USE_ALARM)
		if (setup.falarmShake) {
			[self resignFirstResponder]; // の応答を解除(重要)
		}
		if (setup.falarm3D) {
			[setup.rotationControl stopRotationControl]; // Nortificationも止まる
			nslog(@"3D Alarm off");
		}
		#endif
		#if defined(USE_NSHOT)
		_btnRecNShot.hidden=YES;
		#endif
	}
}
#endif

- (BOOL)canBecomeFirstResponder
{
	printSelf();
	// ここは成れるかどうかの問い合わせだけなので、此処に来る時点ではisFirstResponderはNOである
	return YES;
}

//----------------------------------------------
// 画面を回転して良いかどうか
//----------------------------------------------
// iOS6.0

-(BOOL)shouldAutorotate
{
	return  NO; // 独自回転処理をするのでシステムによる回転は禁止
//	return shouldAutorotateByMain();
}

- (NSUInteger)supportedInterfaceOrientations
{
	return (supportedInterfaceOrientationsByMain());
}

//----------------------------------------------

#if defined(USE_ALARM)||defined(USE_NSHOT)
// アラーム発報
-(void)Alarm:(NSString *)name
{
	if (fnowActiveTimer) { // タイマー有効期間中
		// アラーム発報（設定時）；アラームの発報には画面方向は関係ない（ただし上下向きでは実撮影しない）
		if (_frec==REC_ON) { // REC中(1shot中はここには来ないのでREC_ON1SHOTは不要)
			if ([setup getTmValues:TMAlarmInterval]!=None)
			{ // アラーム設定がある
				nslog(@"%@",name);
				[self alarmDetection];
				//[self motionEnded:UIEventSubtypeMotionShake withEvent:nil];
			}
		}
	}
}
#endif

// 回転処理
// デバイス回転検出時にNSNotificationCenterから呼び出される
- (void)orientationChanged:(NSNotification *)notification
{
	UIDevice *dev=[notification object]; // [UIDevice currentDevice]が引数で送られて来ている
	UIDeviceOrientation orientation=dev.orientation;
	nslog(@"orientationChanged=%@(向き=%d)",notification,(int)orientation);
	switch (orientation) {
		default: // 上向き(=5)、下向き(=6)、不明(=0)の時に此処に来る
			// 無視する
			// iPad2は処理速度が早いためか、中間の向きでも通知が入ってくるのでこれが重要
			nslog(@"この方向は無視する");
			return;
			//
		case UIDeviceOrientationPortrait:			// =1
		case UIDeviceOrientationPortraitUpsideDown: // =2
		case UIDeviceOrientationLandscapeLeft:		// =3
		case UIDeviceOrientationLandscapeRight:		// =4
			break;
	}
#if defined(USE_ALARM)
	if (setup.falarmRotation) {
		[self Alarm:@"回転もアラームにする"];
	}
#endif	
	// 画面回転処理:ボタン変更
	// 回転は常に縦<->横とは限らない。正反対に傾けるときは、縦<->縦,横<->横もある
	// 縦横方向でないときは回転処理しない
	[self rotationOverlay:orientation fall:NO fTranfsorm:YES];
}

//--------------------------------------
// メモリ処理(delegate)
//--------------------------------------

#ifdef DEBUG
- (void)didReceiveMemoryWarning 
{
	printSelf();
    [super didReceiveMemoryWarning];
}
#endif

- (void)dealloc
{
	printSelf();
#ifdef USE_TIMER
	[self stopRecTimer];
#endif
}

@end
