package org.opengion.plugin.cloud;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.opengion.fukurou.model.CloudFileOperation;
import org.opengion.fukurou.model.FileOperation;
import org.opengion.fukurou.model.FileOperationInfo;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;

import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlobDirectory;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.microsoft.azure.storage.blob.ListBlobItem;

/**
 * FileOperation_AZURE.javaは、Azureのストレージに対して、
 * ファイル操作を行うクラスです。
 * 
 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
 *
 * @version 5
 * @author  oota
 * @since   JDK7.0
 */
public class FileOperation_AZURE extends CloudFileOperation {
	/** クラス変数 */
	private final CloudBlobContainer azureContainer;
	private final String PLUGIN = "azure";
	
	/**
	 * コンストラクター
	 * 
	 * 初期化処理です。
	 * Azureの認証処理を行います。
	 * 
	 * @param bucket バケット
	 * @param inPath パス
	 */
	public FileOperation_AZURE(final String bucket, final String inPath) {
		super( StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ), inPath);
		setPlugin(PLUGIN);

		String storageConnectionString = HybsSystem.sys("CLOUD_STORAGE_AZURE_KEY");

		if (StringUtil.isNull(storageConnectionString)) {
			String errMsg = "Azure用認証キー(CLOUD_STORAGE_AZURE_KEY)がシステムリソースに登録されていません。";
			throw new HybsSystemException(errMsg);
		}

		try {
			CloudStorageAccount account = CloudStorageAccount.parse(storageConnectionString);
			CloudBlobClient serviceClient = account.createCloudBlobClient();
			azureContainer = serviceClient.getContainerReference(conBucket);
			// コンテナが存在しない場合は作成する
			azureContainer.createIfNotExists();
		} catch (Exception e) {
			StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
			errMsg.append("コンテナの作成に失敗しました。container：").append(conBucket);
			errMsg.append(" システムエラー情報：").append(e.getMessage());
			throw new HybsSystemException(errMsg.toString());
		}
	}

	/**
	 * 書き込み処理
	 * 
	 * InputStreamのデータを書き込みます。
	 * 
	 * @param is 書き込みデータのInputStream
	 * @throws IOException ファイル関連エラー情報
	 */
	@Override
	public void write(final InputStream is) throws IOException {
		try {
			CloudBlockBlob blob = azureContainer.getBlockBlobReference(conPath);
			byte[] bytes = toByteArray(is);
			ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

			blob.upload(bais, bytes.length);
		} catch (Exception e) {
			StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
			errMsg.append("Azureストレージに書き込みが失敗しました。path：").append(conPath);
			errMsg.append(" システムエラー情報：").append(e.getMessage());
			throw new IOException(errMsg.toString());
		}
	}

	/**
	 * 読み込み処理
	 * 
	 * データを読み込み、InputStreamとして、返します。
	 * 
	 * @return 読み込みデータのInputStream
	 * @throws FileNotFoundException ファイル非存在エラー情報
	 */
	@Override
	public InputStream read() throws FileNotFoundException {
		CloudBlockBlob blob;
		InputStream is;
		try {
			blob = azureContainer.getBlockBlobReference(conPath);
			is = blob.openInputStream();
		} catch (Exception e) {
			StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
			errMsg.append("Azureストレージから読み込みが失敗しました。path：").append(conPath);
			errMsg.append(" システムエラー情報：").append(e.getMessage());
			throw new FileNotFoundException(errMsg.toString());
		}
		return is;
	}

	/**
	 * 削除処理
	 * 
	 * ファイルを削除します。
	 * 
	 * @return 成否フラグ
	 */
	@Override
	public boolean delete() {
		boolean flgRtn = false;

		try {
			azureContainer.getBlockBlobReference(conPath).delete();
			flgRtn = true;
		} catch (Exception e) {
			// エラーはスルーして、falseを返す
		}

		return flgRtn;
	}

	/**
	 * コピー処理
	 * 
	 * ファイルを指定先に、コピーします。
	 * 
	 * @param afPath コピー先
	 * @return 成否フラグ
	 */
	@Override
	public boolean copy(final String afPath) {
		boolean flgRtn = false;

		try {
			CloudBlockBlob copyTrg = azureContainer.getBlockBlobReference(conPath);
			CloudBlockBlob copySaki = azureContainer.getBlockBlobReference(afPath);

			copySaki.startCopy(copyTrg);
			flgRtn = true;
		} catch (Exception e) {
			// エラーはスルーして、falseを返す
		}

		return flgRtn;
	}

	/**
	 * ファイルサイズ取得
	 * 
	 * ファイルサイズを返します。
	 * 
	 * @return ファイルサイズ
	 */
	@Override
	public long length() {
		long rtn = 0;

		try {
			CloudBlob blob = azureContainer.getBlockBlobReference(conPath);
			rtn = blob.getProperties().getLength();
		} catch (Exception e) {
			// スルーして、0を返す
		}
		return rtn;
	}

	/**
	 * 最終更新時刻取得
	 * 
	 * 最終更新時刻を取得します。
	 * 
	 * @return 最終更新時刻
	 */
	@Override
	public long lastModified() {
		long rtn = 0;

		try {
			CloudBlob blob = azureContainer.getBlockBlobReference(conPath);
			rtn = blob.getProperties().getLastModified().getTime();
		} catch (Exception e) {
			// スルーして、0を返す
		}

		return rtn;
	}

	/**
	 * ファイル判定
	 * 
	 * ファイルの場合は、trueを返します。
	 * 
	 * @return ファイルフラグ
	 */
	@Override
	public boolean isFile() {
		boolean blnRtn = false;

		try {
			CloudBlockBlob blob = azureContainer.getBlockBlobReference(conPath);

			if (blob.exists()) {
				blnRtn = true;
			}
		} catch (Exception e) {
			// ここのエラーはスルーして、falseを返す			
		}

		return blnRtn;
	}

	/**
	 * ディレクトリ判定
	 * 
	 * ディレクトリの場合は、trueを返します。
	 * 
	 * @return ディレクトリフラグ
	 */
	@Override
	public boolean isDirectory() {
		boolean blnRtn = false;

		// 後尾に「/」をつけないこと
		for (ListBlobItem item : azureContainer.listBlobs(rTrim(conPath, '/'))) {
			if (item instanceof CloudBlobDirectory) {
				blnRtn = true;
				break;
			}
		}

		return blnRtn;
	}

	/**
	 * ファイル一覧取得
	 * 
	 * パスのファイルとディレクトリ一覧を取得します。
	 * 
	 * @param filter フィルター情報
	 * @return ファイルとティレクトリ一覧
	 */
	@Override
	public File[] listFiles(final FileFilter filter) {
		if (!exists()) {
			return new FileOperationInfo[0];
		}

		String search = conPath;
		if (isDirectory()) {
			search = setDirTail(conPath);
		}

		List<File> rtnList = new ArrayList<File>();

		for (ListBlobItem item : azureContainer.listBlobs(search)) {
			if (item instanceof CloudBlob) {
				// ファイルの情報を設定
				CloudBlob blob = (CloudBlob) item;
				FileOperationInfo fi = new FileOperationInfo(PLUGIN, conBucket, blob.getName());
				fi.setLastModifiedValue(blob.getProperties().getLastModified().getTime());
				fi.setSize(blob.getProperties().getLength());
				fi.setFile(true);
				rtnList.add(fi);
			} else if (item instanceof CloudBlobDirectory) {
				// ディレクトリの情報を設定
				CloudBlobDirectory directory = (CloudBlobDirectory) item;
				final String key = rTrim(directory.getPrefix(), '/');
				FileOperationInfo fi = new FileOperationInfo(PLUGIN, conBucket, key);
				fi.setDirectory(true);
				rtnList.add(fi);
			}
		}

		File[] filterList = filter(rtnList, filter);

		return filterList;
	}

	/**
	 * 親ディレクトリ取得
	 * 
	 * 親のディレクトリを返します。
	 * 
	 * @return 親のディレクトリ
	 */
	@Override
	public FileOperation getParentFile() {
		return new FileOperation_AZURE(conBucket,getParent());
	}

		/** 以下はローカル環境でのテスト用メソッドです。 */
//		/**
//		 * ローカルでのテスト用コンストラクタ
//		 * 
//		 * 要：super.bucketの値は固定値に変更してください。
//		 * 
//		 * @param inPath
//		 */
//		public FileOperation_AZURE(String bucket, String inPath, boolean test) {
//			// super( StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ), inPath);
//			super( StringUtil.nval( bucket, "opengiontestbucket" ), inPath);
//			conBucket = bucket;
//	
//			// ローカル環境で実行する場合の、proxy設定
//			System.setProperty("https.proxyHost","mtc-px14");
//			System.setProperty("https.proxyPort","8081");
//			
//			String storageConnectionString = "[接続文字列]";
//	
//			if (StringUtil.isNull(storageConnectionString)) {
//				String errMsg = "Azure用認証キー(CLOUD_STORAGE_AZURE_KEY)がシステムリソースに登録されていません。";
//				throw new HybsSystemException(errMsg);
//			}
//	
//			try {
//				CloudStorageAccount account = CloudStorageAccount.parse(storageConnectionString);
//				CloudBlobClient serviceClient = account.createCloudBlobClient();
//				azureContainer = serviceClient.getContainerReference(bucket);
//				// コンテナが存在しない場合は作成する
//				azureContainer.createIfNotExists();
//			} catch (Exception e) {
//				StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
//				errMsg.append("コンテナの作成に失敗しました。container：").append(bucket);
//				errMsg.append(" システムエラー情報：").append(e.getMessage());
//				throw new HybsSystemException(errMsg.toString());
//			}
//		}
//		
//		/** テスト用メソッド */
//		public static void main(String[] args) {
//	//		writeTest();
//	//		listTest();
//	//		methodTest();
//		}
//	
//		public static void writeTest() {
//			FileOperation file = new FileOperation_AZURE("", "sample/test.txt", true);
//	
//			try (InputStream is = new ByteArrayInputStream("sample".getBytes())) {
//				file.write(is);
//			} catch (Exception e) {
//				System.out.println(e.getMessage());
//			}
//		}
//	
//		public static void listTest() {
//			FileOperation file = new FileOperation_AZURE("", "sample", true);
//	
//			FileOperation[] list = file.listFiles();
//			System.out.println(list.length);
//			for (FileOperation f : list) {
//				System.out.println(f.getPath());
//			}
//		}
//	
//		public static void methodTest() {
//			FileOperation file = new FileOperation_AZURE("", "test", true);
//			boolean rtn = false;
//			rtn = file.isFile();
//	
//			System.out.println(rtn);
//		}
}
