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

#import "libs.h"
#import "main.h"
#import <UIKit/UIKit.h>
#import "UIDevice-Hardware.h"

//------------------------------------------------------------------------------
// いろいろコード
//------------------------------------------------------------------------------
// スクリーンサイズ取得
//CGRect r = [[UIScreen mainScreen] bounds];

// 構造体表示
//NSString *NSStringFromCGPoint(CGPoint point);
//NSString *NSStringFromCGSize(CGSize size);
//NSString *NSStringFromCGRect(CGRect rect);
//NSString *NSStringFromCGAffineTransform(CGAffineTransform transform);
//NSString *NSStringFromUIEdgeInsets(UIEdgeInsets insets);
//NSString *NSStringFromUIOffset(UIOffset offset);

//------------------------------------------------------------------------------
// 以下関数群
//------------------------------------------------------------------------------

// UIInterfaceOrientationで回転方向を文字列で返す
NSString *strInterfaceOrientation(UIInterfaceOrientation Orientation)
{
	// 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(@"未定義");
}

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

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

BOOL checkDeviceOrientationOK(void)
// デバイスの方向が縦横であるかどうか
// YES=縦横 , NO=それ以外
{
	UIDeviceOrientation deviceOrientation;
	return (checkDeviceOrientation(&deviceOrientation));
}

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

#ifdef UIORIENTATION
static UIInterfaceOrientation _devOrientation_=UIInterfaceOrientationPortrait;

void understandOrientation(UIInterfaceOrientation Orientation)
{
	// すべてのshouldAutorotateToInterfaceOrientation:に入れ、回転を記録しておく
	nslog(@"understandOrientation(UI)=%d",understandOrientation);
	_devOrientation_=Orientation;
}

UIInterfaceOrientation getOrientation(void)
// 記録している画面方向を得る
{
	nslog(@"getOrientation=%d",_devOrientation_);
	return (_devOrientation_);
}

#else	// iOS6対応のための変更
static UIDeviceOrientation _devOrientation_=UIDeviceOrientationPortrait;

// Orientationはダミー
void understandOrientation(UIInterfaceOrientation Orientation)
{
	// すべてのshouldAutorotateToInterfaceOrientation:/supportedInterfaceOrientations:に入れ、回転を記録しておく
	nslog(@"understandOrientation(dev)");
	UIDeviceOrientation di=[UIDevice currentDevice].orientation;
	switch (di) {
		default:
			_devOrientation_=di;
			nslog(@"更新=%d",(int)_devOrientation_);
			break;
		case UIDeviceOrientationUnknown:
		case UIDeviceOrientationFaceUp:
		case UIDeviceOrientationFaceDown:
			// これらが来たときは前の状態をそのまま返す
			nslog(@"無変更");
			break;
	}
}

// 記録している画面方向を得る
UIDeviceOrientation getOrientation(void)
{
#ifndef UIORIENTATION
	understandOrientation(0);
#endif
	nslog(@"getOrientation=%d",(int)_devOrientation_);
	return (_devOrientation_);
}

#endif

// 縦横方向を得る
NSUInteger getDeviceRotation(void)
{
	nslog(@"getScreenRotation");
	UIDeviceOrientation deviceOrientation;
	checkDeviceOrientation(&deviceOrientation);
	return  (deviceOrientation);
}

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

NSString *getDevicePlatform(void)
{
	// これはUIDevice-Hardware.h/mを使っている
	// https://github.com/erichoracek/UIDevice-Hardware
	return [[UIDevice currentDevice]platform];
}

// getDevicePlatform()ではわかりにくいので詳細の機種名に展開する
NSString *getDeviceModelName(void)
{
	nslog(@"hwmodel=%@",[[UIDevice currentDevice]hwmodel]);
	nslog(@"CPU=%tu",[[UIDevice currentDevice]cpuFrequency]);
	nslog(@"CPU Count=%tu",[[UIDevice currentDevice]cpuCount]);
	nslog(@"BUS=%tu",[[UIDevice currentDevice]busFrequency]);
	nslog(@"totalMemory=%tu",[[UIDevice currentDevice]totalMemory]);
	nslog(@"userMemory=%tu",[[UIDevice currentDevice]userMemory]);
	nslog(@"totalDiskSpace=%@",[[UIDevice currentDevice]totalDiskSpace]);
	nslog(@"freeDiskSpace=%@",[[UIDevice currentDevice]freeDiskSpace]);

	// これは標準メソッド
//	NSLog(@"model=%@/%@",[[UIDevice currentDevice]model],[[UIDevice currentDevice]localizedModel]);
	
	return [[UIDevice currentDevice]modelName];
}

DEVICE_TYPE getDeviceType(void)
// 実行中のデバイス種別を返す
{
	NSString *modelname=[[UIDevice currentDevice]model];
	nslog(@"model=%@",modelname);
	nslog(@"name=%@",[[UIDevice currentDevice]name]);
	nslog(@"systemName=%@"		,[[UIDevice currentDevice]systemName]);
	nslog(@"systemVersion=%@"	,[[UIDevice currentDevice]systemVersion]);
	nslog(@"localizedModel=%@"	,[[UIDevice currentDevice]localizedModel]);
	nslog(@"orientation=%d",(int)[[UIDevice currentDevice]orientation]);
	nslog(@"platform=%@",[[UIDevice currentDevice]platform]);
	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(DEVICE_IPHONE_SIMURATOR);
	return(DEVICE_IPAD_SIMULATOR);
}

DEVICE_TYPE getDeviceType2(void)
// 実行中のデバイス種別を返す（その２）
{
	nslog(@"getDeviceType2");
	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);
	return(DEVICE_UNKNOWN);
}

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);
}

BOOL isDeviceIpadAndPortrait(DEVICE_TYPE devtype)
// iPadの縦画面かどうか
{
	nslog(@"isDeviceIpadAndPortrait");
	if (isDeviceIpad(devtype)) { // iPad
		//UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
		//↑これでは、一度も回転していないときにUIDeviceOrientationUnknownが返ってくるので使っていない(iOSのバグ？)
		//nslog(@"deviceOrientation=%d",deviceOrientation);
		
		nslog(@"UIInterfaceOrientation=%d",(int)_devOrientation_);
		switch (_devOrientation_) {
			case UIDeviceOrientationPortrait:
			case UIDeviceOrientationPortraitUpsideDown:
				nslog(@"iPad縦画面");
				return (YES);
			default:
				// 他の方向の時
				break;
		}
	} // iPhone/iPod touch
	return(NO);
}

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

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 *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);
}

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

NSUInteger getHourMinSec(NSDate *date,NSUInteger *hour,NSUInteger *sec)
// dateから時分秒を数値で返す
// リターン値は分
{
	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);
}

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

#if COMPILE_MODE!=2 // Distributionの時は外す
void _printFrame(NSString *mes,CGRect frame)
{
	nslog2(@"%@=%f,%f,%f,%f",mes,frame.origin.x,frame.origin.y,frame.size.width,frame.size.height);
}

void _printInset(NSString *mes,UIEdgeInsets inset)
{
	nslog2(@"%@=%f,%f〜%f,%f",mes,inset.top,inset.left,inset.bottom,inset.right);
}

void _printSize(NSString *mes,CGSize size)
{
	nslog2(@"%@=%f,%f",mes,size.width,size.height);
}

void _printOffset(NSString *mes,CGPoint point)
{
	nslog2(@"%@=%f,%f",mes,point.x,point.y);
}
#endif

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

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


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

void Alert1(NSString *title,NSString *message,id delegate)
// アラートの表示
// delegate : UIAlertViewDelegate
{
	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]; // 表示したらすぐ帰ってくる
}

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

NSString *dateStringWithoutGMT(NSDate *date)
// NSDate descriptionは〜±hhmmなので、この最後のGMT表記を消した文字列を返す
// 長期保存にはretain/copyすること
// Localizable.stringsに"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=ローカライズする
// mstr=[[NSMutableString alloc]init]で与えること
void leftString(NSMutableString *spc,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);
	[spc appendString:title];
	while ([spc length]<maxlength) {
		[spc appendString:NSLocalStr(@"ZSPC1")]; // テキスト表示用
	}
	//	nslog(@"leftString処理後:%@;%d<%d",spc,[spc length],maxlength);
}

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

// NSString sizeWithFontがiOS7でdeprecatedになったので、共通にする代替関数
CGSize sizeWithFont(NSString *str,UIFont *font,int fdescender)
{
	if (!fdescender) {
		return [str sizeWithAttributes:@{NSFontAttributeName:font}];
	} else {
		CGSize sz=[str sizeWithAttributes:@{NSFontAttributeName:font}];
		sz.height-=font.descender; // gとかyとかの下が切れるのを解消するためdescenderは負数値なので-で加算になる
		return sz;
	}
}

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

NSString *dateFormatJtoE(NSString *date)
// 日時文字列の日英を、現在のローカライズに合わせて変換する
// 長期保存にはretain/copyすること
{
	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]; // UTF16
	unichar c2=[date    characterAtIndex:2]; // UTF16
	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];
}

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

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

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

// 正規表現でのバックスラッシュはC言語では\\になることに注意
#if 0 // これの方がより厳しいらしい
が、iOSではエラーになってしまうので使えない
// 電子メールと比較する正規表現:http://msdn.microsoft.com/ja-jp/library/01escwtf.aspx より
#define EMAIL_EXPRESSION	@"^(?(\x22\x22)(\x22\x22.+?\x22\x22@)|(([0-9a-zA-Z]((\\.(?!\\.))|[-!#\\$%&'\\*\\+/=\\?\\^`\\{\\}\\|~\\w])*)(?<=[0-9a-zA-Z])@))"	\
							@"(?(\\[)(\\[(\\d{1,3}\\.){3}\\d{1,3}\\])|(([0-9a-zA-Z][-\\w]*[0-9A-Z]\\.)+[a-zA-Z]{2,6}))$"
#else
#define EMAIL_EXPRESSION	@"^[a-zA-Z0-9!$&*.=^`|~#%'+\\/?_{}-]+@([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,4}$"	
							// 途中に@が必須
#endif

BOOL isMailAddress(NSString *adrs)
// 文字列がメイルアドレスとして有効かどうか調べる
// NO=不一致 , YES=一致
// 厳密な表現は http://www.din.or.jp/~ohzaki/mail_regex.htm らしいが、あまりに長すぎるので簡易チェック
{
	nslog(@"isMailAddress");
	return cmpRegularExpression(adrs,EMAIL_EXPRESSION);
}

#define SERVER_EXPRESSION	@"^[a-zA-Z0-9!$&*.=^`|~#%'+\\/?_{}-]+([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,4}$"	
							// 英数字.の繰り返し

BOOL isServerAddress(NSString *adrs)
// 文字列がサーバードレスとして有効かどうか調べる
// NO=不一致 , YES=一致
{
	nslog(@"isServerAddress");
	return cmpRegularExpression(adrs,SERVER_EXPRESSION);
}


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

NSString *localKeyName(NSString *defaultName)
{
	// ローカライズ別キー名を返す
	return [NSString stringWithFormat:@"%@:%@",NSLocalizedString(@"LOCAL",nil),defaultName];
}

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

static 
RGB_TABLE rgb[]={//B   R   G
	{  0,  0,  0},
	{250,  0,  0},
	{  0,250,  0},
	{250,250,  0},
	{  0,  0,250},
	{250,  0,250},
	{  0,250,250},
	{255,255,255}
};

UIColor *colorByNo(NSInteger no)
// 番号から色を返す関数
// ローカルに確保された値を返すため、長期保持が必要ならretain/copyが必須(即使うなら必要なし)
{
	if (no>sizeof(rgb)/sizeof(RGB_TABLE)) no=PENCIL_COLOR_WHITE;
//	UIColor *color=[UIColor colorWithRed:rgb[no].r/255.0f green:rgb[no].g/255.0f blue:rgb[no].b/255.0f alpha:1.0f];
	UIColor *color=RGB(rgb[no].r,rgb[no].g,rgb[no].b);
	return color;
}

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

double getSystemVersion(void)
// iOSバージョンを得る
{
	NSString *version = [[UIDevice currentDevice]systemVersion];
	nslog(@"getSystemVersion=%@/SystemName=%@",version,[[UIDevice currentDevice]systemName]);
	nslog(@"Version %@(%f)", version,[version floatValue]);
	// V2.0での変更 : floatValueで返し、それを(double)で型変換しても正しい変換がされないのでdouble型に変更
	return [version doubleValue];
}

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

CGFloat distance(CGPoint sp,CGPoint ep)
// 2点間:sp〜epの距離を求める
{
	// 三角定理
	// 斜め^2=縦^2+横^2
	// fabs/sqrtはdouble関数である
	CGFloat dx=fabs(ep.x-sp.x);
	CGFloat dy=fabs(ep.y-sp.y);
	return (sqrt((dx*dx)+(dy*dy)));
}

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

BOOL isPointInRect(CGPoint point,CGRect rect)
// pointが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);
}

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

NSInteger strArray2No(NSString *str,NSArray *array)
// strがNSString *array[]の何番目にあるか調べる
{
	NSInteger no=0;
	NSEnumerator *emu=[array objectEnumerator];
	NSString *name;
	while (name=[emu nextObject]) {
		if ([str isEqualToString:name]) return (no);
		no++;
	}
	// 見つからなかった
	return (-1);
}

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

NSInteger pathArray2No(NSString *fname,NSArray *pathes)
// fnameがNSString *pathes[]の何番目にあるか調べる
// pathes = フルパス名
// fname  = ファイル名
{
	NSInteger no=0;
	NSEnumerator *emu=[pathes objectEnumerator];
	NSString *path;
	while (path=[emu nextObject]) {
		if ([[path lastPathComponent]isEqualToString:fname]) return (no);
		no++;
	}
	// 見つからなかった
	return (-1);
}

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

NSString *makeNowDateStr(void)
// 現在日時の文字列を得る
// ローカルに確保されるので、必要ならcopy/retainすること
{
	NSDateFormatter *formatter=[[NSDateFormatter alloc]init]; // initはiOS4.0で廃止されている
	[formatter setLocale:[NSLocale currentLocale]]; // Localeの設定(重要)
	[formatter setDateFormat:NSLocalStr(@"DATE")];
	NSDate *date=[NSDate date];
	NSString *string=[formatter stringFromDate:date];
	NSString *str=[NSString stringWithString:string]; // ローカルに確保
	return (str);
}

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

CGSize getFontSize(UIFont *font)
{
	CGSize spSize;
	if (  [font.familyName hasPrefix:@"Hiragino"]
		||[font.familyName hasPrefix:@"AppleGothic"]
		||[font.familyName hasPrefix:@"ApSanskrit"]
		) {
		// ヒラギノ/AppleGothic→pointSize
		spSize=sizeWithFont(ZSPC,font,NO);	// 全角スペース
		nslog(@"spSize補正前:w=%f,h=%f",spSize.width,spSize.height);
		spSize.height=font.pointSize; // lineHeightでは縦に余裕がありすぎるのでpointSizeにする
		nslog(@"spSize補正後:w=%f,h=%f",spSize.width,spSize.height);
	} else {
		// ヒラギノ/AppleGothic以外はWを基準にする
		spSize=sizeWithFont(@"W",font,NO);	// 一番幅の広いフォント(Wの方がZSPCより幅が広い）
		nslog(@"spSize(W):w=%f,h=%f",spSize.width,spSize.height);
	}
	return spSize;
}

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

// 文字列中の指定文字も数を数える
// chrはUTF16
NSInteger countCharInString(NSString *str,ULong chr)
{
	ULong c;
	NSInteger i,cnt=0;
	for (i=0;i<[str length];i++) {
		c=[str characterAtIndex:i];
		if (isSurrogate(c)) {
			// サロゲートペア文字である
			unichar c16L=[str characterAtIndex:++i];
			c=surrogateToUtf16(c,c16L);
		}
		if (c==chr) cnt++; // 発見
	}
	return (cnt);
}

//------------------------------------------------------------------------------
#if 0 // v2.2でマクロ化
NSString *CstringToNSString(char *mes)
// UTF8のC文字列をNSStringに変換する
{
	// autorelease objectなのでループ時は注意
	return [NSString stringWithCString:mes encoding:NSUTF8StringEncoding];
}
#endif
//------------------------------------------------------------------------------

void UIGraphicsBeginImageContextRetina(CGSize size)
// UIGraphicsBeginImageContextのRetina対応版
{
	// qpaque=NO;alpha値有効 , 0.0=scale;デバイスサイズに自動対応
	UIGraphicsBeginImageContextWithOptions(size,NO,0.0);
}

// 画像回転
UIImage *rotateImage(UIImage *img,int degree)
{
	nslog(@"rotateImage:%d",degree);

    CGImageRef imgRef = [img CGImage];
    CGContextRef context; // 内部でコンテキストを確保して処理する
	UIImage *retImg;
    switch (degree) {
		case -180:// 上下鏡像反転
			nslog(@"上下鏡像反転");
			// ←◯
			//  |

			UIGraphicsBeginImageContextRetina(CGSizeMake(img.size.width, img.size.height));
			context = UIGraphicsGetCurrentContext();
            CGContextScaleCTM(context, -1.0, -1.0);
            CGContextRotateCTM(context, M_PI); // -π
			break;
		
		case -90: // 左右鏡像反転
			nslog(@"左右鏡像反転");
			// |
			// ◯→
			UIGraphicsBeginImageContextRetina(CGSizeMake(img.size.width, img.size.height));
			context = UIGraphicsGetCurrentContext();
			CGContextScaleCTM(context, -1.0, -1.0);
            CGContextTranslateCTM(context, -img.size.width,-img.size.height);
			break;

		// 回転
        case 270:
			nslog(@"右270度");
			{
			// -◯
			//  ↓
			UIGraphicsBeginImageContextRetina(CGSizeMake(img.size.height, img.size.width));
			context = UIGraphicsGetCurrentContext();
            CGContextScaleCTM(context, 1.0, -1.0);
            CGContextRotateCTM(context, -M_PI/2.0); // -π/2
			}
            break;
        case 180:
			nslog(@"右180度");
			{
			// ◯→
			// |
			UIGraphicsBeginImageContextRetina(CGSizeMake(img.size.width, img.size.height));
			context = UIGraphicsGetCurrentContext();
#if 1
			CGContextTranslateCTM(context, img.size.width, 0);
			CGContextScaleCTM(context, -1.0, 1.0);
#else
            CGContextTranslateCTM(context, img.size.width, 0);
            CGContextScaleCTM(context, 1.0, -1.0);
            CGContextRotateCTM(context, -M_PI); // -π
#endif
			}
            break;
        case 90:
			nslog(@"右90度");
			{
			// ↑
			// ◯-
			UIGraphicsBeginImageContextRetina(CGSizeMake(img.size.height, img.size.width));
			context = UIGraphicsGetCurrentContext();
            CGContextTranslateCTM(context, img.size.height, img.size.width);
            CGContextScaleCTM(context, 1.0, -1.0);
            CGContextRotateCTM(context, M_PI/2.0); // +π/2
			}
            break;
        default:
			nslog(@"0度");
			//  |
			// ←◯
            nslog(@"上記以外の角度指定の時は0度とみなす");
			// 小細工せずそのまま返す
			return img;
    }
	
    CGContextDrawImage(context, CGRectMake(0, 0, img.size.width, img.size.height), imgRef);
    retImg = UIGraphicsGetImageFromCurrentImageContext(); // Autoreleaseオブジェクトなので注意
	
    UIGraphicsEndImageContext();
    return retImg;
}

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

UIImage *resizedImage(UIImage *img,CGSize size)
// 画像拡大縮小
// X-BASIC'では未使用
{
	printSize(@"resizedImage", size);
    CGFloat widthRatio  = size.width  / img.size.width;
    CGFloat heightRatio = size.height / img.size.height;
    CGFloat ratio 		= (widthRatio < heightRatio) ? widthRatio : heightRatio;
    CGSize resizedSize 	= CGSizeMake(img.size.width*ratio, img.size.height*ratio);
	
    UIGraphicsBeginImageContextRetina(resizedSize);
//	UIGraphicsBeginImageContext(resizedSize);
	
    [img drawInRect:CGRectMake(0, 0, resizedSize.width, resizedSize.height)];
    UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext(); // Autoreleaseオブジェクトなので注意
    UIGraphicsEndImageContext();
	
    return resizedImage;
}

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

UIImage *imageFromView(UIView *view)
// UIViewの内容をUIImageで取得する
// Technical Q&A QA1703 の簡易版
// X-BASIC'では未使用
{
	nslog(@"imageFromView");
	UIGraphicsBeginImageContextRetina(view.frame.size);
//	UIGraphicsBeginImageContext(view.frame.size);
	printSize(@"view.frame.size", view.frame.size);
	//
	CGContextRef context = UIGraphicsGetCurrentContext();
	nslog(@"CGContextRef context = %p",context);
	// 同じ内容を再描画;この方法では再描画されないことが多い
	// どういったタイミングで描画できるのかは現在のところ不明
	[view.layer renderInContext:context];
//	[view.layer setNeedsDisplay] ;
	//
	UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // Autoreleaseオブジェクトなので注意
	UIGraphicsEndImageContext();
	return (image);
}

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

rgba_color getStandardColorTable(NSInteger col)
// 標準的な色のRGBAテーブルを返す
{
	static const rgba_color stdCols[]={
		// R,  G,  B,ALPHA
		{  0,  0,  0,255}, // 0 : α=0にすると透明になってしまうのでだめ , α=255で黒になる
		{  0,  0,128,255},
		{128,  0,  0,255},
		{128,  0,128,255},
		{  0,128,  0,255},
		{  0,128,128,255},
		{128,128,  0,255},
		{128,128,128,255},	// 7
		//
		{  0,  0,  0,128},	// 0+8
		{  0,  0,255,255},
		{255,  0,  0,255},
		{255,  0,255,255},
		{  0,255,  0,255},
		{  0,255,255,255},
		{255,255,  0,255},
		{255,255,255,255},	// 7+8
	};
	if (col>=TBLACK && col<=THWHITE) {
		nslog(@"col=%d:%d,%d,%d,%d",(int)col,stdCols[col].r,stdCols[col].g,stdCols[col].b,stdCols[col].alpha);
		return (stdCols[col]);
	}
	// 未定義色の時
#ifdef DEBUG
	rgba_color clear={255,0,0,200}; // 赤色表示
#else
	rgba_color clear={0,0,0,0};
#endif
	return clear;
}

UIColor *standardColors(NSInteger col)
// 標準的な色を指定する
{
	rgba_color stdCols=getStandardColorTable(col);
	return (RGBA(stdCols.r,stdCols.g,stdCols.b,stdCols.alpha)); // UIColor
}

void setColor(CGContextRef context,int r,int g,int b,int alpha)
// 色設定
// r/g/b/alpha=0〜255
{
	@autoreleasepool {
		nslog(@"setColor(context=%@):%d/%d/%d/%d",context,r,g,b,alpha);
		UIColor *color=[UIColor colorWithRed:_cf(r)
									   green:_cf(g)
										blue:_cf(b)
									   alpha:_cf(alpha)];
		// strokeとfill両方設定する
		CGContextSetStrokeColorWithColor(context,color.CGColor);
		CGContextSetFillColorWithColor(  context,color.CGColor);
		// フォント描画色も設定
		[color set];
	}
}

rgba_color getWithStandardColor(int r,int g,int b,int alpha_)
// 色構造体取得
// r/g/b/alpha=0〜255、またはr<0で標準色
{
	rgba_color color;
	if (r<0) { // 標準色設定(r=-colで指定)
		nslog(@"getStandardColorTable");
		color=getStandardColorTable(-r); // 標準色設定
		if (alpha_>=0) {
			color.alpha=alpha_;
		}
	} else {
		color.r=r;
		color.g=g;
		color.b=b;
		color.alpha=alpha_;
	}
	nslog(@"r/g/b/a=%d,%d,%d,%d",color.r,color.g,color.b,color.alpha);
	return (color);
}

// 16ビットのstyleの指定をiOSの線分パターン指定に変換する
// CGFloat lengths[16]; // 最大16bitで指定なので
// lineWidth=1の時専用
size_t setLineStyleWith16bit(NSUInteger style,CGFloat lengths[])
{
	nslog(@"setLineStyleWith16bit");
	// F0F0=11110000|〜
	// CCCC=11001100|〜
	// AAAA=10101010|〜
	// 5555=01010101|01010101 → 1010101010101010
	size_t count=0;
	nslog(@"style=$%04x",(int)style);
	// 上位ビット詰め
	// iOSのline系は、必ず描画から始めなければならない。
	// そのため上位の0は取り除く→最下位からに入れる。
	int hlen=0; // 除いた上位ビットの数
	int bit=BIT15;
	int i=0;
	while (!(style&bit)) {
		hlen++;
		style<<=1;
		i++;
	}
	nslog(@"補正後style=$%04x(%dビット削除)",(int)style,hlen);
	// style→length変換
	int len=0;
	BOOL fset=YES; // 最初はON指定から
	for (;i<16;i++) {
		if (style&bit) { // dot=ON
			if (fset) { // 連続ONの長さ
				len++;
			} else { // !fset
				// OFF→ON
				lengths[count++]=len;
				len=1; // ON１つ目
				fset=YES;
			}
		} else { // dot=OFF
			if (!fset) { // 連続OFFの長さ
				len++;
			} else { // fset
				// ON→OFF
				lengths[count++]=len;
				len=1; // OFF１つ目
				fset=NO;
			}
		}
		bit>>=1;
	}
	// 残りを入れる
	lengths[count++]=len;
	if (hlen>0) {
		// 上位ビットの取り除いた分(off分)は最後に入れる
		lengths[count++]=hlen;
	}
#ifdef DEBUG
	printf("count=%ld\n",count);
	for (i=0;i<count;i++) {
		printf("%f,",lengths[i]);
	}
	printf("\n");
#endif
	// その数
	return (count);
}

void setLineStyle(CGContextRef context,NSUInteger style)
// 描画スタイルを設定する
// lineWidth=1の時しか予想通りの描画をしない
{
	if (style!=0 && style!=0xffff) {
		CGFloat phase=0; // lengthの先頭から使用
		CGFloat lengths[16]; // 最大16bitで指定
		size_t count=setLineStyleWith16bit(style,lengths);
		nslog(@"setLineStyle:%ld",count);
		CGContextSetLineDash(context,phase,lengths,count);
	} else {
		// ラインスタイル指定なし
		nslog(@"setLineStyle none");
		CGContextSetLineDash(context,0,NULL,0);
	}
}

void strokeOrFill(CGContextRef context,BOOL ffill)
// 描画指定
{
	nslog(@"strokeOrFill");
	if (ffill) { // 塗りつぶし
		CGContextFillPath(context);
	} else { // 線だけ
		CGContextStrokePath(context);
		// これを発行すると点の結合が解除される（だから、これの後はMoveToPointが不要）
	}
}

CGContextRef MyCreateBitmapContext(int width,int height,CGBitmapInfo bitmapInfo)
// 独立したビットマップのコンテキストを確保する
{
	nslog(@"MyCreateBitmapContext=%d*%d/bitmapInfo=$%x",width,height,bitmapInfo);
	
    int bitmapBytesPerRow = (width * 4); // 1ピクセル4バイト(ULong)
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate (
												  NULL,	// iOS4.0以降で自動確保の印
												  width,
												  height,
												  8,		// bits per component
												  bitmapBytesPerRow,
												  colorSpace,
												  bitmapInfo
												  );
    CGColorSpaceRelease( colorSpace );
#ifdef DEBUG
    if (context== NULL) {
        nslog(@"Context not created!");
    }
#endif
	nslog(@"context=%@",context);
    return context;
}

#ifdef DEBUG
static int cnt=0;
#endif

void UIGraphicsPushContext2(CGContextRef context)
{
#ifdef DEBUG
	if (cnt>0) {
		printf("UIGraphicsPushContext2がネスティングしている\n");
	}
	printf("UIGraphicsPushContext2:cnt=%d\n",cnt++);
#endif
	// 切り替え
	UIGraphicsPushContext(context);
}

void UIGraphicsPopContext2(void)
{
#ifdef DEBUG
	if (cnt>1) {
		printf("UIGraphicsPopContext2がネスティングしている\n");
	}
	printf("UIGraphicsPopContext2:cnt=%d\n",--cnt);
#endif
	// コンテキスト復帰
	UIGraphicsPopContext();
}

void UIGraphicsRotate180(CGContextRef context,UIView *view)
// CoreGraphicsの座標系をUIViewの座標系に変換する
{
	CGFloat height = view.bounds.size.height;
    CGContextTranslateCTM(context, 0.0, height);
    CGContextScaleCTM(context, 1.0, - 1.0);
}

CGRect getRetinaSize(CGRect rect)
// rectをRetinaサイズに変換する
// Retina出ないときはそのまま
{
	CGFloat screenScale = [[UIScreen mainScreen] scale]; // Retinaディスプレイの倍率を得る
	rect.size.width *=screenScale;
	rect.size.height*=screenScale;
	return (rect);
}

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

NSString *getLanguage(void)
// iOSの言語設定を得る
{
	NSArray *languages = [NSLocale preferredLanguages];
//	nslog(@"preferredLanguages=%@", languages);
	NSString *currentLanguage = languages[0]; // 先頭が現在選択中言語
//	nslog(@"currentLanguage=%@", currentLanguage);
	return (currentLanguage); // ja/en等
}

BOOL isLocalizeJapan(void)
// ローカライズが日本かどうか調べる
// 日本語のとき : YES
// なぜかOSのローカライズが効かない時用
{
	return [getLanguage() isEqualToString:@"ja"];
}

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

void EnterRunLoop(NSTimeInterval tm)
// runUntilDateは、指定された時間までの間「ループ」を実行する
// ここの場合[NSRunLoop currentRunLoop]
// 0.0の場合一瞬だけ
{
	// NSDateだけだとメモリがどんどん食われてしまう
	// 実験の結果autorelease poolにすると大丈夫だった
	NSDate *dt=[[NSDate alloc ]initWithTimeIntervalSinceNow:tm];
	// runUntilDateは、指定された時間までの間「ループ」を実行する
	// ここの場合[NSRunLoop currentRunLoop]
	[[NSRunLoop currentRunLoop]runUntilDate:dt]; // 上の行と一体にしても同じ
}

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

#ifdef DEBUG
void dumpSubView(UIView *view)
// UIViewの構成クラスをダンプする : iOS SDK Hacksより
{
	const char *class_getName(Class cls);
	nslog(@"Class=%s",class_getName([view class]));
	for (UIView *subview in [view subviews]) {
		dumpSubView(subview);
	}
}
#endif

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

// メモリをクリアする
// Cygwin上には存在しないので代替
UByte *memclr(UByte *bp,size_t size)
{
//	nslog(@"bp=%p〜%p(size=%ld)",bp,bp+size-1,size);
	while (size-->0) {
		*bp++=0;
	}
	return (bp);
}

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

// 68形式で.wを読み出す
UWord rd68w(UByte *b)
{
	UWord w=(UWord)*b++;
	w<<=8;
	w|=(*b);
	return (w);
}

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

// 68形式で.wを読み出す
ULong rd68l(UByte *b)
{
	ULong l=(ULong)*b++;
	l<<=8;
	l|=(*b++);
	l<<=8;
	l|=(*b++);
	l<<=8;
	l|=(*b);
	return (l);
}

//------------------------------------------------------------------------
// https://ja.wikipedia.org/wiki/Unicode#.E3.82.B5.E3.83.AD.E3.82.B2.E3.83.BC.E3.83.88.E3.83.9A.E3.82.A2 参照

// UTF16文字コードをサロゲートペアに変換する
// 文字コードから0x10000を引いて1番左の桁を"2"から"1"にする。これをXとする。
// Xを0x400で割ってその商を0xD800に足す。これを「上位サロゲート」とする。
// Xを0x400で割ってその剰余を0xDC00に足す。これを「下位サロゲート」とする。
// 上位サロゲート、下位サロゲートの順番で出力する。
void utf16ToSurrogate(ULong utf16,UWord *hi,UWord *lo)
{
	*hi = (utf16 - 0x10000) / 0x400 + 0xD800;
	*lo = (utf16 - 0x10000) % 0x400 + 0xDC00;
}

// サロゲートペアをUTF16文字コードに変換する
ULong surrogateToUtf16(UWord hi,UWord lo)
{
	return 0x10000 + (hi - 0xD800) * 0x400 + (lo - 0xDC00);
}

// ２バイトがサロゲートペアの上位２バイトかどうか
BOOL isSurrogate(ULong utf16)
{
	return (utf16>=0xd800 && utf16<=0xdbff);
}
	
//------------------------------------------------------------------------
