//----------------------------------------------------------------------------------------------
//
//	tConsole.m : テキスト描画用コンソール
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2012/09/11.
//  Copyright (c) 2012-15年 AIG-Soft. All rights reserved.
//

#import "tConsole.h"
#import <CoreGraphics/CoreGraphics.h>
#import "libs.h"
#import "main.h"
#include "code2code.h"
#import "NSString+drawiOS7.h"

@implementation tConsole

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

- (void)init2
{
	printSelf();
	[super init2];
	
	// デフォルト設定
	self.widthx=self.widthy=0;	// グラフィック座標を使う
	_pointSize0	=(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad? 16:13); // 一応setupに合わせてある
	[self font:DEFFONT pointSize:_pointSize0];
	
	self.clearsContextBeforeDrawing=NO;	// 描画前に消さない
	//
	[self setHome]; // cls相当はなされているのでhomeだけで十分
	// ↓contextがないので、画面に対する設定は無視されるが、内部には記録される
	[self color:-THWHITE g:0 b:0 alpha:-1];
	//
	beepPlay=[[SoundPlay alloc]initWithFile:@"beep1" resourceType:@"aiff"];
	
	// v3.20
	// 文字幅計算基準文字の設定
	self.baseCharacter=nil; // 標準設定
}

-(void)start
{
	[super start];
	_fUpScroll=YES; // スクロールアップ有効(V2.0)
}

-(BOOL)isTextMode
// テキスト座標モードかどうか
{
	return (self.widthx!=0);
}

//---------------------------------------------------------------------------------------
// 内部関数
// 基本的に単独での呼び出し不可
//---------------------------------------------------------------------------------------
// 実描画を伴わない処理

-(void)setHome
{
	printSelf();
	_tx=_ty=0;
}

// カーソル移動
-(void)setCursorMove:(NSInteger)direc cnt:(int)cnt
{
	if ([self isTextMode]) { // テキスト座標モードでのみ有効
		while (cnt-->0) {
			switch (direc) { // 方向別
				case CURSOR_DIREC_LEFT: // 左
					//nslog(@"→:%d",cnt);
					_tx--;
					if (_tx>=0) break;
					_tx=_widthx-1;
					// 1つ上の行へ
				case CURSOR_DIREC_UP: // 上
					//nslog(@"↑:%d",cnt);
					if (_ty>0) _ty--;
					break;
					
				case CURSOR_DIREC_RIGHT: // 右
					//nslog(@"←:%d",cnt);
					_tx++;
					if (_tx<_widthx) break;
					_tx=0;
					// 1つ下の行へ
				case CURSOR_DIREC_DOWN: // 下
					//nslog(@"↓:%d",cnt);
					if (_ty<_widthy-1) _ty++;
				default:
					break;
				
				case CURSOR_DIREC_DOWN_WITH_SCROLL: // 下＋スクロール付き v2.0
					if (_ty<_widthy-1) _ty++;
					else {
						if (_fUpScroll) [super txScroll:SCROLL_UP value:_fontWy fContext:YES];
					}
					break;
					
				case CURSOR_DIREC_UP_WITH_SCROLL: // 上＋スクロール付き v2.0
					nslog(@"CURSOR_DIREC_UP_WITH_SCROLL:ty=%d",(int)_ty);
					if (_ty>0) _ty--;
					else {
						[super txScroll:SCROLL_DOWN value:_fontWy fContext:YES];
					}
					break;
			}
		} // cnt<=0の時は移動させない
	}
}

//---------------------------------------------------------------------------------------
// 実描画を伴うこともあるが、下位関数が処理するのでこれ自体では考慮しなくていい処理

// コントロールコードの表示処理
// 1文字の時はとにかくここに来る
// リターン値 : YES=コントロールコードの処理をした , NO=してない（普通の1文字)
-(BOOL)printCtrlCode:(unichar)code
{
	printSelfs(@"$%x",code);
	nslog(@"control=$%x",code);
	switch (code) {
#if 0
		case DISP_CTRL_BS:
			nslog(@"DISP_CTRL_BS");
			// ここで処理実体を呼び出す
			return (YES);
#endif
		case DISP_CTRL_CLEARLINE:
			nslog(@"DISP_CTRL_CLEARLINE");
			[self clearLine];
			return (YES);

		case DISP_CTRL_CLEAR_EOS: // v2.2
			nslog(@"DISP_CTRL_CLEAR_EOS");
			[self clearEndOfScreen];
			return (YES);
			
		case DISP_CTRL_BEEP:
			nslog(@"DISP_CTRL_TAB");
			[beepPlay play];
			return (YES);
			
		case DISP_CTRL_TAB:
			nslog(@"DISP_CTRL_TAB");
			[self setTab:1];
			return (YES);
			//
		case DISP_CTRL_CLS: // cls
			nslog(@"DISP_CTRL_CLS");
			[super wipe];
			// 以下homeへ
		case DISP_CTRL_HOME: // home
			nslog(@"DISP_CTRL_HOME");
			[self setHome];
			return (YES);
		case DISP_CTRL_CURSOR_RIGHT:
			nslog(@"DISP_CTRL_RIGHT");
			[self setCursorMove:CURSOR_DIREC_RIGHT cnt:1];
			return (YES);
		case DISP_CTRL_CURSOR_LEFT:
			nslog(@"DISP_CTRL_LEFT");
			[self setCursorMove:CURSOR_DIREC_LEFT cnt:1];
			return (YES);
		case DISP_CTRL_CURSOR_UP:
			nslog(@"DISP_CTRL_UP");
			[self setCursorMove:CURSOR_DIREC_UP cnt:1];
			return (YES);
			//
		case DISP_CTRL_CR:	// 行先頭へ
			nslog(@"DISP_CTRL_CR");
			_tx=0;
			return (YES);
			//
		case DISP_CTRL_LF:	// 改行 v2.0
			nslog(@"DISP_CTRL_LF");
			_tx=0;
			[self setCursorMove:CURSOR_DIREC_DOWN_WITH_SCROLL cnt:1];
			return (YES);

		case DISP_CTRL_CURSOR_DOWN:
			nslog(@"DISP_CTRL_DOWN");
			[self setCursorMove:CURSOR_DIREC_DOWN cnt:1];
			return (YES);

		case DISP_CTRL_RCR:	// 逆改行 v2.1
			nslog(@"DISP_CTRL_RCR");
			_tx=0;
			[self setCursorMove:CURSOR_DIREC_UP_WITH_SCROLL cnt:1];
			return (YES);
			
		default:
			// beepも表示コードとしては無視する
			if (code<' ') return (YES); // 他のコントロールコードは無視させる
			break;
	}
	// SJIS/UTF8も認識せずここで返る
	return (NO);
}

// TABの移動
-(void)setTab:(int)cnt
{
	if ([self isTextMode]) { // テキスト座標モードでのみ有効
		if (cnt>=0) { // x=2,tab=2 → x=16
			nslog(@"TAB数=%d",cnt);
			int dx=(int)_tx; // int化
			nslog(@"(int)tx=%d",dx);
			dx=TDRAW_TAB_TABS-(dx%TDRAW_TAB_TABS); // 移動させるべき桁数(最後の1TAB分)
			nslog(@"tx=%f,最終tab桁=%d",_tx,dx);
			// カーソル移動だけじゃなくスペース表示が必要
			// C文字列を作ってNSStringに変換してprintに渡す；高速化？のため
			int sz=dx+(cnt-1)*TDRAW_TAB_TABS;
			char *tabs=alloca(sz+1); // +1 for EOS
			memset(tabs,' ',sz);
			tabs[sz]='\0';

			@autoreleasepool {
				NSString *nstabs=@(tabs);
				[self print:nstabs]; // print chr$(TAB)のときには再帰呼び出しになる
			}
		}
	} else { // グラフィック座標モードではスペース1個にする
		[self print:@" "];
	}
}

// カーソル位置から行末までをクリア
-(void)clearLine
{
	printSelf();
	
    @autoreleasepool {
		pushContext();
		//
		CGRect frame;
		if (_widthx==0) {
			// グラフィック座標系
			nslog(@"グラフィック座標=%f,%f",_tx,_ty);
			frame.origin.x=_tx;
			frame.origin.y=_ty;
		} else {
			// テキスト座標系
			nslog(@"テキスト座標=%f,%f",_tx,_ty);
			frame.origin.x=_tx*_fontWx;
			frame.origin.y=_ty*_fontWy;
		}
		frame.size.height=_fontWy;
		frame.size.width=(consoleWidth-frame.origin.x);
		CGContextClearRect(context,frame);
		//
		[self draw];
	//
	}
}

// カーソル位置から画面最後までをクリア
-(void)clearEndOfScreen
{
	printSelf();
	
    @autoreleasepool {
		pushContext();
		
		// まずこの行文を消す
		CGRect frame;
		if (_widthx==0) {
			// グラフィック座標系
			nslog(@"グラフィック座標=%f,%f",_tx,_ty);
			frame.origin.x=_tx;
			frame.origin.y=_ty;
		} else {
			// テキスト座標系
			nslog(@"テキスト座標=%f,%f",_tx,_ty);
			frame.origin.x=_tx*_fontWx;
			frame.origin.y=_ty*_fontWy;
		}
		frame.size.width =(consoleWidth-frame.origin.x);
		frame.size.height=_fontWy;
		CGContextClearRect(context,frame);
		
		// 次の行から画面最後までを消す
		frame.origin.x=0;
		frame.origin.y+=_fontWy;
		frame.size.width =consoleWidth;
		frame.size.height=consoleHeight-frame.origin.y;
		CGContextClearRect(context,frame);
		
		//
		[self draw];
	//
	}
}

//---------------------------------------------------------------------------------------
// 実描画を伴う処理

-(void)tab:(int)cnt
{
	printSelf();
	if ([self isTextMode]) { // テキストモードのみ有効
		[self setTab:cnt]; // ここでスペースによる描画が行われる
	}
}

-(void)cls
{
	printSelf();
	[super wipe];
	[self setHome];	// homeへ
}

// point0〜pointの間に属性処理を施す
// テキストで1文字ずつ属性処理すると文字間に隙間があくことがあるので（特に反転）、文字列長で一括処理する
// これでも文字列間は隙間があく可能性はあるが、それは技術的課題として今は無視する
// 色は予めsetColorしておくこと
// ATRB_BOLDだけはここで処理されない
-(void)drawAtrb:(CGPoint)point size:(CGSize)size
{
	printSelf();
	nslog(@"drawAtrb:(%f,%f)-W:%f,H:%f",point.x,point.y,size.width,size.height);
	nslog(@"atrb=$%x",atrb);
	int at=getAtrbExceptBoldAndCentering(atrb)&(~(ATRB_REVERSE)); // 太字/中央寄せと反転は別処理なのでとりあえず外す
	//
	CGRect area2;
	area2.origin=point;
	area2.size  =size;
	while (at!=ATRB_NORMAL) {
		CGFloat y=0.0; // Analyzerの警告回避のため
		if (at&ATRB_CANCEL2) { // 取り消し線（2本）
			at&=(~ATRB_CANCEL2); // 処理した属性は消していく
			// １本目
			y=point.y+size.height/2-2; // 高さは文字高さ
			CGContextMoveToPoint(   context,point.x,y);
			CGContextAddLineToPoint(context,point.x+area2.size.width,y); // 幅は1桁分
			CGContextStrokePath(	context);
			// ２本目
			y=point.y+size.height/2+2; // 高さは文字高さ
		}
		else
		if (at&ATRB_CANCEL) { // 取り消し線（1本）
			at&=(~ATRB_CANCEL); // 処理した属性は消していく
			y=point.y+size.height/2;
		}
		else
		if (at&ATRB_UNDERLINE) { // 下線
			at&=(~ATRB_UNDERLINE); // 処理した属性は消していく
			y=point.y+size.height; // 高さは文字高さ
		}
		CGContextMoveToPoint(   context,point.x,y);
		CGContextAddLineToPoint(context,point.x+area2.size.width,y); // 幅は1桁分
		CGContextStrokePath(	context);
	} // while (at)
	// 反転は最後に処理する
	if (getAtrbExceptBoldAndCentering(atrb)&ATRB_REVERSE) { // 反転
		CGContextSetBlendMode(context,kCGBlendModeXOR);
		CGContextFillRect(    context,area2);
		CGContextSetBlendMode(context,kCGBlendModeNormal);
	}
}

// 文字列表示
// コントロールコードは処理しない
// 属性はBOLD以外処理しない
// 色は予めsetColorしておくこと
// グラフィックモードの時は全文字列一括、テキストモードの時は１文字ずつ
// cw=文字幅 : 1=半角 , 2=全角 ; テキストモードでのみ有効 (character width)
// fclrはBOOLにしないこと(atrb&ATRB_CLEARを直接受けるから)
-(CGSize)printMain:(NSString *)str point:(CGPoint)point fclr:(int)fclr cw:(int)cw
{
	printSelf();
	nslog(@"printMain:str=%@/cw=%d/fclr=%d",str,cw,fclr);
	nslog(@"point=%@",NSStringFromCGPoint(point));

	int at=getAtrb(atrb);

	// 先に描画下領域を消す:これをしないと文字が重なってしまう
	CGRect area2;
	area2.origin=point;
	CGSize size;
	if ([self isTextMode]) {
		nslog(@"テキストモードでは、桁幅全てを消さなければならない");
		area2.size.width =_fontWx*cw;
		area2.size.height=_fontWy;
	} else {
		nslog(@"グラフィックモードでは、上に描画する文字列の幅そのものだけ消せば良い");
		size = sizeWithFont(str,_font,_fdescender);
		area2.size=size;
		at&=(~ATRB_CENTERING); // グラフィックモードでは中央寄せは無効
	}
	if (fclr) {
		nslog(@"先に下となる部分をクリアする:%@",NSStringFromCGRect(area2));
		CGContextClearRect(context,area2);
	}
	
	// 描画色
	UIColor *drawColor=[UIColor colorWithRed:_cf(_rgbaColor.r)
									   green:_cf(_rgbaColor.g)
										blue:_cf(_rgbaColor.b)
									   alpha:_cf(_rgbaColor.alpha)];
	nslog(@"font=%@",_font);
	
	if (!_fOlder) {
		// V3.20
		// クリア時の上抜けを防ぐため、上１ラインを空きにする
		point.y+=1.0;
	}
	if (at&ATRB_CENTERING) {
		// プロポーショナルなフォントを_fontWxの幅で中央に描画する
		CGSize sz = sizeWithFont(str,[UIFont fontWithName:_fontName size:_pointSize],_fdescender);
		CGFloat dx=(area2.size.width-sz.width);
		nslog(@"中央寄せ:描画サイズ(%@)=%@/_fontWx=%f/dx=%f",str,NSStringFromCGSize(sz),_fontWx,dx);
		if (dx>=2) { // boldありの時はそのアンチエイリアスまで考慮
			nslog(@"中央寄せ可能");
			point.x+=(dx/2);
		}
	}
	if (at&ATRB_BOLD) {
		// 太字は、斜めに少しずらした描画を重ねることで実現する
		// これでは綺麗でないときは、フォントそのものを切り替えること
		nslog(@"太字");
		CGPoint bpoint=point;
		// 1.0では線が離れすぎた。0.5ではまだ少し細く感じたので
		// V3.2で横方向だけに変更(十分なので＋明朝など細い文字のフォントで線が2重に見えるので)
		bpoint.x+=0.8;
//		bpoint.y+=0.6;
		[str drawAtPoint7NoReturn:bpoint withFont:_font withColor:drawColor];
		// 小数点座標から描画するとアンチエイリアスのためはみ出るので注意
	}
#if 0	// デバッグ用	!!test!!
	// 落ちるときはここである確率が非常に高い
	if (str==nil || _font==nil) {
		nslog(@"strまたは_fontがおかしい:str=%@/_font=%@",str,_font);
	}
#endif
	size=[str drawAtPoint7:point withFont:_font withColor:drawColor];
	return (size);
}

//---------------------------------------------------------------------------------------
// 各命令の個別実装
//---------------------------------------------------------------------------------------
// 実描画あり命令

// リターン値 : NO=正常表示できた , YES=正常表示できない（コントロールコードを含んでいる；グラフィックモードでは全体が表示されない。テキストモードではそれを無視する）
-(BOOL)print:(NSString *)str
{
	nslog(@"print:%@(%tu)",str,[str length]);
	
	BOOL ret=NO;

	size_t len=[str length];
	if (len==0) return(ret); // 表示内容がないときは即終了する(落ちる対策も兼ねる)
	
	printSelf();
	//
	if (_widthx!=0) {
		// テキスト座標系でのコントロールコード処理が最優先
		if (len==1) {
			// 1文字の時
			nslog(@"1文字の時");
			if ([self printCtrlCode:[str characterAtIndex:0]]) { // UTF16
				// コントロールコード処理
				nslog(@"コントロールコード実行");
				return(ret);
			}
		}
	}
	//
	CGPoint point;
	CGSize	size;
	
    @autoreleasepool {
		pushContext();
		//
		// フォント色設定
		[[UIColor colorWithRed:_cf(_rgbaColor.r)
						 green:_cf(_rgbaColor.g)
						  blue:_cf(_rgbaColor.b)
						 alpha:_cf(_rgbaColor.alpha)] set];

		if (_widthx==0) {
			// グラフィック座標系
			nslog(@"print.グラフィック座標=%f,%f",_tx,_ty);
			// 文字列中にコントロールコードが含まれていないか調べる
			for (int i=0;i<[str length];i++) {
				unichar c16=[str characterAtIndex:i];
				if (c16<' ') {
					// 含まれていた
					nslog(@"コントロールコードが入っているときはまるごと表示しない:$%02x",c16);
					ret=YES; // 表示しない（異常動作するから）
					goto print_end;
				}
			}
			point.x=_tx;
			point.y=_ty;
			nslog(@"atrb=$%x/$%x/%d",atrb,ATRB_CLEAR,atrb&ATRB_CLEAR);
			size=[self printMain:str point:point fclr:atrb&ATRB_CLEAR cw:1]; // ワーク内での描画は常に(0,0)から
			[self drawAtrb:point size:size];
			_tx+=size.width;
			// _tyは動かさない
		} else {
			// テキスト座標系
			nslog(@"テキスト座標=%f,%f",_tx,_ty);
			//     length	  UTF8			UTF16
			//　♣	1		E2 99 A3		26 63
			//　▒	1		E2 96 92		25 92
			//　🐓	2		F0 9F 90 93		D8 3D DC 13	サロゲートペア
			//　🍅	2		F0 9F 8D 85		D8 3C DF 45	サロゲートペア

			nslog(@"str=%@(length=%tu)",str,[str length]);

			// テキストモードでは表示位置がテキスト座標なるので、drawAtPointによる一括描画はできない(これが難しさの原点)(１文字ごとに座標が離れるから)
			nslog(@"_fontWx=%f,_fontWy=%f",_fontWx,_fontWy);
			point.x=_tx*_fontWx;
			point.y=_ty*_fontWy;
			
			// 1文字づつ処理する
			for (int i=0;i<[str length];i++) {
				// v3.10でサロゲートペアに対応
				ULong c16=[str characterAtIndex:i];
				nslog(@"UTF16=$%x",(int)c16);
				// 空白は、文字下クリアの後は処理しなくても良さそうだが、属性がかかることがあるのでそのままにしておく
				if (c16<' ') {
					nslog(@"コントロールコードは無視する");
					ret=YES;
					continue;
				}
				NSRange range=NSMakeRange(i, 1);
				if (isSurrogate(c16)) {
					nslog(@"サロゲートペア文字である");
					range=NSMakeRange(i, 2);
					unichar c16L=[str characterAtIndex:++i];
					c16=surrogateToUtf16(c16,c16L);
				}
				// 1文字切り出し
				NSString *s=[str substringWithRange:range];
				nslog(@"s=%@",s);
				
				// 全角半角で処理を分ける
				size = sizeWithFont(s,_font,_fdescender);
				int cw=1; // 文字幅:半角分
				if (_fzenhan) { // 全角=半角*2とするとき
					if (size.width>_fontWx) {
						// 実表示幅で判定する
						nslog(@"表示幅で全角:size.width=%f,fontWx=%f",size.width,_fontWx);
						// 全角
						cw=2;
					}
					// 表示幅では半角以下であるが実は全角にしなければならない文字がある
					else
					{
						ULong c=utf16ToSjis(c16); // [s characterAtIndex:0]);
						nslog(@"UTF16=$%x→SJIS=$%04x",(int)c16,(int)c);
						if (c>=0x8140 && c<=0xeafc) {
							nslog(@"コード的に全角:$%04x",(int)c);
							// 全角
							cw=2;
						}
					}
				} // if (_fzenhan)

				if (cw>=2) {
					// 画面最終桁での全角文字出力かどうか
					if (_tx+cw>=_widthx+1) {
						nslog(@"画面最終桁での全角文字出力である");
						_tx=0;
						_ty+=1;
						nslog(@"ty=%f,widthy=%f",_ty,_widthy);
						if (_ty>=_widthy) {
							// スクロールアップ〜を入れる場合はここに入れる
							// tConsoleのスクロールは独立であり、他とは連動しない
							_ty-=1; // 先の+1をキャンセルするため
							nslog(@"文字列表示中の改行に伴うスクロールアップ");
							if (_fUpScroll) {
								[super txScroll:SCROLL_UP value:_fontWy fContext:NO];
							}
						}
						// 改行したので表示位置を変更
						point.y=_ty*_fontWy;
						point.x=_tx*_fontWx;
					} // for i
				}
				
				// 1文字描画
				[self printMain:s point:point fclr:YES cw:cw];
				// 属性展開
				CGSize size={_fontWx*cw,_fontWy};
				[self drawAtrb:point size:size];
				
				nslog(@"tx:%f+cw:%d=%f/widthx=%f",_tx,cw,_tx+cw,_widthx);

				// カーソル移動
				_tx+=cw;
				// 改行判定
				if (_tx>=_widthx) {
					_tx=0;
					_ty+=1;
					nslog(@"ty=%f,widthy=%f",_ty,_widthy);
					if (_ty>=_widthy) {
						// スクロールアップ〜を入れる場合はここに入れる
						// tConsoleのスクロールは独立であり、他とは連動しない
						_ty-=1; // 先の+1をキャンセルするため
						nslog(@"文字列表示中の改行に伴うスクロールアップ");
						if (_fUpScroll) {
							[super txScroll:SCROLL_UP value:_fontWy fContext:NO];
						}
					}
					point.y=_ty*_fontWy;
				} // for i
				point.x=_tx*_fontWx;
			} // テキスト座標系
			// 文字列作成完了
			nslog(@"表示後テキスト座標=%f,%f",_tx,_ty);
		} // テキストモード
		// 描画指示
		[self draw:DRAW_NOWAIT];
    }	// ここ専用のAutoreleasePoolを作らないと、mainのそれでは溢れて画面が出なくなる
print_end:;
	nslog(@"print end");
	return (ret);
}

//---------------------------------------------------------------------------------------
// 実描画なし命令

// V2.2で座標エラーを返せるようにした
// リターン値 : NO=正常座標だった , YES=異常座標(補正した)
-(BOOL)locate:(CGFloat)x y:(CGFloat)y
{
	printFunc();
	nslog(@"指定x,y=%f,%f",x,y);
	
	int ret=NO; // 正常座標の印
	// 内部記録のみ
	if (x<0) {x=0; ret=YES;}
	if (y<0) {y=0; ret=YES;}
	if ([self isTextMode]) { // テキストモードのみ有効
		// テキスト座標
//		if (x>=_widthx) {x=_widthx-1; ret=YES;}	// v2.1未満 : 最終桁にする
		if (x>=_widthx) {x=0; y++;	  ret=YES;}	// v2.2 : 次の行の先頭に移動する
		if (y>=_widthy) {y=_widthy-1; ret=YES;}
	}
	nslog(@"確定x,y=%f,%f",x,y);
	_tx=x;
	_ty=y;
	// 正常座標だった
	return (ret);
}

-(CGFloat)textX2Gx:(CGFloat)x
{
	return x*_fontWx;
}

-(CGFloat)textY2Gy:(CGFloat)y
{
	return y*_fontWy;
}

// home座標へ移動
-(void)home
{
	printSelf();
	[self locate:0 y:0];
}

-(void)atrb:(NSInteger)atrb_
{
	atrb=getAtrb(atrb_);
	nslog(@"tConsole.atrb:atrb_=$%04x → atrb=$%04x",(int)atrb_,(int)atrb);
#if 0 // v2.1ではBIT指定が可能になったので範囲判定は省く
	if (atrb<ATRB_NORMAL||atrb>ATRB_REVERSE) atrb=ATRB_NORMAL;
#endif
	// ここは保存だけ
}

-(void)cursorMove:(NSInteger)direc cnt:(int)cnt
{
	printSelf();
	if ([self isTextMode]) { // テキストモードのみ有効
		[self setCursorMove:direc cnt:cnt];
	}
}

// r/g/b/alpha=0〜255
-(int)color:(int)r g:(int)g b:(int)b alpha:(int)alpha
{
	printSelf();
	_rgbaColor=getWithStandardColor(r,g,b,alpha);
	// 色の実設定はprint時
	//	nslog(@"color=%d",rgba_color2conpositeRGBA(_rgbaColor));
	return (rgba_color2conpositeRGBA(_rgbaColor));
}

// width wx!=0指定時には_pointSizeは無視される
-(void)font:(NSString *)fontName1 pointSize:(CGFloat)pointSize1
{
	printSelf();
	// 内部記録のみ
	self.fontName=fontName1; // retain
	if (pointSize1>0) {
		_pointSize=pointSize1;
	}
	if (pointSize1<0) {
		_pointSize=_pointSize0;
	} // =0のときは前の設定そのまま
	self.font=[UIFont fontWithName:self.fontName size:_pointSize];
	if (_font==nil) {
		nslog(@"指定されたフォントが見つからないのでデフォルトフォントにする");
		self.font=[UIFont fontWithName:DEFFONT size:_pointSize];
		self.fontName=DEFFONT;
	}
	nslog(@"font=%@",self.font);
}

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

// setter
-(void)setBaseCharacter:(NSString *)baseCharacter
{
	printSelf();
	if (baseCharacter==nil) baseCharacter=@"あ";
	_baseCharacter=baseCharacter;
	nslog(@"基準文字:%@(%tu)",baseCharacter,[baseCharacter length]);
}

-(NSInteger)width:(NSInteger)wx wy:(NSInteger)wy
{
	printSelf();
	nslog(@"width(wx=%d,wy=%d)",(int)wx,(int)wy);
	self.widthx=(CGFloat)wx;
	if (wx==0) {
		nslog(@"グラフィックモード");
		CGSize sz = sizeWithFont(_baseCharacter,[UIFont fontWithName:_fontName size:_pointSize],_fdescender);
		nslog(@"基準文字=%@,フォント幅=%@",_baseCharacter,NSStringFromCGSize(sz));
		// key-valueを使っているのでプロパティー式での代入が必須
		self.fontWx=0;
//		self.fontWy=0;
		self.fontWy=sz.height;	// V3.20で基準文字の高さを与えるように変更
		self.widthy=0; // グラフィックモードの判定はこれを使うこと
		_fzenhan=NO; // 全角半角で桁制御はしない（というか、一切の桁制御はない）
		return (0);
	}
	
	// wx指定時：テキスト座標モード
	// _pointSizeは自動設定される
	// wyのみ=0の時は、wxに基づき自動設定
	@autoreleasepool {
		// wxチェック
		if (wx<MIN_WIDTH) wx=MIN_WIDTH;
		if (wx&1) wx++; // 偶数化
		nslog(@"補正後wx=%d",(int)wx);
		
		// 半角フォントサイズ算出
		CGSize scrnSize=frame0.size;
		CGFloat fWx=scrnSize.width/wx; // まず、単純計算で桁幅を求める
		if (!_fOlder) {
			fWx=floor(fWx); // 切り捨てる；入りきる幅で整数単位化する
		}
		// fWxはここで確定
		nslog(@"画面width:%f/指定桁数:%d=1font wx=%f",scrnSize.width,(int)wx,fWx);
		_pointSize=fWx; // ここから計算開始
		// IPAexではうまく動かない。何故ならば、半角文字部分の文字幅が全角の半分以下とは限らず、かなり変化するから
		// →0x20〜0x7eの幅をすべて調べる；半角カタカナについては利用頻度が低いと思われるので対象にしない(UTF8ではコードが並んでないので結構処理が面倒になるから）
		// 半角フォントが大きい場合は、全て全角とみなして半角処理を止める＝座標は常に＋１
		
		// (1)半角フォントで一番幅が広いものを探す
		nslog(@"半角フォントで一番幅が広いものを探す");
		CGSize sz;
		UIFont *fnt=[UIFont fontWithName:_fontName size:_pointSize];
		nslog(@"font=%@",fnt);
		CGSize fntSizeMax={0,0};
		unichar c,maxc;
		NSString *s,*maxSizeStr;
		for (c=' ';c<0x7f;c++) {
			s=[NSString stringWithCharacters:&c length:1];
			sz = sizeWithFont(s,fnt,_fdescender);
			// drawAtPointによる実描画サイズがsizeiWithFontより大きくなることは、半角文字の範疇ではない感じ
			if (sz.width >fntSizeMax.width ) {
				fntSizeMax.width =sz.width;
				maxSizeStr=s;
				maxc=c;
			}
			if (sz.height>fntSizeMax.height) {
				fntSizeMax.height=sz.height;
				// 高さでは文字は変更しない
			}
		}
		nslog(@"半角横幅最大文字=%@($%02x):%@",maxSizeStr,maxc,NSStringFromCGSize(fntSizeMax));
		// fntSizeMax=半角の最大文字幅
		// maxSizeStr=最大横幅文字
		//
		if (maxc==0x20) {
			nslog(@"等幅フォント:全角=半角*2桁モードにする");
			_fzenhan=YES;
			// 全角は2桁使えば良い＝幅に余裕がある
		} else {
			nslog(@"等幅フォントでない");
			// 全角も1桁使う
			sz = sizeWithFont(_baseCharacter,[UIFont fontWithName:_fontName size:_pointSize],_fdescender);
			nslog(@"基準文字=%@,フォント幅=%@",_baseCharacter,NSStringFromCGSize(sz));
			if (sz.width>=fntSizeMax.width*2) {
				nslog(@"半角最大幅は全角の幅の半分以下なので、全角=半角*2桁モードにする");
				_fzenhan=YES;
				// 全角は2桁使えば良い＝幅に余裕がある
			} else {
				nslog(@"半角最大幅は全角の幅の半分より大きいので、すべての文字=1桁モードにする");
				// sz=_baseCharacterの幅
				if (sz.width>fntSizeMax.width) {
					nslog(@"基準文字のほうが幅広い時はそれを基準にする");
					fntSizeMax.width =sz.width;
					maxSizeStr=_baseCharacter;
				}
				if (sz.height>fntSizeMax.height) {
					nslog(@"基準文字のほうが高い時はそれを基準にする");
					fntSizeMax.height=sz.height;
					// 高さでは文字は変更しない
				}
				_fzenhan=NO;
			}
		}
		nslog(@"横幅最大文字=%@:%@",maxSizeStr,NSStringFromCGSize(fntSizeMax));

#define dpSize	0.25
		nslog(@"フォント幅変化差分 dpSize=%f",dpSize);
		
		// フォントは縦横等倍でしか変化できないので、横で縮小がかかると縦も縮小されてしまう。
		// これが、フォント幅が広い文字を持ってくると縦行数が増える理由
		// 一般には縦幅には余裕があるので、もったいない。
		// 横画面で使えば効率が良さそうだが・・・。
		
		// (2)そのフォントの幅＊wxがscrnsize.widthを超えるときは_pointSizeを小さくする
		//	　逆に、超えないときは、_pointSizeを大きくする
		CGFloat maxWWx=fntSizeMax.width*(CGFloat)wx; // 最大文字幅*wx=必要画面幅
		nslog(@"最大文字幅(%f)*wx(%d)=%f : 画面横幅=%f",fntSizeMax.width,(int)wx,maxWWx,scrnSize.width);
		if (maxWWx>scrnSize.width) {
			nslog(@"widthが画面横幅を超えている");
			do {
				// 段階的にフォントサイズを小さくしておき、1フォント幅の理論計算値より小さくなったらそれで確定
				_pointSize-=dpSize;
				sz = sizeWithFont(maxSizeStr,[UIFont fontWithName:_fontName size:_pointSize],_fdescender); // ここは_pointSize変更中なので_fontを使ってはいけない
				// sizeWithFontは実表示幅と異なることがあるが、tconsole内では表示位置を１文字単位で制御するため問題ない
				nslog(@"pointSize=%f;font=%f*%f",_pointSize,sz.width,sz.height);
			} while (sz.width>fWx);
			fntSizeMax.width =sz.width;
			fntSizeMax.height=sz.height;
			
			maxWWx=fntSizeMax.width*(CGFloat)wx; // nslog()でのみ参照
			nslog(@"縮小補正後最大文字幅(%f)*wx(%d)=%f : 画面横幅=%f",fntSizeMax.width,(int)wx,maxWWx,scrnSize.width);
		}
		else
		if (maxWWx<scrnSize.width) {
			nslog(@"widthにまだ余裕がある");
			CGSize sz0=CGSizeZero;
			do {
				sz0=sz; // 1つ前のサイズ保存
				_pointSize+=dpSize;
				sz = sizeWithFont(maxSizeStr,[UIFont fontWithName:_fontName size:_pointSize],_fdescender);
				// sizeWithFontは実表示幅と異なることがあるが、tconsole内では表示位置を１文字単位で制御するため問題ない
				nslog(@"pointSize=%f;font=%f*%f",_pointSize,sz.width,sz.height);
			} while (sz.width<fWx);
			_pointSize-=dpSize; // 1つ前が当たり
			if (sz0.width>0) { // 念のため。=0はありえないけど。
				fntSizeMax.width =sz0.width;
				fntSizeMax.height=sz0.height;

				maxWWx=fntSizeMax.width*(CGFloat)wx; // nslog()でのみ参照
				nslog(@"拡大最大文字幅(%f)*wx(%d)=%f : 画面横幅=%f",fntSizeMax.width,(int)wx,maxWWx,scrnSize.width);
			}
		}
		else {
			nslog(@"一致しているときは何もしない");
		}

		nslog(@"一旦決定pointSize=%f",_pointSize);
		_pointSize0=_pointSize;
		[self font:_fontName pointSize:_pointSize]; // フォントサイズ設定
		
		// font高さの算出 : 高さは全角も考慮
		CGFloat fWy;
		if (_fOlder) {
			fWy=fntSizeMax.height; // 1行の幅
		} else {
			fWy=ceil(fntSizeMax.height); // 1行の幅(切り上げて整数化;小数点が入ると、アンチエイリアス描画で重ね表示時に残りが出るため)
		}
		nslog(@"フォント高さ=1行高さ=%f",fWy);

		CGFloat why;
		CGFloat yy=(CGFloat)((int)(scrnSize.height/fWy)); // 一旦整数化して小数点以下を切り捨てて再度実数化
		if (wy==0||wy>yy) { // 指定された行数が確保できる行数より多い時、またはwy=0
			nslog(@"widthy自動設定:yy=%f",yy);
			// yyは小数点以下のない数値のはず
			why=yy;
		} else {
			nslog(@"指定された行数が確保できる行数より少ない時");
			if (_fOlder) {
				fWy=scrnSize.height/wy; 		// 1行あたりの幅を計算
				why=wy;
			} else {
				fWy=ceil(scrnSize.height/wy); 	// 1行あたりの幅を計算
				why=floor(scrnSize.height/fWy); // =wyを再計算；少数以下は切り捨て
			}
		}
		nslog(@"補正前：1フォント分の大きさ=%f*%f/行数=%f",fWx,fWy,why);
		
		//----V3.20
		if (!_fOlder) {
			#define DH	(2.0)	// 上1ライン(アンチエイリアス)、下1ライン(太字＋アンチエイリアス;v3.2で太字は横方向だけにしたので不要かも)
			if (fWy-sz.height<DH) {
				nslog(@"行幅と表示フォント幅に十分な差がないとき、上書きでゴミが残る可能性があるので、行数を減らして行幅を広げる");
				nslog(@"行高さ=%f-フォント表示高さ:%f=差分:%f 〜 必要差分:%f",fWy,sz.height,fWy-sz.height,DH);
				while (fWy-sz.height<DH) {
					why--;
					fWy=scrnSize.height/why;
				}
				fWy=floor(fWy); // 少数以下切り捨て(>0)
				why=floor(scrnSize.height/fWy); // =wyを再計算；少数以下は切り捨て
				nslog(@"補正後1行高さ=%f/行数=%f",fWy,why);
			}
		}
		//----V3.20
		
		// 幅・高さ確定
		// アンチエイリアスの問題を避けるため、全て整数値でなければならない
		// key-valueを使っているのでプロパティー式での代入が必須
		self.fontWx=fWx;
		self.fontWy=fWy; // 1行高さ
		self.widthy=why; // 行数
		nslog(@"1フォント分の大きさ=%f*%f/行数=%f",fWx,fWy,why);
		// 別にも記録しておく(gLocate用);現在未使用
		_bfontWx=_fontWx;
		_bfontWy=_fontWy;
		// home
		[self setHome];
		//[self home];再帰呼び出しになるのでこれはダメ
	}
	return (_widthy);
}

//---------------------------------------------------------------------------------------
// v3.20

-(CGFloat)fontSize:(NSString *)message fontName:(NSString *)fontName1 pointSize:(CGFloat)pointSize1 fontWy:(CGFloat *)fontWy
{
	printSelf();
	
	// ここではfdescenderは常にYESにして計算する
	CGSize sz = sizeWithFont(message,[UIFont fontWithName:fontName1 size:pointSize1],YES);
	nslog(@"Message(size=%@):%@",NSStringFromCGSize(sz),message);
	
	if (fontWy!=NULL) {
		*fontWy=sz.height;
	}
	return sz.width;
}

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

@end
