//----------------------------------------------------------------------------------------------
//
//  gConsole.m : グラフィック描画用コンソール
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2012/09/07.
//  Copyright (c) 2012-15年 AIG-Soft. All rights reserved.
//

#import "gConsole.h"
#import <CoreGraphics/CoreGraphics.h>
#import "main.h"
#import "rgbhsv.h"
#import "NSString+drawiOS7.h"

@implementation gConsole

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

// 描画の基本設定
-(void)init2
{
	printSelf();
	[super init2];
	pixel=NULL;
}

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

// JPEG/PNG書き出し→ファイル
// quality : 0〜1.0
-(BOOL)imageSave:(NSString *)fname quality:(CGFloat)quality fjpeg:(BOOL)fjpeg
{
	printSelf();
	@autoreleasepool {
		UIGraphicsPushContext2(context);
		CGContextSynchronize(context); // 画像確定
		CGImageRef imgRef1=CGBitmapContextCreateImage(context); // 画像確定(copy)
		UIGraphicsPopContext2();
		// 終了
		//	UIGraphicsEndImageContext();

		// 処理負荷の軽減のためバックグラウンドに任せる
		fret=FRET_NONE; // 状態未定
		NSArray *arg=@[(__bridge id)imgRef1,
					  fname,
					  [NSValue valueWithBytes:&fjpeg 	objCType:@encode(BOOL)],
					  [NSValue valueWithBytes:&quality	objCType:@encode(CGFloat)]];
		[self performSelectorInBackground:@selector(imageSaveInBackground:) withObject:arg];
		CGImageRelease(imgRef1); // 解放
	}
	return (fret); // ここに来るまでに終了してたらFRET_NONE以外になる
#if 0
	NSData *imgdata;
	if (fjpeg) {
		imgdata=UIImageJPEGRepresentation(image1,quality);
#if 0
		{
			static int cnt=0;
			[imgdata writeToFile:[NSString stringWithFormat:@"/test/saveImg%d.jpg",cnt++] atomically:YES];
		}
#endif
	} else {
		imgdata=UIImagePNGRepresentation(image1);
#if 0
		{
			static int cnt=0;
			[imgdata writeToFile:[NSString stringWithFormat:@"/test/saveImg%d.png",cnt++] atomically:YES];
		}
#endif
	}
	nslog(@"fname=%@",makeFullPathForSaveData(fname));
	BOOL ret=[imgdata writeToFile:makeFullPathForSaveData(fname) atomically:YES];
	nslog(@"ret=%d",ret);
	//
	[pool release];
	[fname release];
	//
	return (ret);
#endif
}

// バックグラウンドで画像保存を行う
-(void)imageSaveInBackground:(NSArray *)arg
{
	printSelfs(@":%@",arg);
	@autoreleasepool {

		int i=0;
		CGImageRef imgRef1=CGImageRetain((__bridge CGImageRef)arg[i++]);
		UIImage *image1 =rotateImage([UIImage imageWithCGImage:imgRef1],-180); // CGImageRef → UIImage変換 + 上下鏡像変換 ; v2.1（画面表示時に変換しなくなったので、こちらで必要になった）
		CGImageRelease(imgRef1); // 解放
		//
		NSString *fname=arg[i++];
		//
		NSValue *val=arg[i++];
		BOOL fjpeg;
		[val getValue:&fjpeg];
		CGFloat quality;
		val=arg[i++];
		[val getValue:&quality];
		
		NSData *imgdata;
		if (fjpeg) {
			imgdata=UIImageJPEGRepresentation(image1,quality);
#if 0
			{
				static int cnt=0;
				[imgdata writeToFile:[NSString stringWithFormat:@"/test/saveImg%d.jpg",cnt++] atomically:YES];
			}
#endif
		} else {
			imgdata=UIImagePNGRepresentation(image1);
#if 0
			{
				static int cnt=0;
				[imgdata writeToFile:[NSString stringWithFormat:@"/test/saveImg%d.png",cnt++] atomically:YES];
			}
#endif
		}
		nslog(@"fname=%@",makeFullPathForSaveData(fname));
		fret=[imgdata writeToFile:makeFullPathForSaveData(fname) atomically:YES]; // これでYES/NOになる
		nslog(@"ret=%d",fret);
	//
	}
}

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

// 画像ファイルをアルバムへコピー
-(BOOL)imageCopyToAlbum:(NSString *)path
{
//	nslog(@"imageCopyToAlbum:%@",path);
	fret=FRET_NONE; // 状態未定
	UIImage *img=[[UIImage alloc]initWithContentsOfFile:path];
	UIImageWriteToSavedPhotosAlbum(img, self, @selector(imageCopyToAlbumComplete:didFinishSavingWithError:contextInfo:), NULL);
	// ↑はvoid関数なので、ここではエラーは取れない。そのかわりimageCopyToAlbumCompleteで判定できる
	// 以下並行動作
	return YES; // 一応アルバムへのコピーは許可された
}

// アルバム保存時のエラー判定
-(void)imageCopyToAlbumComplete:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
	if (error) { // エラー
		nslog(@"アルバム保存エラー:%@",error);
		nslog(@"contextInfo=%p",contextInfo);
		// 保存中にHOME+電源で画面撮影などすると出るようである
		fret=FRET_ERROR;
	} else {
		nslog(@"アルバム保存正常終了");
		fret=FRET_OK;
	}
	nslog(@"アルバム保存終了");
}

//---------------------------------------------------------------------------------------
// 各命令の個別実装
//---------------------------------------------------------------------------------------

-(BOOL)imgLoad:(CGFloat)sx sy:(CGFloat)sy wx:(CGFloat)wx wy:(CGFloat)wy fname:(NSString *)fname ftile:(BOOL)ftile
{
	printSelf();
	nslog(@"%@(ftile=%d)",fname,ftile);
	
	NSString *path=getResourceOrSavedataPath(fname);
	if (path==nil) {
		nslog(@"ファイルが見つからない");
		fret=NO; // 将来のマルチタスク化への準備のため
		return (fret);
	}

	UIImage *loadImg=[UIImage imageWithContentsOfFile:path];
	nslog(@"loadImg=%@",loadImg);
	if (loadImg==nil) {
		// ファイルが見つからない
		fret=NO; // 将来のマルチタスク化への準備のため
	} else {
		CGRect imageRect={{sx,sy},{0,0}}; // origin,size
		if (wy==CGSizeZero.height || wx==CGSizeZero.width) {
			// 画像のサイズを使う
			imageRect.size=loadImg.size;
		} else {
			// ImgLoad():指定サイズに拡大／縮小 / tileImgLoad():領域サイズ
			imageRect.size=CGSizeMake(wx,wy);
		}
		printFrame(@"imageRect", imageRect);
		
		// CGContext*()は座標がQuartz系である。
		// 故に、CGBitmapContextCreateImage(context)をそのまま表示すると上下反転の鏡像で表示されてしまう。
		// それを正常表示するため、consoleは表示が上下胸像になるように設定してある。
		// UIImageは通常座標系であるため、回転して合わせる
		loadImg=rotateImage(loadImg,-180); // 上下鏡像
		CGImageRef img = CGImageRetain(loadImg.CGImage); // ここで画像確定
		nslog(@"img=%@",img);
		//
		UIGraphicsPushContext2(context);
		if (ftile) {
			nslog(@"tileImgLoad");
			CGContextSaveGState(context); // 後でclipを解除するため状態保存
			CGContextClipToRect(context,imageRect); // 領域指定
			CGContextDrawTiledImage(context, CGRectMake(sx,sy,loadImg.size.width,loadImg.size.height), img);
			CGContextRestoreGState(context); // clip解除
		} else {
			nslog(@"imgLoad");
			CGContextDrawImage(context, imageRect, img);
		}
		CGImageRelease(img);
		[self draw];
		//
		// 読み込み成功
		fret=YES; // 将来のマルチタスク化への準備のため
	}
	return (fret);
}

-(void)line:(CGFloat)sx sy:(CGFloat)sy ex:(CGFloat)ex ey:(CGFloat)ey
	   fill:(BOOL)ffill
	  style:(NSUInteger)style
{
	printSelf();
	pushContext();
	[self lineStyleSub:style];
	CGContextMoveToPoint(   context,sx,sy);
	CGContextAddLineToPoint(context,ex,ey);
	strokeOrFill(context,ffill);
	[self draw];
}

-(void)lines:(CGPoint *)points count:(size_t)count
		fill:(BOOL)ffill
	   style:(NSUInteger)style
{
	printSelf();
	pushContext();
	[self lineStyleSub:style];
#ifdef DEBUG
	int i;
	for (i=0;i<count;i++) {
		nslog(@"[%d]=%f,%f",i,points[i].x,points[i].y);
	}
#endif
	CGContextAddLines(context,points,count);
	strokeOrFill(context,ffill);
	[self draw];
}

-(void)box:(CGFloat)sx sy:(CGFloat)sy ex:(CGFloat)ex ey:(CGFloat)ey
	 style:(NSUInteger)style
{
	printSelf();
	pushContext();
	[self lineStyleSub:style];
	CGRect imageRect=CGRectMake(sx, sy, ex-sx+1, ey-sy+1); // v2.1:CGRectは幅なので+1が必要
	CGContextStrokeRect(context,imageRect);
	[self draw];
}

-(void)fill:(CGFloat)sx sy:(CGFloat)sy ex:(CGFloat)ex ey:(CGFloat)ey
{
	printSelf();
	pushContext();
	[self lineStyleSub:0]; // styleはダミー
	CGRect imageRect=CGRectMake(sx, sy, ex-sx+1, ey-sy+1); // v2.1:CGRectは幅なので+1が必要
	CGContextFillRect(context,imageRect);
	[self draw];
}

-(void)circle:(CGFloat)cx cy:(CGFloat)cy r:(CGFloat)r
	startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle
	clockwise:(int)clockwise
		 fill:(BOOL)ffill
		style:(NSUInteger)style
{
	printSelf();
	nslog(@"context=%@,(%f,%f),r=%f,Angle:%f〜%f,clockwise=%d,style=$%04x,ffill=%d",context,cx,cy,r,startAngle,endAngle,clockwise,(int)style,ffill);
	pushContext();
	
	BOOL fStartArc=NO;
	if (startAngle<0) {
		fStartArc=YES;
		startAngle=-startAngle;
	}
	BOOL fEndArc=NO;
	if (endAngle<0) {
		fEndArc=YES;
		endAngle=-endAngle;
	}
	[self lineStyleSub:style];
	
	if (fStartArc) {
		nslog(@"扇モード：start");
		CGContextMoveToPoint (context, cx, cy);
	}
	CGContextAddArc (context,
					 cx,
					 cy,
					 r,
					 startAngle,
					 endAngle,
					 clockwise
					 );
	if (fEndArc) {
		nslog(@"扇モード：end");
		CGContextAddLineToPoint (context, cx, cy);
	}
	strokeOrFill(context,ffill);
	[self draw];
}

// r=半径
// hv=扁平率 ; >0.0:縦に扁平率 , <0.0:横に扁平率 (1.0または0.0=真円)
-(void)ellipse:(CGFloat)cx cy:(CGFloat)cy
			 r:(CGFloat)r
			hv:(CGFloat)hv
		 fill:(BOOL)ffill
		style:(NSUInteger)style
{
	printSelf();
	nslog(@"context=%@,(%f,%f),r=%f,hv=%f,style=$%04x,ffill=%d",context,cx,cy,r,hv,(int)style,ffill);
	
	pushContext();
	
	[self lineStyleSub:style];

	// Ellipseはrectに内接するように描画される
	CGFloat rh=r;
	CGFloat rw=r;
	if (hv>0) {
		// 縦楕円
		rh=r*hv;
	}
	else
	if (hv<0) {
		// 横楕円
		rw=r*hv;
	}
//	else // hv=0.0または1.0:真円の時はrh=r,rw=r
	
	CGRect rect={{cx-rw,cy-rh},{rw*2,rh*2}}; // point,size
	CGContextStrokeEllipseInRect(context,rect);
	strokeOrFill(context,ffill);
	[self draw];
}


-(CGSize)symbol:(CGFloat)sx sy:(CGFloat)sy
		  str:(NSString *)str
	 fontName:(NSString *)fontName
	pointSize:(CGFloat)pointSize
{
	printSelf();
	pushContext();
	CGPoint point={sx,sy};
	UIFont *font=[UIFont fontWithName:fontName size:pointSize];
	if (font==nil) {
		font=[UIFont fontWithName:DEFFONT size:pointSize];
	}
	// 描画色
	UIColor *drawColor=[UIColor colorWithRed:_cf(_rgbaColor.r)
									   green:_cf(_rgbaColor.g)
										blue:_cf(_rgbaColor.b)
									   alpha:_cf(_rgbaColor.alpha)];
	CGSize size=[str drawAtPoint7:point withFont:font withColor:drawColor];
	nslog(@"symbol.size=%f,%f",size.width,size.height);
	[self draw];
	return (size);
}

// 外部からの呼び出し用
-(void)lineStyle:(NSUInteger)style
{
	UIGraphicsPushContext2(context);
	[self lineStyleSub:style];
	UIGraphicsPopContext2();
}

// 内部呼び出し用
// 16bitでlineStyleを設定する
// lineWidthも考慮するので、CGContextSetLineDash()とは結果が違う
-(void)lineStyleSub:(NSUInteger)style
{
	printSelfs(@"style=$%04x",(int)style);
	//
	if (style==0 || style==0xffff) {
		// ラインスタイル指定なし
		nslog(@"ラインスタイル指定なし");
		CGContextSetLineDash(context,0,NULL,0);
	} else {
		CGFloat phase=0; // lengthの先頭から使用
		CGFloat lengths[16]; // 最大16bitで指定
		//
		nslog(@"ラインスタイル指定あり");
		// (1)lineWidth=1の時のパターンを作成する
		size_t count=setLineStyleWith16bit(style,lengths);
		// countは必ず偶数であり、length[偶数]＝on,[奇数]=off長さとなる
		int i;
		if (_lineWidth>1) {
			// on側がlinewidth*2分増えるので、off側にも+加えておく
			for (i=0;i<count;i+=2) {
				lengths[i+1]+=_lineWidth*2;
			}
		} // lineWidth=0,1の時はそのまま使える(0の時は実描画なし)
#ifdef DEBUG
		printf("補正後count=%ld\n",count);
		for (i=0;i<count;i++) {
			printf("%f,",lengths[i]);
		}
		printf("\n");
#endif
		// 設定
		CGContextSetLineDash(context,phase,lengths,count);
	}
	//
}

-(void)lineWidth:(CGFloat)width
{
	printSelfs(@"%f",width);
	UIGraphicsPushContext2(context);
	self.lineWidth=width; // dashの補正のため必要
	CGContextSetLineWidth(context, width);
	UIGraphicsPopContext2();
}

-(void)lineCap:(int)cap
{
	nslog(@"gConsole.lineCap:%d",cap);
	UIGraphicsPushContext2(context);
	CGContextSetLineCap(context, cap);
	UIGraphicsPopContext2();
}

-(void)lineJoin:(int)join
{
	printSelfs(@"%d",join);
	UIGraphicsPushContext2(context);
	CGContextSetLineJoin(context, join);
	UIGraphicsPopContext2();
}

-(void)blend:(int)blend
{
	printSelfs(@"%d",blend);
	UIGraphicsPushContext2(context);
	CGContextSetBlendMode(context, blend);
	UIGraphicsPopContext2();
}

-(void)pset:(CGFloat)x y:(CGFloat)y
{
	printSelf();
	pushContext();
	// Macの座標は大きさを持たない数学座標なのでドットにするには+1が必要
	CGContextMoveToPoint(   context,x  ,y  );
	CGContextAddLineToPoint(context,x+1,y+1);
	CGContextStrokePath(context);
	[self draw];
	// 本当は色設定も内包したかったが、結局contextの色設定をせねばならず、すると他の描画色も変わってしまうためcolorを呼び出すのと同じになってしまう
	// 描画後に色を戻す手もあるが、高速化という意味では本末転倒なので採用しない
}

-(void)color:(int)r g:(int)g b:(int)b alpha:(int)alpha
// r/g/b/alpha=0〜255
{
	printSelf();
	UIGraphicsPushContext2(context);
	_rgbaColor=getWithStandardColor(r,g,b,alpha); // 描画色を保存しておく
	setColor(context,_rgbaColor.r,_rgbaColor.g,_rgbaColor.b,_rgbaColor.alpha);
	UIGraphicsPopContext2();
}

-(void)clip:(CGFloat)sx sy:(CGFloat)sy ex:(CGFloat)ex ey:(CGFloat)ey
{
	printSelf();
	pushContext();
	//UIGraphicsPushContext2(context);
	CGRect area=CGRectMake(sx,sy,ex-sx+1,ey-sy+1); // v2.1:CGRectは幅なので+1が必要
	CGContextClipToRect(context,area);
#ifdef DEBUG
	printFrame(@"area",area);
	/* CGContextClipToRect
	 「指定された四角形によって定義された領域と現在のクリッピングパスの交差点にクリッピングパスを設定します。」とあるので、
	 現在の設定と重ならないと有効にならない＝新しく設定する意味では使えない。
	 */
	CGRect frame=CGContextGetClipBoundingBox(context);
	printFrame(@"現在のclip領域", frame);
#endif
	UIGraphicsPopContext2();
}


// CGContextSaveGState()とCGContextRestoreGState()は1:1で発行しなければならない（そうでないとCGContextRestoreGState()でエラーが発生する）

-(void)saveGState
{
	printSelf();
	UIGraphicsPushContext2(context);
	CGContextSaveGState(context);
	cntGState++; // 回数を記録:ネスティングは出来る
	UIGraphicsPopContext2();
}

-(void)restoreGState
{
	printSelf();
	UIGraphicsPushContext2(context);
	CGContextRestoreGState(context);
	cntGState--;
	UIGraphicsPopContext2();
}

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

@end
