//
//  libs.m
//  汎用関数
//
//  Created by 今井 彰彦 on 10/12/21.
//  Copyright 2010 AIG-Soft. All rights reserved.
//

#import "libs.h"
#import "NSString+sizeWithFont.h"

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

DEVICE_TYPE getDeviceType(void)
// 実行中のデバイス種別を返す
{
	NSString *modelname=[[UIDevice currentDevice]model];
	nslog(@"model=%@",modelname);
	if ([modelname isEqualToString:@"iPad"]) return(DEVICE_IPAD);
	if ([modelname isEqualToString:@"iPhone"]) return(DEVICE_IPHONE);
	if ([modelname isEqualToString:@"iPod touch"]) return(DEVICE_IPODTOUCH);
	if ([modelname isEqualToString:@"iPhone Simulator"]) return(DEVIDE_IPHONE_SIMURATOR);
	return(DEVICE_IPAD_SIMULATOR);
}

BOOL isDeviceIpad(DEVICE_TYPE devtype)
// iPadかどうか（シミュレーターを含む）
{
	switch (devtype) {
		case DEVICE_IPAD:
		case DEVICE_IPAD_SIMULATOR:
			nslog(@"This is iPad=%d",devtype);
			return (YES);
		default: // 警告回避のため
			break;
	}
	return(NO);
}

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

float getSystemVersion(void)
// iOSバージョンを得る
{
	NSString *version = [[UIDevice currentDevice]systemVersion];
	nslog(@"getSystemVersion=%@/SystemName=%@",version,[[UIDevice currentDevice]systemName]);
	nslog(@"Version %@(%f)", version,[version floatValue]);
	return [version floatValue];
}

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

void SetViewBelowStatusBar(UIView *view)
// 何故かIB上ではどう設定してもステータスバー分のずらしがなされないため、手動で行う
{
	// サイズはもともとステータスバー文を引いたサイズが入っている
	//nslog(@"frame=%@",aViewController.view);
	CGRect frame,bounds;
	bounds=[[UIScreen mainScreen]bounds]; // 全画面サイズ
	//	nslog(@"bounds=%f,%f,%f,%f",bounds.origin.x,bounds.origin.y,bounds.size.width,bounds.size.height);
	frame=view.frame;
	frame.origin.y+=(bounds.size.height-frame.size.height); // statusバーの分を下げる
	view.frame=frame;
}

void SetViewSize(UIView *view,CGFloat wx,CGFloat wy)
// UIViewの幅を設定する
// IBでは設定ができないので
// 数値のチェックはしてない
// wx,wy<0のときは、画面幅の1/(-wx),1/(-wy)にする
{
	CGRect frame;
	nslog(@"元size=%f,%f",wx,wy);
	if (wx<0||wy<0) {
		CGRect bounds;
		bounds=[[UIScreen mainScreen]bounds]; // 全画面サイズ
		nslog(@"bounds=%f,%f,%f,%f",bounds.origin.x,bounds.origin.y,bounds.size.width,bounds.size.height);
		if (wx<0) {
			wx=-wx;
			wx=bounds.size.width/wx;
		}
		if (wy<0) {
			wy=-wy;
			wy=bounds.size.height/wy;
		}
	}
	nslog(@"size=%f,%f",wx,wy);
	frame=view.frame;
	frame.size.width =wx;
	frame.size.height=wy;
	view.frame=frame;
}

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

NSString *strInterfaceOrientation(UIInterfaceOrientation Orientation)
// UIInterfaceOrientationで回転方向を文字列で返す
{
	// nslog(@"strOrientation回転方向=%d",Orientation);
	switch (Orientation) {
		case UIInterfaceOrientationPortrait:
			return(@"縦");
		case UIInterfaceOrientationPortraitUpsideDown:
			return(@"縦；上下逆");
		case UIInterfaceOrientationLandscapeLeft:
			// UIDeviceOrientationLandscapeLeftとは逆になるので注意
			return(@"横右");
		case UIInterfaceOrientationLandscapeRight:
			// UIDeviceOrientationLandscapeRightとは逆になるので注意
			return(@"横左");
		default:
			break;
	}
	nslog(@"未定義=%d",(int)Orientation);
	return(@"未定義");
}

NSString *strDeviceOrientation(UIDeviceOrientation Orientation)
// UIDeviceOrientationで回転方向を文字列で返す
{
	// nslog(@"strOrientation回転方向=%d",Orientation);
	switch (Orientation) {
		case UIDeviceOrientationUnknown:
			return(@"不明");
		case UIDeviceOrientationPortrait:
			return(@"縦");
		case UIDeviceOrientationPortraitUpsideDown:
			return(@"縦；上下逆");
		case UIDeviceOrientationLandscapeLeft:
			return(@"横左");
		case UIDeviceOrientationLandscapeRight:
			return(@"横右");
		case UIDeviceOrientationFaceUp:
			return(@"上面");
		case UIDeviceOrientationFaceDown:
			return(@"下面");
	}
	nslog(@"未定義=%d",(int)Orientation);
	return(@"未定義");
}

BOOL checkDeviceOrientationOK(void)
// デバイスの方向が縦横であるかどうか
// YES=縦横 , NO=それ以外
{
//	nslog(@"UIDevice=%@",[UIDevice currentDevice]);
	UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
	nslog(@"checkDeviceOrientationOK:%@(%d)",strDeviceOrientation(deviceOrientation),(int)deviceOrientation);
	switch (deviceOrientation) {
		case UIDeviceOrientationUnknown:
			nslog(@"方向不明");
			// 回転は加速度計を使って取得しているため、傾き途中にも通知が入り、その時この状態になる。
			// なので、回避が必要。
			return (NO);
		case UIDeviceOrientationFaceUp:
		case UIDeviceOrientationFaceDown:
			// この方向も無視する必要がある
			nslog(@"上下向き");
			return (NO);
		default: // 警告排除のため(実際には来ない)
			break;
	}
	// 縦横方向；問題なし
	return(YES);
}

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

NSString *mesONOFF(BOOL onoff)
// switch状態表示文字列を返す
{
	if (onoff) {
		return NSLocalizedString(@"ON",@"オン");
	}
	return NSLocalizedString(@"OFF",@"オフ");
}

NSString *mesONOFF2(BOOL onoff,NSString *onmes,NSString *offmes)
// switch状態表示文字列を指定文字列で返す
{
	if (onoff) {
		return NSLocalStr(onmes);
	}
	return NSLocalStr(offmes);
}

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

// dateから時分秒を数値で返す
// リターン値は分
NSUInteger getHourMinSec(NSDate *date,NSUInteger *hour,NSUInteger *sec)
{
	nslog(@"NSDate=%@",date); // GMT+0000で表示される
	// 分だけ取り出す
	// NSDateから直接取り出す方法がないので非常に面倒な方法をとっている
	NSDateFormatter *formatter=[[NSDateFormatter alloc]init]; // initはiOS4.0で廃止されている
	[formatter setLocale:[NSLocale currentLocale]]; // Localeの設定(重要)
	[formatter setDateFormat:@"m"]; // 分だけ取り出す
	NSString *dtstr;
	dtstr=[formatter stringFromDate:date]; // 一旦インスタンスに入れないとメモリリークを起こす
	NSInteger minute=[dtstr intValue]; // 整数化
//	nslog(@"minute=%d",minute);
	if (hour!=NULL) {
		[formatter setDateFormat:@"H"]; // 時だけ取り出す
		dtstr=[formatter stringFromDate:date]; // 一旦インスタンスに入れないとメモリリークを起こす
		*hour=[dtstr intValue]; // 整数化
//		nslog(@"hour=%d",*hour);
	}
	if (sec!=NULL) {
		[formatter setDateFormat:@"s"]; // 秒だけ取り出す
		dtstr=[formatter stringFromDate:date]; // 一旦インスタンスに入れないとメモリリークを起こす
		*sec=[dtstr intValue]; // 整数化
//		nslog(@"sec=%d",*sec);
	}
	return (minute);
}

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

// ActionSheetの回転時のリサイズ指定
// IBが使えないので
void setActionSheetResize(UIActionSheet *asheet)
{
	asheet.autoresizesSubviews=YES;
	asheet.autoresizingMask=	UIViewAutoresizingFlexibleWidth |
								UIViewAutoresizingFlexibleHeight |
								UIViewAutoresizingFlexibleTopMargin |
								UIViewAutoresizingFlexibleBottomMargin |
								UIViewAutoresizingFlexibleLeftMargin |
								UIViewAutoresizingFlexibleRightMargin;
}


// AlertView の回転時のリサイズ指定
// IBが使えないので
void setAlertViewResize(UIAlertView *aview)
{
	aview.autoresizesSubviews=YES;
	aview.autoresizingMask=	UIViewAutoresizingFlexibleWidth |
							UIViewAutoresizingFlexibleHeight |
							UIViewAutoresizingFlexibleTopMargin |
							UIViewAutoresizingFlexibleBottomMargin |
							UIViewAutoresizingFlexibleLeftMargin |
							UIViewAutoresizingFlexibleRightMargin;
}

// アラートの表示
void Alert1(NSString *title,NSString *message,id delegate)
{
	nslog(@"Alert1:%@",NSLocalStr(title));
	UIAlertView *aview=[[UIAlertView alloc]initWithTitle:NSLocalStr(title)
												 message:NSLocalStr(message)
												delegate:delegate
									   cancelButtonTitle:NSLocalStr(@"CLOSE")
									   otherButtonTitles:nil,nil];
	setAlertViewResize(aview); // リサイズ設定
	[aview show]; // 表示したらすぐ帰ってくる
}


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

// NSDate descriptionは〜±hhmmなので、この最後のGMT表記を消した文字列を返す
NSString *dateStringWithoutGMT(NSDate *date)
{
	NSDateFormatter *formatter=[[NSDateFormatter alloc]init]; // initはiOS4.0で廃止されている
	[formatter setLocale:[NSLocale currentLocale]]; // Localeの設定(重要)
	[formatter setDateFormat:NSLocalStr(@"DATE")];
	NSString *str=[formatter stringFromDate:date];
	//	nslog(@"dateStringWithoutGMT=%@",str);
	//	return (str); // アドレス返しだけではダメ
	return [NSString stringWithString:str]; // ローカルスタック上に実体を生成してからリターンする
}

// title+文字幅を合わせるための空白文字列を作成する(文字数上左寄せ)
// flocalize : YES=ローカライズする
NSString *leftString(NSString *title,BOOL flocalize)
{
	NSInteger maxlength=[NSLocalStr(@"MAXLENGTH") intValue]; // 実際に文字列を表示させて決めること
	if (flocalize) {
		title=NSLocalStr(title); // ローカライズ
	}
	while (maxlength<[title length]) {
		nslog(@"MAXLENGTH=%d<length=%d\n設定値を変更してください",(int)maxlength,(int)[title length]);
		maxlength+=2; // 自動補正
	}
//	nslog(@"leftString処理前:%@;%d<%d",title,[title length],maxlength);
	NSMutableString *spc=[[NSMutableString alloc]initWithString:title];
	while ([spc length]<maxlength) {
		[spc appendString:NSLocalStr(@"ZSPC1")]; // テキスト表示用
	}
//	nslog(@"leftString処理後:%@;%d<%d",spc,[spc length],maxlength);
	return spc;
}

// title+文字幅を合わせるための空白文字列を作成する(表示上/文字列左寄せ)
// flocalize : YES=ローカライズする
NSString *leftString2(NSString *title,BOOL flocalize,UITextView *textView)
{
	if (textView==nil) {
		// 文字列用
		return (leftString(title,flocalize));
	}
	// 表示用
	CGFloat maxlength=[NSLocalStr(@"MAXLENGTHF") floatValue]; // 実際に文字列を表示させて決めること
	if (flocalize) {
		title=NSLocalStr(title); // ローカライズ
	}
	nslog(@"leftString2:%@;%f<%f",title,[title sizeWithFont7:textView.font].width,maxlength);
	NSMutableString *spc=[[NSMutableString alloc]initWithString:title];
	while ([spc sizeWithFont7:textView.font].width<maxlength) {
		[spc appendString:NSLocalStr(@"ZSPC")]; // 表示＆PDF調整用
	}
	return spc;
}

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

// 日時文字列の日英を、現在のローカライズに合わせて変換する
NSString *dateFormatJtoE(NSString *date)
{
	if (date==nil) {
		return [NSString stringWithString:NSLocalStr(@"NODATE")];
	}
	// 途中で日英を切り替えると、前の日時並びのまま表示されてしまう
	// 本来途中での切り替えはないはずだが、一応対策しておく
	// 0123
	// MM/DD/YYYY と YYYY/MM/DDが混在するため、[2]が/かどうかで判定する
	NSString *datefmt=NSLocalStr(@"DATE"); // 現在のローカライズにおける日付フォーマット
	unichar c =[datefmt characterAtIndex:2];
	unichar c2=[date    characterAtIndex:2];
	nslog(@"date=%@ [2]=%c($%04x) : fmt=%@ [2]=%c($%04x)",date,c2,c2,datefmt,c,c);
	// 文字コードはUTF-16
	if (c=='/') { // 現在は英語モード
		if (c2!='/') {
			// 元が日本語モードで入っている
			// 01234567890
			// yyyy/mm/dd -> mm/dd/yyyy 以下同じ
			nslog(@"日->英");
			NSString *yyyy=[date substringToIndex:4]; // yyyy 0〜 4文字
			NSString *tim =[date substringFromIndex:10]; // 時間
			NSString *mmdd=[date substringWithRange:NSMakeRange(5,5)]; // mm/dd
			return [NSString stringWithFormat:@"%@/%@%@",mmdd,yyyy,tim];
		}
		// もともと英語モードである：変換不要
		return [NSString stringWithString:date];
	}
	// 現在は日本語モード
	if (c2=='/') {
		// 元が英語モードで入っている
		nslog(@"英->日");
		// 01234567890
		// mm/dd/yyyy -> yyyy/mm/dd 以下同じ
		NSString *mmdd=[date substringToIndex:5]; // mm/dd 0〜5文字
		NSString *tim =[date substringFromIndex:10]; // 時間
		NSString *yyyy=[date substringWithRange:NSMakeRange(6,4)]; // yyyy
		return [NSString stringWithFormat:@"%@/%@%@",yyyy,mmdd,tim];
	}
	// もともと日本語モードである：変換不要
	return [NSString stringWithString:date];
}



/// @brief 文字列str中に正規表現expressionが含まれるかどうかを調べる
/// @discussion 一致するにしたい場合は"^〜$"と記述する<br>
///  大文字小文字区別なし
/// @return NO=不一致 , YES=一致
BOOL cmpRegularExpression(NSString *str,NSString *expression)
{
	//	nslog(@"cmpRegularExpression");
	//	nslog(@"%@ : %@",str,expression);
	// NSRegularExpressionはiOS 4.0以降で実装された正規表現用クラス
	NSError *error = NULL;
	// 正規表現インスタンスの作成
	NSRegularExpression *regexp = [NSRegularExpression regularExpressionWithPattern:expression
																			options:NSRegularExpressionCaseInsensitive	/* 大文字小文字区別なし */
																			  error:&error];
	nslog(@"regexp=%@",regexp);
	if (error != nil) {
		nslog(@"Error=%@", error);
		// 念のため
		return (NO);
	}
	NSTextCheckingResult *match =[regexp firstMatchInString:str options:0 range:NSMakeRange(0, [str length])]; /* strの長さ全体が比較対象 */
	nslog(@"match=%@",match);
	nslog(@"match.resultType=%llx",match.resultType); // NSTextCheckingTypeRegularExpression＝正規表現が返ってきている
	nslog(@"match.numberOfRanges=%d",(int)match.numberOfRanges);
	nslog(@"match.regularExpression=%@",match.regularExpression); // 正規表現そのものが入っている
	return (match.numberOfRanges>0);
}


/// @brief 指定時間の間RunLoopを実行する
/// @param tm 実行時間；0.0の場合一瞬だけ
/// @discussion アクティビティインジケーターを含むアニメーション系はほぼすべて指定のあとこれの呼び出しが必要となる。
void EnterRunLoop(NSTimeInterval tm)
{
	// NSDateだけだとメモリがどんどん食われてしまう
	// 実験の結果autorelease poolにすると大丈夫だった
	NSDate *dt=[[NSDate alloc ]initWithTimeIntervalSinceNow:tm];
	// runUntilDateは、指定された時間までの間「ループ」を実行する
	// ここの場合[NSRunLoop currentRunLoop]
	[[NSRunLoop currentRunLoop]runUntilDate:dt]; // 上の行と一体にしても同じ
}


/// @brief コレクションビューのレイアウト情報を生成する
/// @param topHeight		ヘッダー部の高さ
/// @param footerHeight		フッター部の高さ
/// @param leftMargin		左余白
/// @param rightMargin		右余白
/// @param interCellX		セル間の横最小幅(セル間横幅は横に入るセル数によって自動計算されるが、これ以下にはならない)
///	@param interCellY		セル間縦最小幅(セル間縦幅は縦に入るセル数によって自動計算されるが、これ以下にはならない)
/// @param sectionHeader	セクションごとのヘッダー高さ
/// @param sectionFooter	セクションごとのフッター高さ
UICollectionViewFlowLayout *makeCollectionViewFlowLayout(
														 CGFloat topHeight		,CGFloat footerHeight,
														 CGFloat leftMargin		,CGFloat rightMargin,
														 CGFloat interCellX		,CGFloat interCellY,
														 CGFloat sectionHeader	,CGFloat sectionFooter
														 )
{
	/// topHeight						I	ヘッダー部の高さ
	/// sectionHeader					I	セクションごとのヘッダー高さ
	/// leftMargin		左余白	+---------------+	+-----------+
	/// rightMargin				|				|	|			|	右余白
	/// interCellX				|				|<->|	セル間の横最小幅(セル間横幅は横に入るセル数によって自動計算されるが、これ以下にはならない)
	/// 						+---------------+	+-----------+
	///	interCellY						：セル間縦最小幅(セル間縦幅は縦に入るセル数によって自動計算されるが、これ以下にはならない)
	///							+---------------+
	/// sectionFooter					I	セクションごとのフッター高さ
	/// footerHeight					I	フッター部の高さ
	UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc]init];
	// 上余白；実際にはセルの上にsectionInset.topも入る
	layout.headerReferenceSize		=CGSizeMake(0,topHeight);
	// 下余白；実際にはセルの下にsectionInset.bottomも入る
	layout.footerReferenceSize		=CGSizeMake(0,footerHeight);
	//
	layout.minimumInteritemSpacing	=interCellX;	// 横セル間の最小サイズ
	layout.minimumLineSpacing		=interCellY;	// 縦セル間の最小サイズ
	// top=セクションの上余白,left=一番左の余白,bottom=セクションの下余白,right=一番右余白
	layout.sectionInset				=UIEdgeInsetsMake(sectionHeader,leftMargin,sectionFooter,rightMargin);
	return layout;
}

/// @brief カスタムcellを定義したxibファイルからセルを得る
/// @param nibName .xibファイル名
/// @return セル
id getCellFromNib(NSString *nibName)
{
	UINib *nib     = [UINib nibWithNibName:nibName bundle:nil];
	NSArray *array = [nib instantiateWithOwner:nil options:nil];
	return array[0]; // UICollectionViewCellまたはUITableViewCell
}




static BOOL fhiddenAnimation=NO; // アニメーション実行中かどうか

/// @brief view1とview2を交互にhiddenしながら切り替えるアニメーションを実行する
/// @param fview1On YES=最終的にview1を表示する , NO=view2を表示する
void hiddenAnimation(UIView *view1,UIView *view2,BOOL fview1On,NSTimeInterval duration)
{
	__block BOOL fhidden; // view1の表示状態（view2は逆）
	__block CGFloat orgAlpha1=view1.alpha;
	__block CGFloat orgAlpha2=view2.alpha;
	CGFloat alphaView1,alphaView2;
	if (fview1On) { // view1を表示する
		fhidden		=NO;
		alphaView1	=orgAlpha1; // 1.0;
		alphaView2	=0.0;
	} else { // view2を表示する
		fhidden		=YES;
		alphaView1	=0.0;
		alphaView2	=orgAlpha2; // 1.0;
	}
	
	// 相互を切り替えるのだから、両方共表示状態にしておかなければならない
	view1.alpha=(view1.hidden? 0.0:orgAlpha1/*1.0*/);
	view2.alpha=(view2.hidden? 0.0:orgAlpha2/*1.0*/);
	view1.hidden=view2.hidden=NO;
	
	// ビューの切り替えはアニメーションで行う
	// .hiddenはアニメーションされないことがあるので.alphaで代用する
	fhiddenAnimation=YES;
	// ブロック構文における循環参照回避のため
	__weak UIView *b_view1=view1;
	__weak UIView *b_view2=view2;
	// アニメーション登録
	[UIView animateWithDuration:duration
						  delay:0.0f
						options:UIViewAnimationOptionCurveLinear	//| UIViewAnimationOptionAllowUserInteraction
					 animations:^{
						 b_view1.alpha	= alphaView1;
						 b_view2.alpha	= alphaView2;
					 }
					 completion:^(BOOL finished) {
						 nslog(@"ビュー切り替え完了");
						 b_view1.hidden	=  fhidden;
						 b_view2.hidden =(!fhidden);
						 b_view1.alpha	=orgAlpha1; //1.0;
						 b_view2.alpha	=orgAlpha2; //1.0;
						 fhiddenAnimation=NO;
					 }
	 ];
}

/// @brief hiddenAnimation()実行中かどうか
/// @return YES=実行中 , NO=実行してない
BOOL isInHiddenAnimation(void)
{
	return fhiddenAnimation;
}


/// senderはUIButtonかUIBarButtonItemで、その.tagを得る
/// @param sender UIButtonまたはUIBarButtonItem
/// @return その.tag値
NSInteger getButtonTag(id sender)
{
	nslog(@"getButtonTag:%@",sender);
	NSInteger tag;
	Class className=[sender class];
	if (className==[UIBarButtonItem class]) {
		UIBarButtonItem *btn=(UIBarButtonItem *)sender;
		nslog(@"btn=%@",btn);
		tag=btn.tag;
	} else {
		UIButton *btn=(UIButton *)sender;
		nslog(@"btn=%@",btn);
		tag=btn.tag;
	}
	return tag;
}

// @brief senderはUIButtonかUIBarButtonItemか調べる
// @return NO=UIButton(というかBarButtonItem以外) , YES=UIBarButtonItem
BOOL isButtonOrBarButtonItem(id sender)
{
	Class className=[sender class];
	return (className==[UIBarButtonItem class]);
}

/// @brief 横画面かどうか調べる
/// @param orientation 画面向き
/// @return YES=横向き NO=それ以外(縦もしくはFaceUp/Down)
BOOL isLandscape(UIInterfaceOrientation orientation)
{
	return (
			orientation == UIInterfaceOrientationLandscapeLeft ||
			orientation == UIInterfaceOrientationLandscapeRight
			);
}

/// @brief 縦画面かどうか調べる
/// @param orientation 画面向き
/// @return YES=縦向き NO=それ以外(縦もしくはFaceUp/Down)
BOOL isPortrait(UIInterfaceOrientation orientation)
{
	return (
			orientation == UIInterfaceOrientationPortrait ||
			orientation == UIInterfaceOrientationPortraitUpsideDown
			);
}



/// @brief リソース内またはpathにある画像をUIImageで返す
/// @discussion まずリソース内に同名のファイルがないか調べ、なければフルパス名pathから読み込む
/// @param path 画像フルパス名
UIImage *getImageFromResourceOrDocuments(NSString *path)
{
	//	nslog(@"path=%@",path);
	NSString *fname=[path lastPathComponent];
	//	nslog(@"lastPathComponent=%@",fname);
	
	if ([fname isEqualToString:path]) {
		// リソース内
		return [UIImage imageNamed:path];
	}
	// path内
	return [UIImage imageWithContentsOfFile:path];
}

/// @brief pointがrectに入っているかどうか調べる
/// @discussion 座標系に注意すること
/// @param point 座標
/// @param rect 調べる領域
/// @return YES=入ってる , NO=入ってない
BOOL isPointInRect(CGPoint point,CGRect rect)
{
	//	nslog(@"rect=%@",NSStringFromCGRect(rect));
	return (point.x>=rect.origin.x && point.y>=rect.origin.y
			&& point.x<rect.origin.x+rect.size.width
			&& point.y<rect.origin.y+rect.size.height);
}

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