//
//  tConsole2.m
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2012/09/15.
//  Copyright (c) 2012-15年 AIG-Soft. All rights reserved.
//

#import "tConsole2.h"

@implementation tConsole2

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

-(void)init2
{
	printSelf();
	[super init2];
	self.textField	=nil;
	self.alertView	=nil;
	self.actionSheet=nil;
	self.ftimeWaitDone=YES;
	
	// KVO設定
	[self addObserver:self forKeyPath:OBSERVE_FINPUTDONE options:NSKeyValueObservingOptionNew context:NULL];
}

-(void)doThread:(SEL)selector
// メインスレッドで実行させて終了を待つ
{
	printSelf();
	// メインスレッドに入れる
	[self performSelectorOnMainThread:selector
						   withObject:nil
						waitUntilDone:YES];	// 命令終了を待つ
	nslog(@"命令発行終了");
}

// メインスレッドで実行させて終了を待つ
// 引数付き
-(void)doThread:(SEL)selector withObject:(id)object
{
	printSelf();
	nslog(@"doThread");
	// メインスレッドに入れる
	[self performSelectorOnMainThread:selector
						   withObject:object
						waitUntilDone:YES];	// 命令終了を待つ
	nslog(@"命令発行終了");
}

//----------------------------------------------------------------------------------------------
// キーボード表示/消去通知
//----------------------------------------------------------------------------------------------

-(void)keyboardNotification:(BOOL)onoff
// キーボード表示通知の設定
{
	if (onoff) { // ON
		nslog(@"keyboardNotification on");
		// キーボード表示時に通知させる
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
		// キーボード消去時に通知させる
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:)  name:UIKeyboardDidHideNotification  object:nil];
		//
	} else { // OFF
		nslog(@"keyboardNotification off");
		// キーボード通知の解除
		[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
		// キーボード消去時に通知させる
		[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification  object:nil];
	}
}
//----------------------------------------------

-(void)keyboardWillShow:(NSNotification *)notification
{
	printSelfs(@"%@",notification);
	if (keyboardFrameEnd.size.height!=0) {
		nslog(@"すでに表示中なので何もしない");
		// なぜかキーボードがすでに表示されているにもかかわらず再度通知がかかることがあるので
		return;
	}
	
	// キーボードの表示開始時の場所と大きさを取得
	//	keyboardFrameBegin = [[notification.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
	//	printFrame(@"キーボード表示開始時サイズ", keyboardFrameBegin);
	
	// キーボードの表示完了時の場所と大きさを取得
	keyboardFrameEnd = [(notification.userInfo)[UIKeyboardFrameEndUserInfoKey] CGRectValue];
	printFrame(@"キーボード表示終了時サイズ", keyboardFrameEnd); // {origin.x,y},{size.width,height}}
	// iOSバグ：横画面において、キーボードwidthとheightが逆になってる(iPadで確認、iPhoneは不明)
	// deviceOrientationではシミュレーター上では方向が得られないので、簡易的に幅の大小で判定している
	CGFloat w=keyboardFrameEnd.size.width;
	if (w<keyboardFrameEnd.size.height) {
		nslog(@"横画面なのでwidth←→heightを入れ替える");
		keyboardFrameEnd.size.width=keyboardFrameEnd.size.height;
		keyboardFrameEnd.size.height=w;
		printFrame(@"修正キーボード表示終了時サイズ", keyboardFrameEnd); // {origin.x,y},{size.width,height}}
	}
}

-(void)keyboardDidHide:(NSNotification *)notification
{
//	printSelfs(@"%@",notification);
	if (!_finputDone) {
		// まだ入力中に消されたときは再表示
		[_textField becomeFirstResponder];
	} else {
		// キーボードを消した印
		keyboardFrameEnd.size.height=0;
	}
	// iOS8で外部キーボードモードの時はキーボードが表示されない＝消えることもないのでここには来ない
}

//----------------------------------------------------------------------------------------------
// input
//----------------------------------------------------------------------------------------------

-(void)input:(NSString *)defInput_ keyType:(UIKeyboardType)keyType_ placefolder:(NSString *)placefolder_
{
	printSelf();
	self.message =placefolder_;
	self.defInput=defInput_;
	keyType		 =keyType_;
	keyboardFrameEnd.size.height=0; // キーボードを表示してない印
	[self doThread:@selector(inputSub_:)]; // メインスレッドで実行させる
	
	// 入力終了待ちは、別途外部でwaitUntilInputEndすること
}

// defInput=デフォルト入力(X-BASICでは使わない)
-(void)input:(NSString *)defInput_ keyType:(UIKeyboardType)keyType_
{
	printSelf();
	
	self.message =@""; // placeholder
	self.defInput=defInput_;
	keyType		 =keyType_;
	keyboardFrameEnd.size.height=0; // キーボードを表示してない印
	[self doThread:@selector(inputSub_:)]; // メインスレッドで実行させる
	
	// 入力終了待ちは、別途外部でwaitUntilInputEndすること
}

- (void)inputSub_:(id)view
// これはメインスレッドで実行される
{
	printSelf();
	@autoreleasepool { // 不要かもしれないけど、別スレッド実行のでのお約束
		nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
		// ここで表示に関わるすべての要素を設定しないと正しく表示されない(iOSのバグ)

		_textField=[[UITextField alloc]init];
		_textField.delegate=self;
		_textField.text	=self.defInput;
		_textField.placeholder=self.message;

		self.defInput=nil;
		self.message =nil;
		
		_textField.font=[UIFont fontWithName:self.fontName size:self.pointSize];
		_textField.textColor				=RGBA2(self.rgbaColor);	// v1.6でバグ取り
		_textField.borderStyle				=UITextBorderStyleBezel;
		_textField.autocorrectionType		=UITextAutocorrectionTypeNo;
		_textField.spellCheckingType		=UITextSpellCheckingTypeNo;
		_textField.autocapitalizationType	=UITextAutocapitalizationTypeNone;
		_textField.keyboardType				=keyType;
		
		// 現在のカーソル位置から
		CGPoint point={self.tx,self.ty};
		nslog(@"(tx,ty)=(%f,%f)",self.tx,self.ty);
		if ([super isTextMode]) {
			// テキストモード
			point.x*=self.fontWx;
			point.y*=self.fontWy;
		} // グラフィックモードの時は座標そのままを使う
		
		// v2.2 : textFieldは独自のUIViewなので、ベースの変形情報を継承させなければならない、ようである。
		// しかし、なぜか表示y座標は変換されないので、強制変換してやる必要がある。
		CGAffineTransform m=baseAffine;
		[_textField.layer setAffineTransform:m];
		
		CGRect frame; // =_textField.frame;
		frame.size.width =frame0.size.width-point.x-4; // 画面右側の空き領域
		frame.size.height=self.fontWy+4; // sizeには枠線領域分も含まれるようなので、少し広げておく

		// V2.1で鏡像変換を.layerにかけたが、textFieldだけはなぜかｙ座標が変換されないので強制変換する
		nslog(@"frame.size=%f,%f",frame0.size.width,frame0.size.height);
		point.y=frame0.size.height-point.y-frame.size.height;
		nslog(@"point=%f,%f",point.x,point.y);

		frame.origin=point;
		_textField.frame=frame;
		printFrame(@"_textField", frame);
		//
		_finputDone=NO; // 入力待ち;KVOを発生させないためself.にしない
		_textField.hidden=NO;

		[self addSubview:_textField];
		
		// キーボードを表示
		// ここでは表示位置が任意なので、キーボードありなりによる画面制御はしない
		[self keyboardNotification:YES];
		[_textField becomeFirstResponder];
	// この後マルチタスクでUITextFiled処理をされるので、ここでは入力を持ってリターンできない
	//
	}
}

// 以下デリゲート
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField1
{
	printSelf();
//	nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
	return YES;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField1
{
	printSelf();
//	nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField1
{
	printSelf();
//	nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
	return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField1
{
	printSelf();
//	nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField1
{
	printSelf();
	nslog(@"isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
	// ここで消去処理などをしないと*EndEditing:は呼び出されない
	nslog(@"_textField.text=%@",_textField.text);
	[self resignFirstResponder];		// キーボードを消す
	[self keyboardNotification:NO];
	_textField.hidden=YES;
	[_textField removeFromSuperview];	// 表示消去
	self.finputDone=YES; // 入力終了(KVO)
	// YES：デフォルト動作をする
	// NO : それ以外
	return (NO);
}
// 以上デリゲート

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

// KVOによる変更通知受信
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	// OS経由で受信するだけ
	nslog(@"KVO:finputDone=%@",object);
}

// input入力待ち
// X-BASICでは必要
// ここはXBから呼び出される＝サブスレッド中
-(NSString *)waitUntilInputEnd
{
	printSelf();
	[self wait_:NO]; // 入力待ち
	//
	NSString *str=[NSString	stringWithString:_textField.text];
	self.textField=nil; // release
	return str;
}

// 入力等待ち
-(BOOL)wait_:(BOOL)fbreakEnable
{
	int checkBreakIniOS(void);
	nslog(@"wait_:isMainThread=%d/isMultiThreaded=%d",[NSThread isMainThread],[NSThread isMultiThreaded]);
	nslog(@"self.finputDone=%d",self.finputDone);
	while (!self.finputDone) {
		// finputDoneはKVOで変更される
		EnterRunLoop(0.0); // これがないと、他の並行動作・イベント取得処理が全て無視されておかしくなる(TouchEndedとか)
		if (fbreakEnable) {
			if (checkBreakIniOS()) {
				// Breakが押された
				if ([waitTimer isValid]) [waitTimer invalidate]; // タイマー待ち中だったときは禁止する
				self.ftimeWaitDone=YES;
				nslog(@"wait_ break");
				return(YES);
			}
		}
	}
	// 正常終了
	self.ftimeWaitDone=YES; // 終了した印
	nslog(@"wait_ end");
	return (NO);
}

// selectMenu/2終了待ち
// X-BASICでは必要
// ここはXBから呼び出される＝サブスレッド中
-(void)waitUntilSelectEnd
{
	printSelf();
	[self wait_:YES]; // 入力待ち
}

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

-(void)waitTimeEnd
{
	printSelf();
	self.finputDone=YES; // KVOが発生する
	self.ftimeWaitDone=YES; // 時間待ちが終了した印
}

// メインスレッドで時間待ちをタイマーに入れる
-(void)waitTimeInMainThread:(NSValue *)arg
{
	@autoreleasepool { // 別スレッドのお約束
		printSelf2();
		NSTimeInterval tm;
		[arg getValue:&tm];
		nslog(@"%@ → %f",arg,tm);
		self.ftimeWaitDone=NO; // 終了してない印
		waitTimer=[NSTimer scheduledTimerWithTimeInterval:tm
													target:self
												  selector:@selector(waitTimeEnd) // 終了時呼び出し
												  userInfo:nil
												   repeats:NO
					];
		nslog(@"isValid=%d/fireDate=%@/timeInterval=%f",[waitTimer isValid],[waitTimer fireDate],[waitTimer timeInterval]);
		// [waitTimer timeInterval]はrepeats=YESの時のみ設定される(とドキュメントにも書いてある)
		[[NSRunLoop currentRunLoop] addTimer:waitTimer forMode:NSDefaultRunLoopMode];
	}
}

// tm=秒 ; <1.0=*100ms
-(BOOL)waitTime:(NSTimeInterval)tm fwait:(BOOL)fwait
{
	printSelf();
	nslog(@"(%f):fwait=%d",tm,fwait);
	
	if (!self.ftimeWaitDone) {
		nslog(@"まだ前のウエイトが終わってない");
		return(NO);
	}
	
	@autoreleasepool {
		self.finputDone=NO;	// 時間待ち中

		// timerはメインスレッドで実行しなければいけないようなので、一旦メインスレッドに渡す
		NSValue *val=[NSValue valueWithBytes:&tm objCType:@encode(NSTimeInterval)];
		[self doThread:@selector(waitTimeInMainThread:) withObject:val];
		//
		if (fwait) [self wait_:YES]; // 時間経過待ち
	}
	
	nslog(@"waitTime end");
	return(YES);
}

//----------------------------------------------------------------------------------------------
// SelectMenu
//----------------------------------------------------------------------------------------------

-(void)selectMenu:(NSString *)title_ menu:(NSArray *)menues_ message:(NSString *)message_
{
	printSelf();

	self.message=message_;
	self.defInput=title_;
	self.menues=menues_;
	
	[self doThread:@selector(selectMenuSub_)]; // メインスレッドで実行させる
	
	// 入力終了待ちは、別途外部でwaitUntilSelectEndすること
}

-(void)selectMenu:(NSString *)title_ menu:(NSArray *)menues_
{
	printSelf();
	
	self.message=nil;
	self.defInput=title_;
	self.menues=menues_;

	[self doThread:@selector(selectMenuSub_)]; // メインスレッドで実行させる

	// 入力終了待ちは、別途外部でwaitUntilSelectEndすること
}

-(void)selectMenuSub_
{
	printSelf();
	@autoreleasepool { // 不要かもしれないけど、別スレッド実行のでのお約束
		_alertView=[[UIAlertView alloc]init];
		_alertView.delegate=self;
		_alertView.message=self.message;
		_alertView.title=self.defInput;
		nslog(@"title=%@/message=%@",self.defInput,self.message);
		self.defInput=nil;
		
		// 2つなら横に、3つ以上なら縦に並ぶ
		// 最終項目が少し離れる
		for (NSString *s in self.menues) {
			[_alertView addButtonWithTitle:s];
		}
		self.menues=nil;
		
		nslog(@"numberOfButtons=%d"			,(int)_alertView.numberOfButtons);
		nslog(@"firstOtherButtonIndex=%d"	,(int)_alertView.firstOtherButtonIndex);	// -1
		nslog(@"cancelButtonIndex=%d"		,(int)_alertView.cancelButtonIndex);		// -1
		
		//!!test!!
//		self.finputDone=NO;		// 入力待ち;KVOを発生させないためself.にしない
		_finputDone=NO;		// 入力待ち;KVOを発生させないためself.にしない
		[_alertView show];	// 表示
		self.alertView=nil;	// 実開放は後
	}
}

- (void)alertView:(UIAlertView *)alertView1 clickedButtonAtIndex:(NSInteger)buttonIndex
{
	_selectNo=buttonIndex; // 番号は0〜
	nslog(@"alertView.buttonIndex=%d",(int)buttonIndex);
	[_alertView	dismissWithClickedButtonIndex:buttonIndex animated:YES]; // ここで開放されるはず
	self.finputDone=YES; // 入力終了
}

//----------------------------------------------------------------------------------------------
// SelectMenu2
//----------------------------------------------------------------------------------------------

-(void)selectMenu2:(NSString *)title menu:(NSArray *)menues_
{
	printSelf();
	
	self.defInput=title;
	self.menues=menues_;
	
	// 描画はメインスレッドで行わなければならないので渡す
	[self doThread:@selector(selectMenuSub2_)]; // メインスレッドで実行させる
	
	// 入力終了待ちは、別途外部でwaitUntilSelectEndすること
}

-(void)selectMenuSub2_
{
	printSelf();
	@autoreleasepool { // 不要かもしれないけど、別スレッド実行のでのお約束
		_actionSheet=[[UIActionSheet alloc]init];
		_actionSheet.delegate=self;
		_actionSheet.title=self.defInput;
		self.defInput=nil;

		// 常に縦に並ぶ
		// 最終項目も離れない
		for (NSString *s in self.menues) {
			[_actionSheet addButtonWithTitle:s];
		}
		self.menues=nil;
		
		nslog(@"numberOfButtons=%d"			,(int)_actionSheet.numberOfButtons);
		nslog(@"firstOtherButtonIndex=%d"	,(int)_actionSheet.firstOtherButtonIndex);	// -1
		nslog(@"cancelButtonIndex=%d"		,(int)_actionSheet.cancelButtonIndex);		// -1

		_actionSheet.actionSheetStyle=UIBarStyleDefault;
		self.finputDone=NO;	// 入力中;KVOを発生させないためself.にしない
		[_actionSheet showInView:self];
		self.actionSheet=nil; // 実開放は後
	}
}

- (void)actionSheet:(UIActionSheet *)actionSheet1 clickedButtonAtIndex:(NSInteger)buttonIndex
{
	printSelfs(@"buttonIndex=%d",(int)buttonIndex);
	// Break/メニュー外画面が押されるとbuttonIndex=-1で呼び出されるので無視する
#ifdef DEBUG
	if (buttonIndex<0) {
		nslog(@"Break/menu外タッチ");
		// しかし困ったことに、この時点でactionSheetは自動的に消されてしまう
		//return;
	}
#endif
	_selectNo=buttonIndex; // 番号は0〜/-1=BREAK/DEBUGまたは画面外タッチ
	[_actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES]; // ここで開放されるはず
	self.finputDone=YES; // 入力終了(KVO)
	nslog(@"actionSheet end");
}

// selectMenu2も終了待ちはwaitUntilSelectEnd

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

-(void)dealloc
{
	// key-value通知解除
	[self removeObserver:self forKeyPath:OBSERVE_FINPUTDONE];
}

@end
