//----------------------------------------------------------------------------------------------
//
//  gConsole+bitmap.m : グラフィック描画用コンソール＋ビットマップ
//  X-BASIC'
//
//  Created by AIG-Soft(iMac) on 2013/01/23.
//  Copyright (c) 2013年 AIG-Soft. All rights reserved.
//

#import "gConsole+bitmap.h"
#import <CoreGraphics/CoreGraphics.h>
#import "main.h"
#import "malloc2.h"
#import "rgbhsv.h"
#import "clipping.h"

@implementation gConsole (bitmap)

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

-(void)end
{
	printSelf();
	[super end];
	[self bitmapClose];
}

//---------------------------------------------------------------------------------------
// bitmap関数群
//---------------------------------------------------------------------------------------

// bitmapを確保する
-(BOOL)bitmapOpen:(int)width height:(int)height fdirect:(BOOL)fdirect_
{
	nslog(@"bitmapOpen:%d*%d",width,height);
	if (pixel!=NULL) return(NO); // すでに確保済みなので再確保できない
	
	fdirect=fdirect_;
	if (fdirect) {
		nslog(@"console内のbitmapを使うとき");
		// これはselfの情報である=gconsole自体が外部で切り替えられれば自動的に切り替わる
		width =self.bitmapWidth = consoleWidth;
		height=self.bitmapHeight= consoleHeight;
		byte1Line	= (int)consoleByte1Line;
		pixel		= consolePixel;
	} else { // 別ワーク確保
		// ピクセルデータのメモリ確保
		if (width<=0) {
			width=(int)frame0.size.width;
		}
		if (height<=0) {
			height=(int)frame0.size.height;
			
		}
		self.bitmapWidth =width;
		self.bitmapHeight=height;
		byte1Line	=self.bitmapWidth*PIXELBYTE;
		pixel		=(UByte *)malloc2(byte1Line*height);
		if (pixel==NULL) return(NO); // メモリが確保できない
	}
	nslog(@"self.bitmapWidth =%u",self.bitmapWidth);
	nslog(@"self.bitmapHeight=%d(%d)",self.bitmapHeight,height);
	nslog(@"byte1Line   =%d",byte1Line);
	nslog(@"pixel       =%p",pixel);
	// クリア
#if 0
	UByte *p=pixel;
	for (NSInteger y=0;y<self.bitmapHeight;y++) {
		for (NSInteger i=0;i<byte1Line;i++) {
			*p++=0;
		}
	}
	NSLog(@"p=%p,%d,lines=%d",p,(int)(p-pixel),(int)(p-pixel)/byte1Line);
#else
	memset(pixel,0,byte1Line*height);
#endif
	return (YES);
}

// bitmapからUIImageとして画像を取り出す
-(UIImage *)bitmap2UIImage
{
	printSelf();
	CGDataProviderRef provider=CGDataProviderCreateWithData(NULL, pixel, byte1Line*self.bitmapHeight, NULL);
	CGColorSpaceRef colorspace=CGColorSpaceCreateDeviceRGB();
	// CGImageを作成
	CGImageRef image0=CGImageCreate(self.bitmapWidth,	// 幅
								   self.bitmapHeight,	// 高さ
								   8,				// 構成要素1つのビット長
								   PIXELBYTE*8,		// ピクセルごとのビット長
								   byte1Line,		// 行ごとのバイト数 ; 1ピクセル4バイトだから	X68と同じ垂直構造
								   colorspace,		// 色空間
								   (kCGBitmapByteOrderDefault|kCGImageAlphaLast),	// ピクセルの構造
								   provider,		// ピクセルデータが入ったDataProvider
								   NULL,			// (標準値)
								   YES,				// YESにするとピクセル補完処理が働くらしいが、見た目上効果はわからなかった
								   kCGRenderingIntentDefault	// (標準値)
								   );
	// UIImageを作成
	UIImage *output=[UIImage imageWithCGImage:image0];
	// 不要になったDataProviderとcolorspaceを解放
	CGColorSpaceRelease(colorspace);
	CGDataProviderRelease(provider);
	CGImageRelease(image0);
	return (output);
}

// bitmapを開放する
-(void)bitmapClose
{
	printSelf();
	if (!fdirect) {
		// 不要になったピクセルメモリを開放
		if (pixel!=NULL) {
			free2(pixel);
		}
	} else { // console内ビットマップを使っているとき
		fdirect=NO;
		// ワーク開放は必要ない
	}
	pixel=NULL;
}

-(BOOL)checkXY:(int)x y:(int)y
// 座標のチェック
// YES : ビットマップ範囲外(描画してはいけない)
// NO  : 範囲内
{
	if (x<0||x>=self.bitmapWidth ) return(YES);
	if (y<0||y>=self.bitmapHeight) return(YES);
	return(NO);
}

-(int)checkX:(int)x
{
	if (x<0) x=0;
	if (x>=self.bitmapWidth) x=self.bitmapWidth-1;
	return (x);
}

-(int)checkY:(int)y
{
	if (y<0) y=0;
	if (y>=self.bitmapHeight) y=self.bitmapHeight-1;
	return (y);
}

void checkBitmapBoxArea(int *sx,int *sy,int *ex,int *ey)
// 四角形領域の座標の大小入れ替え
// sx<=ex , sy<=eyにする
{
	if (*ex<*sx) {int x=*ex; *ex=*sx; *sx=x;}
	if (*ey<*sy) {int y=*ey; *ey=*sy; *sy=y;}
}

-(void)checkBitmapBoxArea:(int *)sx sy:(int *)sy ex:(int *)ex ey:(int *)ey
{
	*sx=[self checkX:*sx];
	*sy=[self checkY:*sy];
	*sx=[self checkX:*sx];
	*sy=[self checkY:*sy];
	checkBitmapBoxArea(sx,sy,ex,ey); // 座標の大小入れ替え
}

// get/putで必要なバイト数を計算する
SLong calcBitmapBoxArea(int sx,int sy,int ex,int ey)
{
	checkBitmapBoxArea(&sx,&sy,&ex,&ey);
	return ((ex-sx+1)*(ey-sy+1)*4); // 1ドットr/g/b/alpha各8ビット
}

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

-(void)bitmapWipe
// bitmapをクリアする
{
	printSelf();
	if (pixel!=NULL) {
		memset(pixel,0,byte1Line*self.bitmapHeight);
	}
}

-(void)bitmapPset:(int)x y:(int)y r:(int)r g:(int)g b:(int)b alpha:(int)alpha
// bitmap用pset
{
//	printSelf();
	if (pixel!=NULL) {
		if ([self checkXY:x y:y]) return; // 座標チェック
		UByte *pixel0=calcBitmapAdrs(x,y);
		*pixel0++=r;
		*pixel0++=g;
		*pixel0++=b;
		*pixel0++=alpha;
	}
}

-(void)bitmapPset:(int)x y:(int)y rgba:(SLong)rgba
{
	int r,g,b,alpha;
	splitRGB(rgba, &r ,&g, &b, &alpha);
	[self bitmapPset:x y:y r:r g:g b:b alpha:alpha];
}

// bitmap用point
-(ULong)bitmapPoint:(int)x y:(int)y r:(int *)r g:(int *)g b:(int *)b alpha:(int *)alpha
{
//	printSelf();
	if (pixel==NULL) return(0);
	if ([self checkXY:x y:y]) return(0); // 座標チェック
	//
	UByte *pixel0=calcBitmapAdrs(x,y);
	*r=*pixel0++;
	*g=*pixel0++;
	*b=*pixel0++;
	int aa=255;
	if (alpha!=NULL) { // alphaは省かれることがあるので
		*alpha=aa=*pixel0++;
	}
	// 合成RGB値を返す
	return (rgb(*r,*g,*b,aa));
}

// bitmap用point
-(ULong)bitmapPoint:(int)x y:(int)y
{
//	printSelf();
	if (pixel==NULL) return(0);
	if ([self checkXY:x y:y]) return(0); // 座標チェック
	//
	UByte *pixel0=calcBitmapAdrs(x,y);
	int r=*pixel0++;
	int g=*pixel0++;
	int b=*pixel0++;
	int a=*pixel0++;
	// 合成RGB値を返す
	return (rgb(r,g,b,a));
}

// bitmap内画像をグラフィック画面に表示する
-(void)bitmapImgLoad:(CGFloat)sx sy:(CGFloat)sy wx:(CGFloat)wx wy:(CGFloat)wy ftile:(BOOL)ftile
{
	printSelfs(@"(ftile=%d)",ftile);
	
	pushContext();
	if (pixel==NULL) {
		nslog(@"pixcel=NULL");
		return;
	}
	if (!fdirect) {
		CGRect imageRect;
		if (wy<=CGSizeZero.height || wx<=CGSizeZero.width) {
			// 画像のサイズを使う
			imageRect.size.width =self.bitmapWidth;
			imageRect.size.height=self.bitmapHeight;
		} else {
			// ImgLoad():指定サイズに拡大／縮小 / tileImgLoad():領域サイズ
			imageRect.size=CGSizeMake(wx,wy);
		}
		CGPoint point={sx,sy};
		imageRect.origin=point;
		printFrame(@"imageRect", imageRect);
		//
		UIImage *loadImg=[self bitmap2UIImage];
		CGImageRef img = CGImageRetain(loadImg.CGImage); // ここで画像確定
		nslog(@"img=%@",img);
		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);
	}
	// fdirect時はすべての引数は無効
	[self draw];
//	[self draw:DRAW_WAIT];
	nslog(@"bitmapImgLoad end");
}

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

-(void)bitmapGet:(int)sx sy:(int)sy ex:(int)ex ey:(int)ey buff:(UByte *)buff
{
	if (pixel==NULL) {
		nslog(@"bitmapGet:pixcel=NULL");
		return;
	}
	
	[self checkBitmapBoxArea:&sx sy:&sy ex:&ex ey:&ey]; // 座標の変換と大小入れ替え

	nslog(@"sx=%d,sy=%d,ex=%d,ey=%d",sx,sy,ex,sy);
	
	for (int y=sy;y<=ey;y++) {
		UByte *pixel0=calcBitmapAdrs(sx,y);
		for (int x=sx;x<=ex;x++) {
			// 座標チェック
			if (![self checkXY:x y:y]) {
				// 範囲内
				*(buff+3)=*pixel0++; // r
				*(buff+2)=*pixel0++; // g
				*(buff+1)=*pixel0++; // b
				*(buff+0)=*pixel0++; // alpha
			}
			buff+=SZ_LONG;
		}
	}
}

-(void)bitmapPut:(int)sx sy:(int)sy ex:(int)ex ey:(int)ey buff:(UByte *)buff
{
	if (pixel==NULL) {
		nslog(@"bitmapPut:pixcel=NULL");
		return;
	}
	[self checkBitmapBoxArea:&sx sy:&sy ex:&ex ey:&ey]; // 座標の変換と大小入れ替え

	nslog(@"sx=%d,sy=%d,ex=%d,ey=%d",sx,sy,ex,sy);
	
	for (int y=sy;y<=ey;y++) {
		UByte *pixel0=calcBitmapAdrs(sx,y);
		for (int x=sx;x<=ex;x++) {
			// 座標チェック
			if (![self checkXY:x y:y]) {
				// 範囲内
				*pixel0++=*(buff+3); // r
				*pixel0++=*(buff+2); // g
				*pixel0++=*(buff+1); // b
				*pixel0++=*(buff+0); // alpha
			}
			buff+=SZ_LONG;
		}
	}
}

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

-(void)bitmapPattern:(int)sx sy:(int)sy wx:(int)wx wy:(int)wy ptrn:(UByte *)ptrn
// v2.2
// 本来ptrnはULongでありスプライト(iOS型)と同じで、各要素は合成rgb値(H:r/g/b/alpha:L順)
{
	if (pixel==NULL) return;
	for (int y=sy;y<sy+wy;y++) {
		UByte *pixel0=calcBitmapAdrs(sx,y);
		for (int x=sx;x<sx+wx;x++) {
			// 座標チェック
			if (![self checkXY:x y:y]) {
				// 範囲内
				// pixelはH:alpha/b/g/r:L順なので以下の順になる
				*pixel0++=*(ptrn+3); // r
				*pixel0++=*(ptrn+2); // g
				*pixel0++=*(ptrn+1); // b
				*pixel0++=*(ptrn+0); // alpha
			} // 範囲外はデータを飛ばすだけ
			ptrn+=SZ_LONG;
		}
	}
}

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

@end
