//******************************************************************************
//
// MIDITrail / MTURLAccessUtil
//
// URLアクセスユーティリティクラス
//
// Copyright (C) 2021 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "YNBaseLib.h"
#import "MTURLAccessUtil.h"


//******************************************************************************
// プライベートメソッド定義
//******************************************************************************
@interface MTURLAccessUtil ()

//指定ディレクトリでのファイル名生成
- (NSString*)makeFileNameWithBaseFileName:(NSString*)pBaseFileName destDirPath:(NSString*)pDestDirPath;

//指定URLのファイルを指定パスに取り込む
- (int)importFileFromURL:(NSURL*)pURL toFilePath:(NSString*)pToFilePath;

@end


@implementation MTURLAccessUtil

//******************************************************************************
// 初期化
//******************************************************************************
- (MTURLAccessUtil*)init
{
	[super init];
	
	m_pImportedFilePath = nil;
	
	return self;
}

//******************************************************************************
// 破棄
//******************************************************************************
- (void)dealloc
{
	[m_pImportedFilePath release];
	
	[super dealloc];
	
	return;
}

//******************************************************************************
// 指定URLのファイルをDocumentsディレクトリに取り込む
//******************************************************************************
- (int)importFileFromURL:(NSURL*)pURL destDirPath:(NSString*)pDestDirPath
{
	int result = 0;
	NSArray* pPathList = nil;
	NSString* pDocDirPath = nil;
	NSString* pInboxDirPath = nil;
	NSString* pDestFilePath = nil;
	NSString* pBaseFileName = nil;
	NSString* pFileName = nil;
	NSString* pFilePath = nil;
	NSError* pError = nil;
	BOOL apiresult = NO;
	
	//URL指定なしなら何もしない
	if (pURL == nil) goto EXIT;
	
	//指定URLのファイル名
	pBaseFileName = [pURL lastPathComponent];
	
	//MIDITrailのDocumentsディレクトリとInboxディレクトリのパスを作成
	pPathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	pDocDirPath = [pPathList objectAtIndex:0];
	pInboxDirPath = [pDocDirPath stringByAppendingPathComponent:@"Inbox"];
	
	//MIDITrailのDocumentsディレクトリ内のファイルを指定された場合
	//  /private/var/mobile/Containers/Data/Application/(ID)/Documents/xxxx.mid
	//  /private/var/mobile/Containers/Data/Application/(ID)/Documents/Inbox/xxxx.mid
	if ([pURL.path rangeOfString:pDocDirPath options:NSCaseInsensitiveSearch].location != NSNotFound) {
		//Documents/Inboxディレクトリ内のファイルを指定された場合
		if ([pURL.path rangeOfString:pInboxDirPath options:NSCaseInsensitiveSearch].location != NSNotFound) {
			//移動先ファイルパスを生成
			pFileName = [self makeFileNameWithBaseFileName:pBaseFileName destDirPath:pDestDirPath];
			if (pFileName == nil) {
				result = YN_SET_ERR(@"File name error.", 0, 0);
				goto EXIT;
			}
			pDestFilePath = [pDestDirPath stringByAppendingPathComponent:pFileName];
			
			//指定ファイルをDocuments直下に移動
			apiresult = [[NSFileManager defaultManager] moveItemAtPath:pURL.path toPath:pDestFilePath error:&pError];
			if (!apiresult) {
				result = YN_SET_ERR(@"File move error.", pError.code, 0);
				goto EXIT;
			}
			pFilePath = pDestFilePath;
		}
		//Documentsディレクトリ配下のファイルを指定された場合
		else {
			pFileName = pBaseFileName;
			pFilePath = [pURL.path stringByStandardizingPath]; //パス先頭の"/private"を削除
		}
	}
	//MIDITrailのDocumentsディレクトリ外のファイルを指定された場合（以下ファイルパスの例）
	//  /private/var/mobile/Containers/Data/Application/(ID)/Documents/xxxx.mid
	//  /private/var/mobile/Containers/Shared/AppGroup/(ID)/File%20Provider%20Storage/Downloads/xxxx.mid
	//  /private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/Downloads/xxxx.mid
	//UIDocumentPickerViewControllerのインポートモードでファイルを選択した場合
	//  /private/var/mobile/Containers/Data/Application/(ID)/tmp/jp.sourceforge.users.yknk.ios.MIDITrail-Inbox/xxxx.mid
	else {
		//移動先ファイルパスを作成
		pFileName = [self makeFileNameWithBaseFileName:pBaseFileName destDirPath:pDestDirPath];
		if (pFileName == nil) {
			result = YN_SET_ERR(@"File name error.", 0, 0);
			goto EXIT;
		}
		pDestFilePath = [pDestDirPath stringByAppendingPathComponent:pFileName];
		
		//指定URLからDocumentsディレクトリにファイル取り込み
		result = [self importFileFromURL:pURL toFilePath:pDestFilePath];
		if (result != 0) goto EXIT;
		
		pFilePath = pDestFilePath;
	}
	
	[m_pImportedFilePath release];
	m_pImportedFilePath = pFilePath;
	[m_pImportedFilePath retain];
	
EXIT:
	return result;
}

//******************************************************************************
// 指定ディレクトリでのファイル名生成
//******************************************************************************
- (NSString*)makeFileNameWithBaseFileName:(NSString*)pBaseFileName destDirPath:(NSString*)pDestDirPath
{
	NSFileManager* pFileManager = nil;
	NSString* pFileName = nil;
	NSString* pDestFilePath = nil;
	int i = 0;
	
	//移動先のファイルパスを作成
	pFileManager = [NSFileManager defaultManager];
	for (i = 0; i < 1000; i++) {
		//移動先ファイル名
		if (i == 0) {
			//1回目 ex: original.mid
			pFileName = pBaseFileName;
		}
		else {
			//2回目以降 ex: original_1.mid
			pFileName = [NSString stringWithFormat:@"%@_%d.%@",
							[pBaseFileName stringByDeletingPathExtension], i,
							[pBaseFileName pathExtension]];
		}
		//移動先ファイルパス
		pDestFilePath = [pDestDirPath stringByAppendingPathComponent:pFileName];
		
		//移動先ファイルがすでに存在するならやり直す
		if ([pFileManager fileExistsAtPath:pDestFilePath]) {
			pDestFilePath = nil;
		}
		else {
			break;
		}
	}
	//1000回繰り返しても重複しないファイル名が作成できなかった場合はnilのまま返す
	
	return pFileName;
}

//******************************************************************************
// 指定URLのファイルを指定パスに取り込む
//******************************************************************************
- (int)importFileFromURL:(NSURL*)pURL toFilePath:(NSString*)pToFilePath
{
	int result = 0;
	BOOL isStartAccessing = NO;
	NSFileCoordinator* pFileCoordinator = nil;
	NSError* pError = nil;
	__block NSData* pData = nil;
	__block NSError* pDataError = nil;
	__block BOOL dataResult = NO;
	
	//指定URLセキュリティスコープリソースアクセス開始
	isStartAccessing = [pURL startAccessingSecurityScopedResource];
	
	//指定URLのファイルをDocumentsフォルダにコピー
	pFileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
	[pFileCoordinator coordinateReadingItemAtURL:pURL
										 options:NSFileCoordinatorReadingWithoutChanges
										   error:&pError
									  byAccessor:^(NSURL* pNewURL) {
		//指定URLのデータ読み込み
		//サイズが小さいファイルを前提とした同期処理であることに注意
		pData = [NSData dataWithContentsOfURL:pNewURL options:0 error:&pDataError];
		if (pDataError != nil) {
			YN_SET_ERR(@"Data access error.", pDataError.code, 0);
			YN_SHOW_ERR();
			return;
		}
		//Documentsフォルダにファイルを書き込む
		dataResult = [pData writeToFile:pToFilePath options:0 error:&pDataError];
		if (!dataResult) {
			YN_SET_ERR(@"File write error.", pDataError.code, 0);
			YN_SHOW_ERR();
			return;
		}
	}];
	if (pError != nil) {
		result = YN_SET_ERR(@"Data access error.", pError.code, 0);
		goto EXIT;
	}
	
EXIT:;
	if (isStartAccessing) {
		//指定URLセキュリティスコープリソースアクセス終了
		[pURL stopAccessingSecurityScopedResource];
	}
	return result;
}

//******************************************************************************
//最後に取り込まれたファイルのパス
//******************************************************************************
- (NSString*)importedFilePath
{
	return m_pImportedFilePath;
}

@end

