/*
 
 views.h ... Views and Layouts in 邃修lipboard.
 
 Copyright (c) 2009, KennyTM~
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:
 
 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 * Neither the name of the KennyTM~ nor the names of its contributors may be
   used to endorse or promote products derived from this software without
   specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 */

#import "views.h"
#import <iKeyEx/common.h>
#import <iKeyEx/ImageLoader.h>
#import <iKeyEx/KeyboardLoader.h>
#import <iKeyEx/UIKBSpecialKeyButtons.h>
#import <iKeyEx/UIKBSound.h>
#import <UIKit2/Functions.h>
#import <UIKit2/UIKeyboardImpl.h>
//#import "clipboard.h"
//#import "datasource.h"
#import "controller.h"





#pragma mark -

@implementation GestureView

//-(void)flashFirstRowImplicit {
//	// make 0th row visible
//	if (flash0thRow) {
//		[self selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionNone]; 
//		if (self.contentOffset.y > 0) {
//			[self scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
//		} else {
//			[self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:self afterDelay:0.15];
//		}
//		[self performSelector:@selector(reloadData) withObject:nil afterDelay:0.075];
//	}
//}

//-(void)flashFirstRow {
//	flash0thRow = YES;
//	[self flashFirstRowImplicit];	
//}

//-(void)scrollViewDidEndScrollingAnimation:(UIScrollView*)tbl {
//	if (flash0thRow) {
//		flash0thRow = NO;
//		[(UITableView*)tbl deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES]; 
//	}
//}

//-(Clipboard*)clipboard { return datasource.clipboard; }
//-(void)setClipboard:(Clipboard*)clpbrd { datasource.clipboard = clpbrd; [self reloadData]; }

-(void)dealloc {
	[gestures removeAllObjects];
	[gestures release];
	
	[gesturesNumber removeAllObjects];
	[gesturesNumber release];

	[gesturesSymbol removeAllObjects];
	[gesturesSymbol release];

	
	[moves removeAllObjects];
	[moves release];
	[points removeAllObjects];
	[points release];
	
	[_target release];
//	[datasource release];
	[super dealloc];
}



//-(void)tableView:(UITableView*)tbl didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
//	Clipboard* clpbrd = datasource.clipboard;
//	NSUInteger row = indexPath.row;
//	[clpbrd updateEntryAtReversedIndex:row];
//	if (_action != NULL) {
//		[_target performSelector:_action withObject:[clpbrd lastData] withObject:tbl];
//	}
//	
//	[UIKBSound play];
//	
//	// If you're expect in Graphics Design, please suggest a better, workable animation sequence :)
//	[tbl deselectRowAtIndexPath:indexPath animated:YES];
//	flash0thRow = (row > 0);
//	[self flashFirstRowImplicit];
//}

//-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
//	// avoid crash on swipe
//	if (event == nil)
//		return NO;
//	// avoid crash on swipe when the board is empty.
//	// See http://discussions.apple.com/thread.jspa?messageID=8439372 for detail.
//	else if ([datasource tableView:self numberOfRowsInSection:0] == 0)
//		return NO;
//	else
//		return CGRectContainsPoint(self.bounds, point);
//}

-(id)initWithFrame:(CGRect)frm {
	if ((self = [super initWithFrame:frm])) {
//		self.rowHeight = 32;
//		self.separatorStyle = UITableViewCellSeparatorStyleNone;
		//self.backgroundColor = [UIColor clearColor];
		self.backgroundColor = [UIColor lightGrayColor];
//		datasource = [[hCClipboardDataSource alloc] init];
//		self.dataSource = datasource;
//		self.delegate = self;
		_target = nil;
		_action = NULL;
//		self.scrollsToTop = NO;
		
//		emptyClipboardIndicator = [[UILabel alloc] initWithFrame:CGRectZero];
//		emptyClipboardIndicator.textColor = [UIColor whiteColor];
//		emptyClipboardIndicator.shadowColor = [UIColor colorWithWhite:0 alpha:0.5];
//		emptyClipboardIndicator.font = [UIFont boldSystemFontOfSize:[UIFont smallSystemFontSize]];
//		emptyClipboardIndicator.textAlignment = UITextAlignmentCenter;
//		emptyClipboardIndicator.backgroundColor = [UIColor clearColor];
//		[self addSubview:emptyClipboardIndicator];
//		[emptyClipboardIndicator release];
		
//		[self reloadData];

		//for Gesture
		gestures = [[NSMutableArray alloc]init];
		gesturesNumber = [[NSMutableArray alloc]init];
		gesturesSymbol = [[NSMutableArray alloc]init];

		moves = [[NSMutableArray alloc]init];
		points = [[NSMutableArray alloc]init];
		
		capsInfo = [[UILabel alloc] initWithFrame:CGRectZero];
		capsInfo.backgroundColor = [UIColor clearColor];	
		[capsInfo setText:@"[a]"];
		[self addSubview:capsInfo];
		[capsInfo release];

		[self initData];		

		[self setGesture];
		
		//for Legacy
		[super setTapDelegate: self];

	}
	return self;
}

-(void)setTarget:(id)target action:(SEL)action {
	if (target != _target) {
		[_target release];
		_target = [target retain];
	}
	_action = action;
}

-(void)layoutSubviews {
	[super layoutSubviews];
	int ori = [[UIKeyboardImpl sharedInstance] orientation];
	BOOL landsc = (ori == 90 || ori == -90);
	if (!landsc) {
		capsInfo.frame = CGRectMake(8, 172-16-25, 40, 20);
	} else {
		capsInfo.frame = CGRectMake(8, 119-16-25, 40, 20);
	}
}
//-(void)setPlaceholderText:(NSString*)txt { emptyClipboardIndicator.text = txt; }
//-(BOOL)switchClipboard {
//	BOOL result = [datasource switchClipboard];
//	[self reloadData];
//	return result;
//}

//-(BOOL)isDefaultClipboard { return datasource.usesPrefix; }



- (void)drawRect:(CGRect)rect {
    // Drawing code
	// Get the current context 
	CGContextRef context = UIGraphicsGetCurrentContext();
	
	CGContextBeginPath(context);	
	
	
	if([points count] >= 2)
	{
	
		CGPoint firstPoint = [[points objectAtIndex:0]CGPointValue];	
	
		CGFloat gray[4] = {0.5f, 0.5f, 0.5f, 1.0f}; 
		
		CGFloat red[4] = {0.75f, 0.25f, 0.25f, 1.0f}; 
		CGContextSetStrokeColor(context, red); 
		CGContextSetFillColor(context, red); 
			
	    CGContextFillEllipseInRect(context,CGRectMake(firstPoint.x - 10, firstPoint.y - 10,20,20));
		CGContextMoveToPoint (context, firstPoint.x, firstPoint.y);		
		// otherPoint
		for(int nCount = 1;nCount < [points count];nCount++)
		{	
			CGPoint thePoint =  [[points objectAtIndex:nCount]CGPointValue];
			CGContextAddLineToPoint (context, thePoint.x, thePoint.y);		
		    //CGContextFillEllipseInRect(context,CGRectMake(thePoint.x, thePoint.y,10,10));
    		//CGContextMoveToPoint (context, thePoint.x, thePoint.y);		

		}
		//CGContextClosePath (context);
		
		CGContextSetLineWidth(context, 10.0f); 

			
		CGContextSetRGBFillColor(context, 0xff/255.0, 0xd7/255.0,0x00/255.0, 1); 
		CGContextDrawPath (context, kCGPathStroke);
	}
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
	
//	CGPoint pt = [[touches anyObject] locationInView:self]; 
		
//	NSLog(@"StartGesture/n");
//	//ジェスチャー内容クリア
//	if([curGesture count]>0)
//	{
//		[curGesture removeAllObjects];
//	}
//	if([curGesturePoint count]>0)
//	{
//		[curGesturePoint removeAllObjects];
//		
//	}
//	//初期位置をセット
	
	CGPoint pt = [[touches anyObject] locationInView:self]; 

	lastPoint.x = pt.x;
	lastPoint.y = pt.y;

	rect.origin.x = 1000;
	rect.origin.y = 1000;
	rect.size.width = 0;
	rect.size.height = 0;

	[moves removeAllObjects];
	[points removeAllObjects];

	[self setNeedsDisplay]; 	
	
	
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
	
	CGPoint pt = [[touches anyObject] locationInView:self]; 
	// calcul dif 
	int msx = pt.x;//  現在のマウス位置
	int msy = pt.y;
	
	int difx = msx - lastPoint.x;  //前回との差
	int dify = msy - lastPoint.y;
	float sqDist=difx*difx+dify*dify;// 前回との距離
	float sqPrec= DEFAULT_PRECISION * DEFAULT_PRECISION;
	
	if (sqDist>sqPrec){ //前回との距離が一定以上だったら
		[points addObject:[NSValue valueWithCGPoint:CGPointMake(msx,msy)]];// ポイントをPUSH
		[self addMove:difx dify:dify];              //移動ををPUSH
		lastPoint.x=msx;                 //最後の位置
		lastPoint.y=msy;
		
		if (msx<rect.origin.x)rect.origin.x=msx; //判定用の矩形 本来のrectの使い方とは違うけど…。
		if (msx>rect.size.width)rect.size.width=msx;
		if (msy<rect.origin.y)rect.origin.y=msy;
		if (msy>rect.size.height)rect.size.height=msy;
		[self setNeedsDisplay]; 	
	}

}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
	int ori = [[UIKeyboardImpl sharedInstance] orientation];
	BOOL landsc = (ori == 90 || ori == -90);
	int checkX ;
	if (!landsc) {
		checkX  = (8 +  320-36-16) / 2;
	} else {
	    checkX  = (8 + 480-72-16 ) /2 ;
	}
	int aveX =  (rect.origin.x + rect.size.width )/ 2;
	
	
	if([_target getSymbol] == YES)//SymbolMode
	{
		[self matchGesture:2]; //検出
	}
	else
	{
		if(aveX <= checkX)
		{
			[self matchGesture:0]; //検出
		}
		else
		{
			[self matchGesture:1]; //検出
		}
	}
	[moves removeAllObjects];
	[points removeAllObjects];


	if([_target getSymbol] == YES)//SymbolMode
	{
		[capsInfo setText:@"[.]"];
	}
	else
	{
		if([_target getCaps] == NO)
		{
			[capsInfo setText:@"[a]"];
		}
		else
		{
			[capsInfo setText:@"[A]"];
		}
	}

	[self setNeedsDisplay]; 	

}

//for Legacy
- (void)mouseDown: (GSEventRef)event
{
	CGPoint pt =GSEventGetInnerMostPathPosition(event);
	lastPoint.x = pt.x;
	lastPoint.y = pt.y;

	rect.origin.x = 1000;
	rect.origin.y = 1000;
	rect.size.width = 0;
	rect.size.height = 0;

	[moves removeAllObjects];
	[points removeAllObjects];

	[self setNeedsDisplay];
}

- (void)mouseDragged: (GSEventRef)event
{
	CGPoint pt =GSEventGetInnerMostPathPosition(event);
 	//CGPoint pt = GSEventGetLocationInWindow(event);
	// calcul dif 
	int msx = pt.x;//  現在のマウス位置
	int msy = pt.y;
	
	int difx = msx - lastPoint.x;  //前回との差
	int dify = msy - lastPoint.y;
	float sqDist=difx*difx+dify*dify;// 前回との距離
	float sqPrec= DEFAULT_PRECISION * DEFAULT_PRECISION;
	
	if (sqDist>sqPrec){ //前回との距離が一定以上だったら
		[points addObject:[NSValue valueWithCGPoint:CGPointMake(msx,msy)]];// ポイントをPUSH
		[self addMove:difx dify:dify];              //移動ををPUSH
		lastPoint.x=msx;                 //最後の位置
		lastPoint.y=msy;
		
		if (msx<rect.origin.x)rect.origin.x=msx; //判定用の矩形 本来のrectの使い方とは違うけど…。
		if (msx>rect.size.width)rect.size.width=msx;
		if (msy<rect.origin.y)rect.origin.y=msy;
		if (msy>rect.size.height)rect.size.height=msy;
		[self setNeedsDisplay]; 	
	}

}

- (void)mouseUp: (GSEventRef)event
{
    int ori = [[UIKeyboardImpl sharedInstance] orientation];
	BOOL landsc = (ori == 90 || ori == -90);
	int checkX ;
	if (!landsc) {
		checkX  = (8 +  320-36-16) / 2;
	} else {
	    checkX  = (8 + 480-72-16 ) /2 ;
	}
	int aveX =  (rect.origin.x + rect.size.width )/ 2;
	
	
	if([_target getSymbol] == YES)//SymbolMode
	{
		[self matchGesture:2]; //検出
	}
	else
	{
		if(aveX <= checkX)
		{
			[self matchGesture:0]; //検出
		}
		else
		{
			[self matchGesture:1]; //検出
		}
	}
	[moves removeAllObjects];
	[points removeAllObjects];


	if([_target getSymbol] == YES)//SymbolMode
	{
		[capsInfo setText:@"[.]"];
	}
	else
	{
		if([_target getCaps] == NO)
		{
			[capsInfo setText:@"[a]"];
		}
		else
		{
			[capsInfo setText:@"[A]"];
		}
	}

	[self setNeedsDisplay]; 	
 
}




-(void)addGesture:(NSString*)o gesture:(NSString*)gesture matchHandler:(uint)matchHandler
{
	NSMutableArray *g=[[[NSMutableArray alloc]initWithCapacity:1]autorelease ];
	for (uint i=0;i<gesture.length;i++){
		if([[gesture substringWithRange:NSMakeRange(i,1)]compare:@"."]==NSOrderedSame)
		{
			[g addObject:[NSNumber numberWithInt:-1]];
		}
		else
		{
			[g addObject:[NSNumber numberWithInt:[[gesture substringWithRange:NSMakeRange(i,1)]intValue]]];			
		}		 
	}
	gestureInfo *gInfo = [[[gestureInfo alloc] initWithGestureData:o moves:g match:matchHandler]autorelease]; 
	[gestures addObject:gInfo];
}


-(void)addNumberGesture:(NSString*)o gesture:(NSString*)gesture matchHandler:(uint)matchHandler
{
	NSMutableArray *g=[[[NSMutableArray alloc]initWithCapacity:1]autorelease ];
	for (uint i=0;i<gesture.length;i++){
		if([[gesture substringWithRange:NSMakeRange(i,1)]compare:@"."]==NSOrderedSame)
		{
			[g addObject:[NSNumber numberWithInt:-1]];
		}
		else
		{
			[g addObject:[NSNumber numberWithInt:[[gesture substringWithRange:NSMakeRange(i,1)]intValue]]];			
		}		 
	}
	gestureInfo *gInfo = [[[gestureInfo alloc] initWithGestureData:o moves:g match:matchHandler]autorelease]; 
	[gesturesNumber addObject:gInfo];
}

-(void)addSymbolGesture:(NSString*)o gesture:(NSString*)gesture matchHandler:(uint)matchHandler
{
	NSMutableArray *g=[[[NSMutableArray alloc]initWithCapacity:1]autorelease ];
	for (uint i=0;i<gesture.length;i++){
		if([[gesture substringWithRange:NSMakeRange(i,1)]compare:@"."]==NSOrderedSame)
		{
			[g addObject:[NSNumber numberWithInt:-1]];
		}
		else
		{
			[g addObject:[NSNumber numberWithInt:[[gesture substringWithRange:NSMakeRange(i,1)]intValue]]];			
		}		 
	}
	gestureInfo *gInfo = [[[gestureInfo alloc] initWithGestureData:o moves:g match:matchHandler]autorelease]; 
	[gesturesSymbol addObject:gInfo];
}

-(void)initData
{
	[self buildAnglesMap];
	[gestures removeAllObjects];
	
}

//アングルマップ
-(void) buildAnglesMap
{
	
	// Angle of one sector
	sectorRad=M_PI*2/DEFAULT_NB_SECTORS;
	
	// map containing sectors no from 0 to PI*2
	anglesMap=[[NSMutableArray alloc] initWithCapacity:1];
	
	// the precision is Math.PI*2/100
	float step =M_PI*2/100;
	
	// memorize sectors
	int sector;
	for (float i=-sectorRad/2;i<=M_PI*2-sectorRad/2;i+=step){
		sector=floor((i+sectorRad/2)/sectorRad);
		[anglesMap addObject:[NSNumber numberWithInt:sector]];
	}
}	


-(void) addMove:(int)dx dify:(int)dy
{
	float angle=atan2(dy,dx)+sectorRad/2;
	if (angle<0)angle+=M_PI*2;
	int no=floorf(angle/(M_PI*2)*100);
	[moves addObject:[anglesMap objectAtIndex:no]];//  移動方向から方向番号を判定、PUSH
}		


-(void)matchGesture:(int)mode
{
//mode = 0 alphabet 
//mode = 1 number
//mode = 2 Symbol
	
	if([moves count] == 0)
	{
		[_target performSelector:@selector(paste:) withObject:@"DOT"];
	}
	
	uint bestCost = 1000000;  //コスト初期値
	uint nbGestures;
	if(mode == 0)
	{
		nbGestures =[gestures count]; //チェックするジェスチャーの数
	}
	else if(mode == 1)
	{
		nbGestures =[gesturesNumber count];
	}
	else
	{
		nbGestures =[gesturesSymbol count];
	}
	uint cost;//計算コスト
	//NSArray *gest;  //いま比較するジェスチャー
	gestureInfo * bestGesture = nil;  
	gestureInfo *infos
		= [[gestureInfo alloc]initWithGestureData2:moves lastPoint:lastPoint rect:rect];

	for (uint i=0;i < nbGestures;i++){ // チェック対象ジェスチャーの数だけ
		
		
		
		NSArray *gest;
		if(mode == 0)
		{		
			gest= [[NSArray alloc]initWithArray:((gestureInfo*)[gestures objectAtIndex:i]).moves]; //チェックジェスチャーの移動一覧
			infos.datas= [NSString stringWithString:((gestureInfo*)[gestures objectAtIndex:i]).datas]; // ジェスチャーに一致した場合の文字
		
			cost=[self costLeven:gest b:moves]; // コスト計算
		
			if (cost<=DEFAULT_FIABILITY){ 
				if (((gestureInfo*)[gestures objectAtIndex:i]).matchHandler!=0){// もし特別関数があるなら
					infos.cost=cost;
					//cost=[((gestureInfo*)[gestures objectAtIndex:i]).matchHandler infos]; //関数呼び出し
					//SEL tempFunc = ((gestureInfo*)[gestures objectAtIndex:i]).matchHandler;
					cost =[self callSpacialCase:((gestureInfo*)[gestures objectAtIndex:i]).matchHandler infos:infos];
				}
				if (cost<bestCost){
					bestCost=cost;
					bestGesture=(gestureInfo*)[gestures objectAtIndex:i];
				}
			}
		}
		else if(mode == 1)
		{		
			gest= [[NSArray alloc]initWithArray:((gestureInfo*)[gesturesNumber objectAtIndex:i]).moves]; //チェックジェスチャーの移動一覧
			infos.datas= [NSString stringWithString:((gestureInfo*)[gesturesNumber objectAtIndex:i]).datas]; // ジェスチャーに一致した場合の文字
		
			cost=[self costLeven:gest b:moves]; // コスト計算
		
			if (cost<=DEFAULT_FIABILITY){ 
				if (((gestureInfo*)[gesturesNumber objectAtIndex:i]).matchHandler!=0){// もし特別関数があるなら
					infos.cost=cost;
					//cost=[((gestureInfo*)[gesturesNumber objectAtIndex:i]).matchHandler infos]; //関数呼び出し
					//SEL tempFunc = ((gestureInfo*)[gesturesNumber objectAtIndex:i]).matchHandler;
					cost =[self callSpacialCase:((gestureInfo*)[gesturesNumber objectAtIndex:i]).matchHandler infos:infos];
				}
				if (cost<bestCost){
					bestCost=cost;
					bestGesture=(gestureInfo*)[gesturesNumber objectAtIndex:i];
				}
			}
		}
		else
		{		
			gest= [[NSArray alloc]initWithArray:((gestureInfo*)[gesturesSymbol objectAtIndex:i]).moves]; //チェックジェスチャーの移動一覧
			infos.datas= [NSString stringWithString:((gestureInfo*)[gesturesSymbol objectAtIndex:i]).datas]; // ジェスチャーに一致した場合の文字
		
			cost=[self costLeven:gest b:moves]; // コスト計算
		
			if (cost<=DEFAULT_FIABILITY){ 
				if (((gestureInfo*)[gesturesSymbol objectAtIndex:i]).matchHandler!=0){// もし特別関数があるなら
					infos.cost=cost;
					//cost=[((gestureInfo*)[gesturesSymbol objectAtIndex:i]).matchHandler infos]; //関数呼び出し
					//SEL tempFunc = ((gestureInfo*)[gesturesSymbol objectAtIndex:i]).matchHandler;
					cost =[self callSpacialCase:((gestureInfo*)[gesturesSymbol objectAtIndex:i]).matchHandler infos:infos];
				}
				if (cost<bestCost){
					bestCost=cost;
					bestGesture=(gestureInfo*)[gesturesSymbol objectAtIndex:i];
				}
			}
		}

	}
	
	
	if (bestGesture!=nil){
		//NSString * bestData = [NSString	stringWithString:bestGesture.datas];
		[_target performSelector:@selector(paste:) withObject:bestGesture.datas];
	}else{
		//dispatchEvent(new GestureEvent(GestureEvent.NO_MATCH));
	}
}


//角度の差				
-(uint) difAngle:(uint)a b:(uint)b
{
	uint dif=abs(a-b);
	if (dif>DEFAULT_NB_SECTORS/2)dif=DEFAULT_NB_SECTORS-dif;
	return dif;
}


-(uint) costLeven:(NSArray*)a b:(NSArray*)b
{	
	// point
	if ([a objectAtIndex:0]==-1)
	{
		return [b count]==0 ? 0 : 100000;
	}
	
	if ([b count] >= 100)
	{
		return 100000;
	}
	
	// precalc difangles
	//var d:Array=fill2DTable(a.length+1,b.length+1,0);
	uint d[100][100] = {0};
	uint w[100][100] = {0};
	
	//var w:Array=d.slice(); //複製作成
	uint x = 0;
	uint y = 0;
	for (x=1;x<=[a count];x++){
		for (y=1;y<[b count];y++){
			d[x][y]=[self difAngle:[[a objectAtIndex:x-1]intValue] b:[[b objectAtIndex:y-1]intValue]];
		}
	}
	
	// max cost
	for ( y=1;y<=[b count];y++)w[0][y]=100000;
	for ( x=1;x<=[a count];x++)w[x][0]=100000;
	w[0][0]=0;
	
	// levensthein application
	uint cost=0;
	uint pa;
	uint pb;
	uint pc;
	
	for ( x=1;x<=[a count];x++)
	{
		for ( y=1;y<[b count];y++)
		{
			cost=d[x][y];
			pa=w[x-1][y]+cost;
			pb=w[x][y-1]+cost;
			pc=w[x-1][y-1]+cost;
			int min = pa;
			if(min > pb)
			{
				min = pb;
			}
			if(min > pc)
			{
				min = pc;
			}
			
			w[x][y]=min;
		}
	}
	
	return w[x-1][y-1];
}	

-(void)setGesture
{
	[self addGesture:@"A" gesture:@"71" matchHandler:0];
	[self addGesture:@"B" gesture:@"260123401234" matchHandler:0];
	[self addGesture:@"B" gesture:@"0123401234" matchHandler:0];
	[self addGesture:@"C" gesture:@"43210" matchHandler:0];
	//SEL d_match = @selector(dMatch:);
	[self addGesture:@"D" gesture:@"26701234" matchHandler:D_MATCH];
	[self addGesture:@"E" gesture:@"4321043210" matchHandler:0];
	[self addGesture:@"F" gesture:@"42" matchHandler:0];
    //SEL g_match = @selector(gMatch:);
	[self addGesture:@"G" gesture:@"432107650" matchHandler:G_MATCH];
	[self addGesture:@"H" gesture:@"267012" matchHandler:H_MATCH];
	[self addGesture:@"I" gesture:@"2" matchHandler:0];
	[self addGesture:@"J" gesture:@"234" matchHandler:0];
	[self addGesture:@"K" gesture:@"3456701" matchHandler:0];
	[self addGesture:@"L" gesture:@"20" matchHandler:0];
	[self addGesture:@"M" gesture:@"6172" matchHandler:M_MATCH];
	[self addGesture:@"N" gesture:@"616" matchHandler:0];
	//SEL o_match = @selector(oMatch:);
	[self addGesture:@"O" gesture:@"432107654" matchHandler:O_MATCH];
    //SEL p_match = @selector(pMatch:);
	[self addGesture:@"P" gesture:@"26701234" matchHandler:P_MATCH];
    //SEL q_match = @selector(qMatch);
	[self addGesture:@"Q" gesture:@"4321076540" matchHandler:Q_MATCH];
	[self addGesture:@"R" gesture:@"267012341" matchHandler:0];
	//SEL s_match = @selector(sMatch:);	
	[self addGesture:@"S" gesture:@"432101234" matchHandler:S_MATCH];
	[self addGesture:@"T" gesture:@"02" matchHandler:0];
	[self addGesture:@"U" gesture:@"21076" matchHandler:0];
	[self addGesture:@"V" gesture:@"35" matchHandler:0];
	[self addGesture:@"V" gesture:@"170" matchHandler:0];	
	[self addGesture:@"W" gesture:@"2716" matchHandler:0];
	[self addGesture:@"X" gesture:@"1076543" matchHandler:0];
	[self addGesture:@"Y" gesture:@"21076234567" matchHandler:0];
	[self addGesture:@"Z" gesture:@"030" matchHandler:0];
	
	[self addNumberGesture:@"0" gesture:@"4321076542" matchHandler:0];
	[self addNumberGesture:@"1" gesture:@"72" matchHandler:0];
	[self addNumberGesture:@"2" gesture:@"701230" matchHandler:0];
	[self addNumberGesture:@"3" gesture:@"0123401234" matchHandler:0];
	[self addNumberGesture:@"4" gesture:@"302" matchHandler:0];
	//SEL five_match = @selector(fiveMatch:);	
	[self addNumberGesture:@"5" gesture:@"4201234" matchHandler:FIVE_MATCH];
	//SEL six_match = @selector(sixMatch:);	
	[self addNumberGesture:@"6" gesture:@"43210765" matchHandler:SIX_MATCH];
	[self addNumberGesture:@"7" gesture:@"03" matchHandler:0];
	[self addNumberGesture:@"8" gesture:@"4321234567654" matchHandler:0];
	[self addNumberGesture:@"9" gesture:@"43210762" matchHandler:0];
	
	[self addGesture:@"/" gesture:@"7" matchHandler:0];
	[self addGesture:@"\\" gesture:@"1" matchHandler:0];
	
	[self addGesture:@"SHIFT" gesture:@"6" matchHandler:0];			
	[self addGesture:@"BACKSPACE" gesture:@"4" matchHandler:0];
	[self addGesture:@"SPACE" gesture:@"0" matchHandler:0];
	[self addGesture:@"DOT" gesture:@"." matchHandler:0];			
	[self addNumberGesture:@"SHIFT" gesture:@"6" matchHandler:0];			
	[self addNumberGesture:@"BACKSPACE" gesture:@"4" matchHandler:0];
	[self addNumberGesture:@"SPACE" gesture:@"0" matchHandler:0];
	[self addNumberGesture:@"DOT" gesture:@"." matchHandler:0];			

	[self addSymbolGesture:@"DOT" gesture:@"." matchHandler:0];		
	[self addSymbolGesture:@"," gesture:@"3" matchHandler:0];		
	[self addSymbolGesture:@"'" gesture:@"2" matchHandler:0];		
	[self addSymbolGesture:@"`" gesture:@"51" matchHandler:0];		
	
	[self addSymbolGesture:@"-" gesture:@"0" matchHandler:0];		
	[self addSymbolGesture:@"/" gesture:@"7" matchHandler:0];
	[self addSymbolGesture:@"\\" gesture:@"1" matchHandler:0];
	[self addSymbolGesture:@"?" gesture:@"7012321" matchHandler:0];
	[self addSymbolGesture:@"!" gesture:@"6" matchHandler:0];
	[self addSymbolGesture:@"$" gesture:@"432101234" matchHandler:S_MATCH];
	[self addSymbolGesture:@"~" gesture:@"252" matchHandler:0];
	[self addSymbolGesture:@"=" gesture:@"030" matchHandler:0];	
	[self addSymbolGesture:@"_" gesture:@"04" matchHandler:0];	
	[self addSymbolGesture:@"|" gesture:@"62" matchHandler:0];	

	[self addSymbolGesture:@"^" gesture:@"71" matchHandler:0];		
	[self addSymbolGesture:@"?" gesture:@"03" matchHandler:0];		
	[self addSymbolGesture:@"%" gesture:@"123456701234567" matchHandler:0];		
	[self addSymbolGesture:@"+" gesture:@"3456701" matchHandler:0];		
	[self addSymbolGesture:@"@" gesture:@"432107654" matchHandler:O_MATCH];	
	[self addSymbolGesture:@"&" gesture:@"4321234567654" matchHandler:0];		
	[self addSymbolGesture:@"*" gesture:@"1076543" matchHandler:0];		
	//[self addSymbolGesture:@"TAB" gesture:@"42" matchHandler:0];		
	[self addSymbolGesture:@"\"" gesture:@"616" matchHandler:0];		
	[self addSymbolGesture:@"#" gesture:@"272" matchHandler:0];		
	[self addSymbolGesture:@":" gesture:@"26" matchHandler:0];		
	[self addSymbolGesture:@";" gesture:@"37" matchHandler:0];		
	[self addSymbolGesture:@"(" gesture:@"321" matchHandler:0];		
	[self addSymbolGesture:@")" gesture:@"123" matchHandler:0];		
	[self addSymbolGesture:@"{" gesture:@"4321043210" matchHandler:0];		
	[self addSymbolGesture:@"}" gesture:@"0123401234" matchHandler:0];		
	[self addSymbolGesture:@"<" gesture:@"57" matchHandler:0];		
	[self addSymbolGesture:@">" gesture:@"75" matchHandler:0];		
	[self addSymbolGesture:@"[" gesture:@"4567045670" matchHandler:0];		
	[self addSymbolGesture:@"]" gesture:@"0765407654" matchHandler:0];		
	
}


-(uint)callSpacialCase:(uint)spCase infos:(gestureInfo*)infos
{
	switch(spCase){
		case D_MATCH :
			return [self dMatch:infos];
		
		case G_MATCH :
			return [self gMatch:infos];

		case O_MATCH :
			return [self oMatch:infos];

		case P_MATCH :
			return [self pMatch:infos];

		case Q_MATCH :
			return [self qMatch:infos];

		case S_MATCH :
			return [self sMatch:infos];

		case FIVE_MATCH :
			return [self fiveMatch:infos];

		case SIX_MATCH :
			return [self sixMatch:infos];

		case H_MATCH :
			return [self hMatch:infos];

		case M_MATCH :
			return [self mMatch:infos];


		default:
		 return 10000;
		}


}


/**
* G - Special case
*/
-(uint) gMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py>.3 ? infos.cost : 10000;
}

/**
* Q - Special case
*/
-(uint) qMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py<.3 ? infos.cost : 10000;
}
		
/**
* D - Special case
*/
-(uint) dMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py>.8 ? infos.cost : 10000;
}

		
/**
* P - Special case
*/
-(uint) pMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py<.7 ? infos.cost : 10000;
}


/**
* O - Special case
*/
-(uint) oMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py<.3 ? infos.cost : 10000;
}

/**
* 6 - Special case
*/
-(uint) sixMatch:(gestureInfo*)infos
{
	float py=(infos.lastPoint.y-infos.rect.origin.y)/(infos.rect.size.height);
	return py>.3 ? infos.cost : 10000;
}

/**
* 5 - Special case
*/
-(uint) fiveMatch:(gestureInfo*)infos
{
	NSMutableString *testString = [NSMutableString stringWithCapacity:1];
	for (int i = 0;i < [infos.moves count];i++)
	{
		[testString appendFormat:@"%u",[[infos.moves objectAtIndex:i]intValue]];
	}
	NSRange resultRange = [testString rangeOfString:@"111" 
					  options:NSCaseInsensitiveSearch //NSCaseInsensitiveSearch
						range:NSMakeRange(0,testString.length)];
	if(NSNotFound == resultRange.location)
	{
		return infos.cost;
	}
	else
	{
		return 10000;
	}
	
	//var pos:int=infos.moves.join("").indexOf("111");
	//return pos==-1 ? infos.cost : 10000;
}

	
/**
* S - Special case
*/
-(uint) sMatch:(gestureInfo*)infos
{
	NSMutableString *testString = [NSMutableString stringWithCapacity:1];
	for (int i = 0;i < [infos.moves count];i++)
	{
		[testString appendFormat:@"%u",[[infos.moves objectAtIndex:i]intValue]];
	}
	NSRange resultRange = [testString rangeOfString:@"111" 
					  options:NSCaseInsensitiveSearch //NSCaseInsensitiveSearch
						range:NSMakeRange(0,testString.length)];
	if(NSNotFound != resultRange.location)
	{
		return infos.cost;
	}
	else
	{
		return 10000;
	}

//	var pos:int=infos.moves.join("").indexOf("111");
//	return pos>-1 ? infos.cost : 10000;
}
		
/**
* H - Special case
*/
-(uint) hMatch:(gestureInfo*)infos
{
	NSMutableString *testString = [NSMutableString stringWithCapacity:1];
	for (int i = 0;i < [infos.moves count];i++)
	{
		[testString appendFormat:@"%u",[[infos.moves objectAtIndex:i]intValue]];
	}
	NSRange resultRange = [testString rangeOfString:@"222" 
					  options:NSCaseInsensitiveSearch //NSCaseInsensitiveSearch
						range:NSMakeRange(0,testString.length)];
	if(NSNotFound != resultRange.location)
	{
		return infos.cost;
	}
	else
	{
		return 10000;
	}

//	var pos:int=infos.moves.join("").indexOf("111");
//	return pos>-1 ? infos.cost : 10000;
}

/**
* M - Special case
*/
-(uint) mMatch:(gestureInfo*)infos
{
	NSMutableString *testString = [NSMutableString stringWithCapacity:1];
	for (int i = 0;i < [infos.moves count];i++)
	{
		[testString appendFormat:@"%u",[[infos.moves objectAtIndex:i]intValue]];
	}
	NSRange resultRange = [testString rangeOfString:@"222" 
					  options:NSCaseInsensitiveSearch //NSCaseInsensitiveSearch
						range:NSMakeRange(0,testString.length)];
	if(NSNotFound != resultRange.location)
	{
		return infos.cost;
	}
	else
	{
		return 10000;
	}

//	var pos:int=infos.moves.join("").indexOf("111");
//	return pos>-1 ? infos.cost : 10000;
}




@end





//@implementation hCVerticalToolBar

//@synthesize columns;

//-(void)layoutSubviews {
//	CGSize btnSize = self.bounds.size;
//	btnSize.height = round(btnSize.height * columns / ([self.subviews count]));
//	if (columns > 1)
//		btnSize.width = round(btnSize.width / columns);
//	CGFloat y = 0;
//	CGFloat x = 0;
//	NSUInteger i = 0;
//	for (UIButton* btn in self.subviews) {
//		btn.frame = CGRectMake(x, y, btnSize.width, btnSize.height);
//		++ i;
//		if (i < columns) {
//			x += btnSize.width;
//		} else {
//			x = 0;
//			i = 0;
//			y += btnSize.height;
//		}
//	}
//}
//-(UIButton*)addButtonWithImage:(UIImage*)img target:(id)target action:(SEL)action {
//	UIButton* newBtn = [UIButton buttonWithType:UIButtonTypeCustom];
//	newBtn.showsTouchWhenHighlighted = YES;
//	[newBtn setImage:img forState:UIControlStateNormal];
//	[newBtn addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
//	[self addSubview:newBtn];
//	[self layoutIfNeeded];
//	return newBtn;
//}

//-(id)initWithFrame:(CGRect)frm {
//	if ((self = [super initWithFrame:frm])) {
//		columns = 1;
//	}
//	return self;
//}
//@end


@implementation hGestureLayout


@synthesize gestureView;
-(id)initWithFrame:(CGRect)frm {
	if ((self = (hGestureLayout*)[super initWithFrame:frm])) {
		NSBundle* thisBundle = [KeyboardBundle activeBundle].bundle;
		keyboardAppearance = UIKeyboardAppearanceDefault;
		
		controller = [[hGestureLayoutController alloc] init];
		controller.view = self;
		controller.bundle = thisBundle;
		
		calloutShower = [[UICalloutViewShower alloc] initWithView:self];
		
		backgroundView = [[UIImageView alloc] initWithFrame:frm];
		[self addSubview:backgroundView];
		[backgroundView release];
		
		spBtnGroup = [[UIKBButtonsGroup alloc] initWithLandscape:NO appearance:UIKeyboardAppearanceDefault];
		[spBtnGroup addSubviewWithClass:[UIKBInternationalButton class]];
		[spBtnGroup addSubviewWithClass:[UIKBSpaceBarButton class]];
		[spBtnGroup addSubviewWithClass:[UIKBReturnKeyButton class]];
		[spBtnGroup addSubviewWithClass:[UIKBDeleteKeyButton class]];
//		switchClipboardButton = [[UIKBSpecialKeyButton alloc] init];
//		switchClipboardButton.showsTouchWhenHighlighted = YES;
//		[switchClipboardButton addTarget:controller
//								  action:@selector(switchClipboard:)
//							forControlEvents:UIControlEventTouchUpInside];
//		[switchClipboardButton setImage:[UIImage imageWithContentsOfFile:[thisBundle pathForResource:@"toTemplate" ofType:@"png"]]
//								   forState:UIControlStateNormal];
//		[calloutShower registerButton:switchClipboardButton
//					withCalloutString:[thisBundle localizedStringForKey:@"Switch to Templates" value:nil table:nil]];
//		[UIKBSound registerButton:switchClipboardButton];
//		[spBtnGroup addSubview:switchClipboardButton];
//		[switchClipboardButton release];
		[self addSubview:spBtnGroup];
		[spBtnGroup release];
		
//		toolbar = [[hCVerticalToolBar alloc] initWithFrame:CGRectZero];
//		
//		copyBtn = [toolbar addButtonWithImage:[UIImage imageWithContentsOfFile:[thisBundle pathForResource:@"copy" ofType:@"png"]]
//									   target:controller
//									   action:@selector(copyText)];
//		[calloutShower registerButton:copyBtn withCalloutString:[thisBundle localizedStringForKey:@"Copy" value:nil table:nil]];
//		[UIKBSound registerButton:copyBtn];
//		
//		markSelBtn = [toolbar addButtonWithImage:[UIImage imageWithContentsOfFile:[thisBundle pathForResource:@"selectStart" ofType:@"png"]]
//										  target:controller
//										  action:@selector(markSelection:)];
//		[calloutShower registerButton:markSelBtn withCalloutString:[thisBundle localizedStringForKey:@"Select from here..." value:nil table:nil]];
//		[UIKBSound registerButton:markSelBtn];
//		
//		
//		UIButton* btn = [toolbar addButtonWithImage:_UIImageWithName(@"UIButtonBarPreviousSlide.png")
//											 target:controller
//											 action:@selector(moveToBeginning)];
//		[calloutShower registerButton:btn withCalloutString:[thisBundle localizedStringForKey:@"Move to beginning" value:nil table:nil]];
//		[UIKBSound registerButton:btn];
//		
//		btn = [toolbar addButtonWithImage:_UIImageWithName(@"UIButtonBarNextSlide.png")
//								   target:controller
//								   action:@selector(moveToEnd)];
//		[calloutShower registerButton:btn withCalloutString:[thisBundle localizedStringForKey:@"Move to end" value:nil table:nil]];
//		[UIKBSound registerButton:btn];
//		
//		[self addSubview:toolbar];
//		[toolbar release];
		
		

		gestureView = [[GestureView alloc] initWithFrame:CGRectZero];
		[gestureView setTarget:controller action:@selector(paste:)];
		//[gestureView setPlaceholderText:[thisBundle localizedStringForKey:@"Clipboard is empty" value:nil table:nil]];
		[self addSubview:gestureView];
//		[gestureView release];
		
		[self layoutIfNeeded];
		

	}
	return self;
}

-(void)layoutSubviews {
	int ori = [[UIKeyboardImpl sharedInstance] orientation];
	BOOL landsc = (ori == 90 || ori == -90);
	if (!landsc) {
		gestureView.frame = CGRectMake(8, 8, 320-36-16, 172-16);
	} else {
		gestureView.frame = CGRectMake(8, 8, 480-72-16, 119-16);
	}
	[toolbar setNeedsLayout];
	
	spBtnGroup.landscape = landsc;
	backgroundView.image = UIKBGetImage(UIKBImageBackground, keyboardAppearance, landsc);
	backgroundView.frame = spBtnGroup.frame;
	
	CGRect bdyRect = self.bounds;
	bdyRect.origin.y = -bdyRect.size.height;
	bdyRect.size.height += bdyRect.size.height;
	calloutShower.boundaryRect = bdyRect;
	
	[gestureView layoutSubviews];
	
//	switchClipboardButton.frame = [UIKBPlaneChooserButton defaultFrameWithLandscape:landsc];
}

-(void)showKeyboardType:(UIKeyboardType)type withAppearance:(UIKeyboardAppearance)appr {
	UIKeyboardImpl* impl = [UIKeyboardImpl sharedInstance];
	int ori = [impl orientation];
	BOOL landsc = (ori == 90 || ori == -90);
	keyboardAppearance = appr;
	
	backgroundView.image = UIKBGetImage(UIKBImageBackground, appr, landsc);
	spBtnGroup.keyboardAppearance = appr;
	backgroundView.frame = spBtnGroup.frame;
	
	//BOOL disableCopyKeys = ![impl textInputTraits].secureTextEntry;
//	copyBtn.enabled = disableCopyKeys;
//	markSelBtn.enabled = disableCopyKeys;
	
	[self setNeedsLayout];
}

-(void)updateReturnKey {
	UIKBReturnKeyButton* retKey = (UIKBReturnKeyButton*)[spBtnGroup firstSubviewWithClass:[UIKBReturnKeyButton class]];
	if (retKey != nil) {
		UIKeyboardImpl* impl = [UIKeyboardImpl sharedInstance];
		retKey.returnKeyType = impl.returnKeyType;
		retKey.enabled = impl.returnKeyEnabled;
	}
}


-(void)dealloc {

	
	[calloutShower release];
	[controller release];
	[super dealloc];
}

@end



@implementation gestureInfo

@synthesize datas,moves,matchHandler,cost,lastPoint,rect;

-(gestureInfo*)initWithGestureData:(NSString*)in_datas moves:(NSMutableArray*)in_moves match:(uint)in_matchHandler
{
	datas = [[NSString alloc]initWithString:in_datas];
	moves = [[NSArray alloc]initWithArray:in_moves];
	matchHandler = in_matchHandler;
	
	return self;	
}

-(gestureInfo*)initWithGestureData2:(NSMutableArray*)in_moves 
						 lastPoint:(CGPoint)in_lastPoint rect:(CGRect)in_rect
{
	//datas = [[NSString alloc]initWithString:in_datas];
	moves = [[NSArray alloc]initWithArray:in_moves];

	//matchHandler = in_matchHandler;
	lastPoint.x = in_lastPoint.x;
	lastPoint.y = in_lastPoint.y;
	rect.origin.x = in_rect.origin.x;
	rect.origin.y = in_rect.origin.y;
	rect.size.width = in_rect.size.width - in_rect.origin.x; //inの方は上下左右ポイントなので幅と高さにする
	rect.size.height = in_rect.size.height - in_rect.origin.y;
	return self;	
}


- (void)dealloc {
    [datas release];
    [moves release];
    [super dealloc];
}
@end
