//
//  Bluetooth.m
//  ZeroRec/Komadori
//
//  Created by AIG-Soft on 11/05/06.
//  Copyright 2011 AIG-Soft. All rights reserved.
//
// bluetoothの送受信はサブスレッドで来るので、その中で画面更新系は使えないので注意

#import "Bluetooth.h"

#define	MAX_BUFF	5

// 15文字以内、英小文字、数字、-のみ利用可能
#define ServiceType	@"komadori-p2p"

@interface Bluetooth()
<
MCSessionDelegate,
MCBrowserViewControllerDelegate
>
{
	// 制御
	MCAdvertiserAssistant	*advertiserAssistant;
	MCSession	*session;
	
	// 受信制御
	NSUInteger	rp;
	NSUInteger	wp;
	unichar		buffer[MAX_BUFF];
}

// 接続先ID
@property (nonatomic) NSMutableArray *connectedPeerIDs;	// MCPeerIDの配列

@end

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

@implementation Bluetooth

#pragma mark 初期化

-(id)init
{
	self=[super init];
	if (self!=nil) {
		_fconnect=BT_CONNECT_NONE;
		_fremote =BT_REMOTE_NO;
		self.connectedPeerIDs=[[NSMutableArray alloc]init];
	}
	return (self);
}

// 接続確立
// target+selectorは接続時完了時呼び出し処理
// fhost YES=ホスト側
-(void)open:(NSString *)dispName
{
	printSelfs(@"dispName=%@",dispName);
	//
	// MCPeerID の生成
	MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:dispName];
	
	// MCSession の生成
	session = [[MCSession alloc] initWithPeer:peerID];
	session.delegate = self;
	
	// MCAdvertiserAssistant の生成とアドバタイズの開始 = 自身を周囲に知らせる
	advertiserAssistant = [[MCAdvertiserAssistant alloc] initWithServiceType:ServiceType discoveryInfo:nil session:session];
	[advertiserAssistant start];
	
	if (_fremote==BT_REMOTE_YES) {
		// ブラウザ表示 = 接続先の選択
		MCBrowserViewController *mvc = [[MCBrowserViewController alloc] initWithServiceType:ServiceType session:session];
		mvc.delegate = self;
//		mvc.title=@"Blutooth接続先選択";	// 設定しても入らない
		[self.delegate bluetoothSelectShow:mvc];
	} else {
		// 受信モード
		_fconnect=BT_CONNECT_START;
		// 受信バッファークリア
		[self clearReceiveBuff];
	}
	printSelfs(@"終了%@",@"");
}

// session終了処理(内部関数)
-(void)endOfSession
{
	[advertiserAssistant stop];
	[session disconnect];
	session=nil;
	advertiserAssistant=nil;
	_fconnect=BT_CONNECT_NONE;
	_fremote =BT_REMOTE_NO;
}

// 接続終了
-(void)close
{
	printSelfs(@"fconnect=%d",_fconnect);
	if (_fconnect>=BT_CONNECT_SESSION) {
		nslog(@"close実処理");
		// 後は登録関数へ処理を渡す(呼び出し側)
		_fconnect=BT_CONNECT_CLOSE;
		[self.delegate bluetoothDidConnected:self];
		[self endOfSession];
	}
	_fconnect=BT_CONNECT_NONE;
}

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

#pragma mark - MCBrowserViewControllerDelegate

// 接続先候補表示時に呼び出される
// 多分NOにすると候補に上がらない
- (BOOL)browserViewController:(MCBrowserViewController *)browserViewController
	  shouldPresentNearbyPeer:(MCPeerID *)peerID
			withDiscoveryInfo:(NSDictionary *)info
{
	printSelfs(@"peerID=%@", peerID);
	nslog(@"info=%@",info);
	return YES;
}

/// 選択終了
- (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController
{
	printSelf();
	[browserViewController dismissViewControllerAnimated:YES completion:nil];
}

/// 選択キャンセル
- (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController
{
	printSelf();
	[self endOfSession];
	[browserViewController dismissViewControllerAnimated:YES completion:nil];
	_fremote=BT_REMOTE_CANCEL;
	nslog(@"delegate=%@",self.delegate);
	if ([self.delegate respondsToSelector:@selector(bluetoothCancelConnect:)]) {
		[self.delegate bluetoothCancelConnect:self];
	}
}


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

#pragma mark MCSessionDelegate

/*
 リスト表示時に：shouldPresentNearbyPeer〜多分接続先を見つけた時(双方で発生している)
 リストから選択〜何もなし
 承認確認〜承認でdidChangeStateが来てMCSessionStateConnectingからMCSessionStateConnectedへ；接続済み
 		辞退では何も帰ってこない
 「完了」すると接続が切れてしまう
 */

// 通信状態変更(必須)
// 複数の接続先があった場合、それぞれに対して呼び出されるので注意。
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state
{
	printSelfs(@"peerID=%@", peerID);
	BOOL needToNotify = NO;
	
	// 接続先情報を配列に登録する
	// fconnectは複数のうち最初の1つに対して設定される
	switch (state) {
		case MCSessionStateConnecting:
			nslog(@"ここへ接続中");
			if (_fconnect==BT_CONNECT_NONE) {
				_fconnect=BT_CONNECT_SESSION;
			}
			break;
		case MCSessionStateConnected:
			nslog(@"ここへは接続完了");
			if (![self.connectedPeerIDs containsObject:peerID]) {
				[self.connectedPeerIDs addObject:peerID];
				needToNotify = YES;
			}
			nslog(@"connectedPeerIDs=%@",self.connectedPeerIDs);
			_fconnect=BT_CONNECT_OK;
			// 受信バッファークリア
			[self clearReceiveBuff];
			break;
		case MCSessionStateNotConnected:
			// 「辞退」では来ない
			nslog(@"ここは接続できなかった"); // いきなり来ることもある
			if ([self.connectedPeerIDs containsObject:peerID]) {
				[self.connectedPeerIDs removeObject:peerID];
				needToNotify = YES;
			}
			if (self.connectedPeerIDs==0) _fconnect=BT_CONNECT_NONE; // 全部解除されてしまった時
			nslog(@"connectedPeerIDs=%@",self.connectedPeerIDs);
			break;
	}
	if (needToNotify) {
		nslog(@"self.delegate=%@",self.delegate);
		if ([self.delegate respondsToSelector:@selector(bluetoothDidConnected:)]) {
			nslog(@"connectedPeerIDs=%@",self.connectedPeerIDs);
//			[self.delegate bluetoothDidConnected:self];
			[self performSelectorOnMainThread:@selector(bluetoothDidConnectedInMainThread:) withObject:nil waitUntilDone:NO];
		}
	}
}

-(void)bluetoothDidConnectedInMainThread:(id)sender
{
	[self.delegate bluetoothDidConnected:self];
}


// データ受信完了(必須)
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
{
	printSelfs(@"peerID=%@",peerID);
	// データは文字列だが、最初の1文字の文字コードをコマンドとする
	unichar c;
	[data getBytes:&c length:sizeof(c)];
	nslog(@"データ受信from:%@=%c($%04x)",peerID,c,c);
	buffer[wp]=c;
	if (++wp>=MAX_BUFF) wp=0;
	if (wp==rp) { // フル状態の時は、最古コマンドを消す
		if (++rp>=MAX_BUFF) rp=0;
	}
	// 後は登録関数へ処理を渡す
//	[self.delegate bluetoothDidRecieve:data peer:peerID];
	[self performSelectorOnMainThread:@selector(bluetoothDidRecieveInMainThread:) withObject:@[data,peerID] waitUntilDone:nil];
}

-(void)bluetoothDidRecieveInMainThread:(NSArray *)arg
{
	NSData *data=(NSData *)arg[0];
	MCPeerID *peerID=(MCPeerID *)arg[1];
	[self.delegate bluetoothDidRecieve:data peer:peerID];
}

// 受信開始(必須)
- (void)session:(MCSession *)session
didStartReceivingResourceWithName:(NSString *)resourceName
	   fromPeer:(MCPeerID *)peerID
   withProgress:(NSProgress *)progress
{
	printSelfs(@"peerID=%@",peerID);
	nslog(@"resourceName=%@",resourceName);
	nslog(@"progress=%@",progress);
}

// 受信完了(必須)
- (void)session:(MCSession *)session
didFinishReceivingResourceWithName:(NSString *)resourceName
	   fromPeer:(MCPeerID *)peerID
		  atURL:(NSURL *)localURL
	  withError:(NSError *)error
{
	printSelfs(@"peerID=%@",peerID);
	nslog(@"resourceName=%@",resourceName);
	nslog(@"localURL=%@",localURL);
	nslog(@"error=%@",error);
}

//(必須)
- (void)session:(MCSession *)session
didReceiveStream:(NSInputStream *)stream
	   withName:(NSString *)streamName
	   fromPeer:(MCPeerID *)peerID
{
	printSelfs(@"peerID=%@",peerID);
	nslog(@"stream=%@",stream);
	nslog(@"streamName=%@",streamName);
}

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

// メッセージ送信(文字型)
- (void)send:(unichar)command
{
	printSelf();
	nslog(@"メッセージ送信:%c($%04x)",command,command);
	if (session==nil) {
		nslog(@"接続できてません");
		return;
	}
	NSData *data=[NSData dataWithBytes:&command length:sizeof(command)];
	nslog(@"data:%@",data);
	nslog(@"connectPeerIDs=%@",self.connectedPeerIDs);
	NSError *error=nil;
	[session sendData:data
			  toPeers:self.connectedPeerIDs
			 withMode:MCSessionSendDataReliable
				error:&error];
	nslog(@"送信完了:%@",error);
}

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

-(void)clearReceiveBuff
// 受信データキャッシュクリア
{
	printSelf();
	rp=wp=0;
}

-(unichar)readReceiveBuff
// 受信データキャッシュ読み出し
{
	unichar c=BT_COMMAND_NONE;
	if (rp!=wp) {
		c=buffer[rp];
		if (++rp>=MAX_BUFF) rp=0;
	} // rp==wpの時はデータなし
	return (c);
}

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

// 念のため
-(void)dealloc
{
	[self close];
}

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

@end
