//
//  RotationControl.m
//  制御部
//  X-BASIC'
//
//  Created by AIG-Soft on 2012/09/19.
//  Copyright 2012-2015 AIG-Soft. All rights reserved.
//

#import "RotationControl.h"

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

@implementation RotationControl

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

// 初期化
// sampling : サンプリング間隔(ms単位)
-(id)initWithSampling:(NSUInteger)sampling
{
	printSelf();
	self=[super init];
	if (self!=nil) {
		// CMMotionManager生成
		opQueueMotion=nil; // サンプリングはまだ開始しない
		motionManager=[[CMMotionManager alloc]init];
		
		// データ読み込み用ワーク確保
		self.datas = [[NSMutableArray alloc]init];
		
		interval=((NSTimeInterval)(DEF_SAMPLING)/(NSTimeInterval)(SEC_MS)); // 先に型キャストしておかないと整数演算されて0になってしまう
		datasPerSec=(SEC_MS/sampling);
		nslog(@"1秒間のサンプリング回数=%d,サンプリングインターバル=%f(秒)",(int)datasPerSec,interval);
		
		// 初期化
		self.fpause		=NO;			// 一時停止しない（初期）
		counter			=0;				// カウンター(0=未使用)
		self.dno		=MOTION_DEGREE;	// デフォルトのデータ
		self.dataStore	=self;			// デフォルトのデータ格納メソッド；必要なら再設定すること
	}
	return(self);
}

//-------------------------------------------------------------------
// ダミーデリゲート
//-------------------------------------------------------------------

// データ格納(内部＝アラーム検知用)
// 外部からデリゲートを指定しないときはdataStore=selfを指定すること
-(void)rotationControlStoreData:(CMRotationRate *)xyz
{
//	printSelf();
}

//-------------------------------------------------------------------
// サンプリング処理
//-------------------------------------------------------------------

// サンプリング処理設定
-(void)startSampling
{
	printSelf();
	nslog(@"dno=%d,interval=%f",(int)self.dno,interval);
	
	opQueueMotion = [[NSOperationQueue alloc]init]; // 多分これがself.retainCountを+1してる
	
	// デバイスモーション取得間隔 [sec]
	// これは実機でしか実行できないけど、if (getDeviceType()!=DEVICE_SIMULATOR) は入れない
	motionManager.deviceMotionUpdateInterval = interval;
	
	// デバイスモーションの取得開始
#if 0	// そのうち初期化モードを指定可能にして入れる
	// (1)初期化データ取得(iOS5 only)
	// Z軸を鉛直として、X軸を横とする。その際、GPSと電子コンパスを利用して、X軸を"真北"に設定
	[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical
													   toQueue:opQueueMotion
												   withHandler:^(CMDeviceMotion *data, NSError *error){
												   	// 初期化だけなのでデータ取得はしない
												   }
	 ];
#endif
	
	// (2)常時データ取得
	// 現在のバージョンでは、一度に使えるデータ源は１つのみ
	// (システムに掛かる負荷から、２つ以上にはしないほうが良いらしい)
	//- (void)startDeviceMotionUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMDeviceMotionHandler)handler
	[motionManager startDeviceMotionUpdatesToQueue:opQueueMotion
			withHandler:^(CMDeviceMotion *data, NSError *error) {
				dispatch_async(dispatch_get_main_queue(),
					^{
						//nslog(@"sampling:fpause=%d",fpause_);
						if (counter>0) { // サンプリングカウンター利用中
							counter--;
						}
						if (!self.fpause) {
							CMRotationRate xyz;
							CMAcceleration axyz;
							//nslog(@"sampling:dno=%d",dno_);
							switch (self.dno) {
								case MOTION_DEGREE: 
									//nslog(@"角度");
									axyz=data.gravity; // CMDeviceMotionはクラス,CMAccelerationは構造体なので取り出しが必要
									{ /// 数値変換 : -90〜90度
									axyz.x*=90.0;
									axyz.y*=90.0;
									axyz.z*=90.0;
									}
									[self.dataStore rotationControlStoreData:(CMRotationRate *)&axyz];
									// 本来CMAcceleration*だが、内容が同じなので型キャストで渡す
									break;
								case MOTION_VELOCITY:
									//nslog(@"角速度");
									// ラジアン/秒＝角速度
									xyz=data.rotationRate; // CMDeviceMotionはクラス,CMRotationRateは構造体なので取り出しが必要
									[self.dataStore rotationControlStoreData:&xyz];
									break;
								case MOTION_ROTATION:
									//nslog(@"オイラー角");
									xyz.x=data.attitude.roll;	// ロール角(ラジアン）
									xyz.y=data.attitude.pitch;	// ピッチ角(ラジアン）
									xyz.z=data.attitude.yaw;	// ヨー角(ラジアン）
									{ /// 数値変換 : -90〜90度
									xyz.x*=(180.0/M_PI); // 本当はf=90*x*2/π
									xyz.y*=(180.0/M_PI);
									xyz.z*=(180.0/M_PI);
									// v1.2  : yaw!=方位であることが判明したのでここはヨー角で固定
									}
									[self.dataStore rotationControlStoreData:&xyz];
									break;
							}
						} // 一時停止中は無視
					} // ^{
				); // dispatch_async
			} // withHandler
	 ];
}

//-------------------------------------------------------------------
// データ制御
//-------------------------------------------------------------------

// datas[index=時間]
-(CMRotationRate)getDatas:(NSUInteger)index
{
	NSValue *dat=self.datas[index];
	CMRotationRate xyz;
	[dat getValue:&xyz];
	return (xyz);
}

// 時間軸方向のデータ数
-(NSUInteger)getDatasCount
{
	return [self.datas count]; // 今回は3軸同時なのですべて同数
}

// データ配列を空にする
-(void)eraseData
{
	printSelf();
	[self.datas removeAllObjects];
}

#if 0	// 今回は使ってないので
-(BOOL)addData:(CMRotationRate *)xyz
{
	// データを配列に入れる
	[datas_ addObject:[NSValue value:xyz withObjCType:@encode(CMRotationRate)]];
}
#endif

//-------------------------------------------------------------------
// 解放処理
//-------------------------------------------------------------------

// rotationControlの停止
// release時に先にこれを呼び出しておかないとself.retainCountが-1されない
// 開始はinitWithSampling→startSampling
-(void)stopRotationControl
{
	printSelf();

	// サンプリングを停止＆無視させる；これをしないとrelease完了までの間にハンドラーが呼び出されてエラーが出てしまう
	self.fpause=YES;
	[motionManager stopDeviceMotionUpdates];

	// startWithSamplingで確保分の開放
	if (opQueueMotion!=nil) {
		opQueueMotion=nil;
		nslog(@"opQueueMotion release");
	} // startSamplingしてないときは確保されてない
	
	// initWithSamplingで確保分の開放
	motionManager = nil;
	 // データ読み込み用ワーク開放
	
	nslog(@"RotationControl.stopRotationControl終了");
	// これを呼び出してもタイミングにより1回はサンプリングが走ってしまうことがあるのでfpause=YESにしておくのが重要
}

- (void)dealloc
{
	printSelf();
///	[self stopRotationControl];
}

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

@end
