package org.opengion.plugin.cloud;

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.opengion.fukurou.model.CloudFileOperation;
import org.opengion.fukurou.model.FileOperation;
import org.opengion.fukurou.model.FileOperationInfo;
import org.opengion.fukurou.util.Closer;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;

import oracle.cloud.storage.CloudStorage;
import oracle.cloud.storage.CloudStorageConfig;
import oracle.cloud.storage.CloudStorageFactory;
import oracle.cloud.storage.exception.NoSuchContainerException;
import oracle.cloud.storage.model.Key;
import oracle.cloud.storage.model.QueryOption;
import oracle.cloud.storage.model.QueryResult;

/**
 * FileOperation_ORACLE.javaは、Oracleクラウドのストレージに対して、
 * ファイル操作を行うクラスです。
 * 
 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
 *
 * @version 5.0
 * @author oota
 * @since JDK7.0
 */
public class FileOperation_ORACLE  extends CloudFileOperation {
	private static final long serialVersionUID = 5108020190201L;
	/** クラス変数 */
	private final CloudStorage oracleStorage;
	private final String PLUGIN = "oracle";
	
	/**
	 * コンストラクター
	 * 
	 * 初期化処理です。
	 * Oracleクラウドの認証処理を行います。
	 * 
	 * @param bucket バケット
	 * @param inPath パス
	 */
	public FileOperation_ORACLE(final String bucket, final String inPath) {
		super(StringUtil.nval( bucket, HybsSystem.sys("CLOUD_BUCKET") ),inPath);
		setPlugin(PLUGIN);
		
		CloudStorageConfig config = new CloudStorageConfig();
		
		// リソースパラメータ設定
		// サービス名
		final String serviceName = HybsSystem.sys("CLOUD_STORAGE_ORACLE_SERVICE_NAME");
		// ユーザ名
		final String userName = HybsSystem.sys("CLOUD_STORAGE_ORACLE_USERNAME");
		// パスワード
		final String password = HybsSystem.sys("CLOUD_STORAGE_ORACLE_PASSWORD");
		// サービスURL
		final String serviceUrl = HybsSystem.sys("CLOUD_STORAGE_ORACLE_SERVICEURL");
		
		initCheck(serviceName, userName, password, serviceUrl);
		
		try {
			config.setServiceName(serviceName)
				.setUsername(userName)
				.setPassword(password.toCharArray())
				.setServiceUrl(serviceUrl);
			
			oracleStorage = CloudStorageFactory.getStorage(config);
			
			// コンテナの存在チェック
			try {
				oracleStorage.describeContainer(conBucket);
			}catch(NoSuchContainerException nce) {
				// コンテナが存在しない場合は、作成する
				oracleStorage.createContainer(conBucket);
			}
		}catch(MalformedURLException me) {
			throw new HybsSystemException(me.getMessage());
		}
	}
	
	/**
	 * 初期ﾁｪｯｸ
	 * 
	 * パラメータの値チェックを行い、
	 * 未設定の場合はエラーを返します。
	 * 
	 * @param serviceName サービス名
	 * @param userName ユーザ名
	 * @param password パスワード
	 * @param serviceUrl サービスurl
	 */
	private void initCheck(String serviceName, String userName, String password, String serviceUrl){
		// ｼｽﾃﾑﾘｿｰｽに認証情報が登録されていない場合は、エラー
		StringBuilder errString = new StringBuilder();
		if(StringUtils.isEmpty(serviceName)){
			errString.append("CLOUD_STORAGE_ORACLE_SERVICE_NAME");
		}
		if(StringUtils.isEmpty(userName)){
			errString.append(",CLOUD_STORAGE_ORACLE_USERNAME");
		}
		if(StringUtils.isEmpty(password)){
			errString.append(",CLOUD_STORAGE_ORACLE_PASSWORD");
		}
		if(StringUtils.isEmpty(serviceUrl)){
			errString.append(",CLOUD_STORAGE_ORACLE_SERVICEURL");
		}

		if(errString.length() > 0){
			throw new HybsSystemException("クラウドストレージのキー情報("+errString.toString()+")がシステムリソースに登録されていません。");
		}

	}
	
	/**
	 * 書き込み処理
	 * 
	 * InputStreamのデータを書き込みます。
	 * 
	 * @param is 書き込みデータのInputStream
	 * @throws IOException ファイル関連エラージョウホウ
	 */
	@Override
	public void write(InputStream is) throws IOException {
		try {
			oracleStorage.storeObject(conBucket, conPath, "application/octet-stream", is);
		}catch(Exception e) {
			StringBuilder errMsg = new StringBuilder(HybsSystem.BUFFER_MIDDLE);
			errMsg.append("Oracleストレージに書き込みが失敗しました。path：").append(conPath);
			errMsg.append(" システムエラー情報：").append(e.getMessage());
			throw new IOException(errMsg.toString());
		}
	}

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

	/**
	 * 削除処理
	 * 
	 * ファイルを削除します。
	 * 
	 * @return 成否フラグ
	 */
	@Override
	public boolean delete() {
		boolean flgRtn = false;
		
		try {
			oracleStorage.deleteObject(conBucket,  conPath);
			flgRtn = true;
		}catch(Exception e) {
			// エラーはスルーして、falseを返す
		}
		
		return flgRtn;
	}

	/**
	 * コピー処理
	 * 
	 * ファイルを指定先に、コピーします。
	 * 
	 * @param afPath コピー先
	 * @return 成否フラグ
	 */
	@Override
	public boolean copy(String afPath) {
		boolean flgRtn = false;
		InputStream is = null;
		
		try {
			is = read();
			FileOperation_ORACLE afFile = new FileOperation_ORACLE(conBucket,afPath);
			afFile.write(is);
			flgRtn = true;
		}catch(IOException ie) {
			// エラーはスルーして、falseを返す
		}finally {
			Closer.ioClose(is);
		}
		
		return flgRtn;
	}

	/**
	 * ファイルサイズ取得
	 * 
	 * ファイルサイズを返します。
	 * 
	 * @return ファイルサイズ
	 */
	@Override
	public long length() {
		long rtn = 0;
		
		try {
			rtn = oracleStorage.describeObject(conBucket, conPath).getSize();
		}catch(Exception e) {
			// エラーはスルーして、0を返す
		}
		
		return rtn;
	}

	/**
	 * 最終更新時刻の取得
	 * 
	 * 最終更新時刻を取得します。
	 * 
	 * @return 最終更新時刻
	 */
	@Override
	public long lastModified() {
		long rtn = 0;
		
		try {
			rtn = oracleStorage.describeObject(conBucket, conPath).getLastModified().getTime();
		}catch(Exception e) {
			// エラーはスルーして、0を返す
		}
		
		return rtn;
	}

	/**
	 * ファイル判定
	 * 
	 * ファイルの場合は、trueを返します。
	 * 
	 * @return ファイルフラグ
	 */
	@Override
	public boolean isFile() {
		boolean flgRtn = false;
		
		try {
			oracleStorage.describeObject(conBucket, conPath);
			flgRtn = true;
		}catch(Exception e) {
			// ここでのエラーはスルーして、falseを返す
		}
		
		return flgRtn;
	}

	/**
	 * ディレクトリ判定
	 * 
	 * ディレクトリの場合は、trueを返します。
	 * 
	 * @return ディレクトリフラグ
	 */
	@Override
	public boolean isDirectory() {
		boolean blnRtn = false;
		
		if(StringUtil.isNull(conPath)) {
			return true;
		}
		
		Map<QueryOption, String> map = new HashMap<QueryOption, String>();
		map.put(QueryOption.PREFIX,  setDirTail(conPath));
		
		List<Key> list = oracleStorage.listObjects(conBucket, map);
		if(list.size() > 0) {
			blnRtn = true;
		}
		
		return blnRtn;
	}

	/**
	 * ファイル一覧取得
	 * 
	 * パスのファイルとディレクトリ一覧を取得します。
	 * 
	 * @return ファイルとティレクトリ一覧
	 */
	@Override
	public File[] listFiles(FileFilter filter){
		if(!exists()) {
			return new File[0];
		}
		
		String search = conPath;
		if(isDirectory()) {
			search = setDirTail(conPath);
		}
		
		List<File> rtnList = new ArrayList<File>();
		
		// 検索処理
		QueryResult result = oracleStorage.listObjectsByPath(conBucket, "/", search, null);
		
		// ファイル情報の設定
		for(Key trg: result.getKeys()) {
			String key = trg.getKey();
			
			FileOperationInfo fi = new FileOperationInfo(PLUGIN, conBucket, key);
			fi.setLastModifiedValue(trg.getLastModified().getTime());
			fi.setSize(trg.getSize());
			fi.setFile(true);
			rtnList.add(fi);
		}
		
		// サブディレクトリ情報の設定
		for(String path: result.getPaths()) {
			String key = rTrim(path, '/');
			
			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_ORACLE(conBucket,getParent());
	}
	
	/** 以下はローカル環境でのテスト用メソッドです。 */
//		public FileOperation_ORACLE(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");
//		    
//			CloudStorageConfig config = new CloudStorageConfig();
//			// リソースパラメータ設定
//			final String serviceName = "[storage名]";
//			final String userName = "[userId]";
//			final String password = "[password]";
//			final String serviceUrl = "https://aucom-east-1.storage.oraclecloud.com";
//			
//			initCheck(serviceName, userName, password, serviceUrl);
//			
//			try {
//				config.setServiceName(serviceName)
//					.setUsername(userName)
//					.setPassword(password.toCharArray())
//					.setServiceUrl(serviceUrl);
//				
//				oracleStorage = CloudStorageFactory.getStorage(config);
//				
//				// コンテナの存在チェック
//				try {
//					oracleStorage.describeContainer(bucket);
//				}catch(NoSuchContainerException nce) {
//					// コンテナが存在しない場合は、作成する
//					oracleStorage.createContainer(bucket);
//				}
//			}catch(MalformedURLException me) {
//				throw new HybsSystemException(me.getMessage());
//			}
//		}
//		
//		/** テスト用メソッド */
//		public static void main(String[] args) {
//	//		writeTest();
//			listTest();
//	//		methodTest();
//		}
//	
//		public static void writeTest() {
//			FileOperation file = new FileOperation_ORACLE("", "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_ORACLE("", "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_ORACLE("", "test", true);
//			boolean rtn = false;
//			rtn = file.isFile();
//	
//			System.out.println(rtn);
//		}
}
