/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.servlet;

import java.io.File;

import javax.servlet.http.HttpSession;

import org.opengion.fukurou.util.FileUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.io.StorageAPI;
import org.opengion.hayabusa.io.StorageAPIFactory;

/**
 * ファイルをサーバーにアップロードする場合に使用されるファイル管理クラスです。
 * HTML5 ファイルアップロードの複数選択（multiple）対応 に伴い、一つのクラスとして public化します。
 *
 * @og.group その他機能
 * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択（multiple）対応
 * @og.rev 5.9.25.0 (2017/10/06) クラウドストレージ追加対応
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class UploadedFile implements Comparable<UploadedFile> {

	/** バッファの初期容量を通常より多い目に設定します。  {@value}  */
	public static final int BUFFER_MIDDLE = 200;

	/** システム依存の改行記号をセットします。	*/
	public static final String CR = System.getProperty("line.separator");

	// 5.9.25.0 (2017/10/06) MODIFY File型をString型に変更
	//private File filename = null;		// 現時点での置き換え後ファイル名
	private String filename = null;	// 現時点での置き換え後ファイル名

	private final String uniqKey ;		// アップロードされたファイル名(ユニークにしておきます)
	private final String dir;
	private final String name;
	private final String original;
	private final String type;

	/**
	 * アップロードファイルの管理オブジェクトを作成します。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択（multiple）対応
	 *
	 * @param	uniqKey		ユニークキー(初期アップロードファイル名)
	 * @param	dir			ファイルを保管するフォルダ
	 * @param	name		ファイルアップロードされた時のname属性
	 * @param	original	ファイル名(オリジナル)
	 * @param	type	コンテントタイプ
	 */
//	UploadedFile( final String dir, final String name, final String filename, final String original, final String type) {
	UploadedFile( final String uniqKey, final String dir, final String name, final String original, final String type ) {
		this.uniqKey	= uniqKey;		// 5.7.1.1 (2013/12/13) uniqKey を確定させる。
		this.dir		= dir;
		this.name		= name;
//		this.filename	= filename;
		this.original	= original;
		this.type		= type;
	}

	/**
	 * ファイルアップロードされた時のname属性を取得します。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) HTML5 ファイルアップロードの複数選択（multiple）対応
	 *
	 * @return	ファイルアップロードされた時のname属性
	 */
	public String getName() {
		return name;
	}

	/**
	 * コンテントタイプを取得します。
	 *
	 * @return	コンテントタイプ
	 */
	public String getContentType() {
		return type;
	}

	/**
	 * ファイル名(置き換え後)を取得します。
	 *
	 * @og.rev 5.7.1.2 (2013/12/20) zip 対応で、Fileオブジェクトを返すようにします。
	 * @og.rev 5.9.25.0 (2017/10/06) FILE型をString型に変更
	 *
	 * @return	ファイル名(置き換え後)
	 */
//	public String getFilesystemName() {
//	public File getUploadFile() {
	public String getUploadFile(){
		return filename;
	}

	/**
	 * ファイル名(置き換え後)の置き換えを実行します。
	 * useBackup = true にすると、dir の直下に、"_backup" フォルダを作成します。
	 * バックアップファイル名は、元のファイル名(拡張子含む) ＋ "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子
	 *
	 * newName が null の場合は、original のファイル名に、変換します。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) 新規追加
	 * @og.rev 5.7.2.1 (2014/01/17) FileUtil.renameTo の引数の順番を間違えていた。
	 * @og.rev 5.9.25.0 (2017/10/06) returnをString型に変更。引数にfileURLとsessionを追加
	 *
	 * @param	newName		ファイル名(置き換え後)
	 * @param	useBackup	置き換え後ファイルをバックアップするかどうか(true:バックアップする/false:しない)
	 * @param  fileURL		クラウドストレージ用のURL
	 * @param  hsession		セッション
	 * @return	最終的に作成されたファイルオブジェクト
	 */
	//public File renameTo( final String newName , final boolean useBackup) {
	public String renameTo( final String newName , final boolean useBackup, final String fileURL, HttpSession hsession) {

		String newNm = newName ;
		// 新規ファイル名を作成します。(拡張子等)
		if( newNm != null && newNm.length() > 0 ) {
			// 新ファイル名から拡張子取得
			String newExt = FileUtil.getExtension( newNm );
			if( newExt == null || newExt.length() == 0 ) {
				String oldExt = FileUtil.getExtension( original );
				newNm = newNm + "." + oldExt ;
			}
		}
		else {
			newNm = original;
		}

		File newFile = null ;
		if( newNm != null && newNm.length() > 0 ) {
			// 5.9.25.0 (2017/10/06) ADD bluemixｸﾗｳﾄﾞｽﾄﾚｰｼﾞを利用する処理を追加
			String storage = HybsSystem.sys( "CLOUD_STORAGE");
			if(storage != null && storage.length() > 0){
				// ｽﾄﾚｰｼﾞに保存
				StorageAPI storageApi = StorageAPIFactory.newStorageAPI(storage, HybsSystem.sys("CLOUD_STORAGE_CONTAINER"), hsession);
				storageApi.rename(fileURL, uniqKey, newNm, useBackup, hsession);
				// ﾌｧｲﾙ名をfilenameに設定する
				filename = newNm;
			}else{
				// 標準のﾌｧｲﾙ作成
				newFile = new File( dir,newNm );

				File uniqFile = new File( dir , uniqKey );		// 5.7.1.1 (2013/12/13) アップロードされたファイル

				// 5.7.2.1 (2014/01/17) FileUtil.renameTo の引数の順番を間違えていた。
	//			FileUtil.renameTo( newFile, uniqFile , useBackup );
				FileUtil.renameTo( uniqFile , newFile, useBackup );		// from ⇒ to の順番に指定。
				// (2017/09/22) ADD FILE型をString型に変更したので、getName()をfilenameに設定。
				filename = newFile.getName();
			}

//			// 置き換えファイルの存在チェック。
//			if( newFile.exists() ) {
//				if( useBackup ) {
//					// newNm にフォルダ階層を含む場合に、そなえて。
//					File parent = newFile.getParentFile();			// バックアップすべきファイルのフォルダ
//					File backup = new File( parent , "_backup" );	// その直下に、"_backup" フォルダを作成
//					if( !backup.exists() && !backup.mkdirs() ) {
//						String errMsg = "バックアップ処理でbackupフォルダの作成に失敗しました。[" + backup + "]";
//						throw new RuntimeException( errMsg );
//					}
//					// バックアップファイル名は、元のファイル名(拡張子含む) ＋ "_" + 現在時刻のlong値 + "." + 元のファイルの拡張子
//					String bkupName = newFile.getName() + "_" + System.currentTimeMillis() + "."  + FileUtil.getExtension( newNm ) ;
//					File fromFile = new File( dir,newNm );		// オリジナルの newFile をrename するとまずいので、同名のFileオブジェクトを作成
//					File bkupFile = new File( backup,bkupName );
//
//					if( !fromFile.renameTo( bkupFile ) ) {
//						String errMsg = "バックアップ処理でバックアップファイルをリネームできませんでした。" +CR
//											 + "  [" + fromFile + "] ⇒ [" + bkupFile + "]" ;
//						throw new RuntimeException( errMsg );
//					}
//				}
//				else if( !newFile.delete() ) {
//					String errMsg = "既存のファイル[" + newNm + "]が削除できませんでした。";
//					throw new RuntimeException( errMsg );
//				}
//			}
//
//			File uniqFile = new File( dir , uniqKey );		// 5.7.1.1 (2013/12/13) アップロードされたファイル
//			if( !uniqFile.renameTo( newFile ) ) {
//				String errMsg = "所定のファイルをリネームできませんでした。" + CR
//									+ "  [" + uniqFile + "] ⇒ [" + newFile + "]" ;
//				throw new RuntimeException( errMsg );
//			}

		}
		// 5.7.1.1 (2013/12/13) ここの処理が走ることは無いはず。
		else {
			String errMsg = "新ファイル名が存在しません。[" + newNm + "]" ;
			throw new RuntimeException( errMsg );
		}
		// 新ファイル名のセットは、すべての処理が完了してから、設定する。
		// 5.9.25.0 (2017/10/06) DELETE FILE型をString型に変更により、前の処理でfinenameは設定済み
		//filename = newFile ;
		return filename;
	}

	/**
	 * ファイル名(置き換え後)をセットします。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) 廃止
	 *
	 * @param	name	ファイル名(置き換え後)
	 */
//	public void setFilesystemName( final String name ) {
//		filename = name;
//	}

	/**
	 * ファイル名(オリジナル)を取得します。
	 *
	 * @return	ファイル名(オリジナル)
	 */
	public String getOriginalFileName() {
		return original;
	}

	/**
	 * ファイル名(置き換え後)の File オブジェクトを取得します。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) 廃止。
	 *
	 * @return Fileオブジェクト
	 */
//	public File getFile() {
//		if(dir == null || filename == null) {
//			return null;
//		}
//		else {
//			return new File(dir + File.separator + filename);
//		}
//	}

	/**
	 * ファイル名から 拡張子を取得します。
	 *
	 * @og.rev 5.7.1.1 (2013/12/13) ローカルに移動。若干のロジック変更
	 *
	 * @param	fileName	ファイル名
	 * @return	拡張子
	 */
//	private String getExtension( final String fileName ) {
//		int index = fileName.lastIndexOf('.');
////		if(index!=-1) {
////			return fileName.substring(index + 1, fileName.length());
////		}
//		if( index >= 0 ) {
//			return fileName.substring( index + 1 );
//		}
//		return "";
//	}

	/**
	 * 自然比較メソッド
	 * インタフェース Comparable の 実装に関連して、再定義しています。
	 * 登録されたシーケンス(画面の表示順)で比較します。
	 * equals メソッドでは、キーの同一性のみに着目して判定しています。
	 * この比較では、(運用上同一キーは発生しませんが)たとえ同一キーが存在した
	 * としても、その比較値が同じになることを保証していません。
	 *
	 * @param   other 比較対象のObject
	 *
	 * @return  このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
	 * @throws  ClassCastException 引数が UploadedFile ではない場合
	 * @throws  IllegalArgumentException 引数が null の場合
	 */
	@Override
	public int compareTo( final UploadedFile other ) {
		if( other == null ) {
			String errMsg = "引数が、null です。" ;
			throw new IllegalArgumentException( errMsg );
		}

		return ( uniqKey ).compareTo( other.uniqKey );
	}

	/**
	 * このオブジェクトと他のオブジェクトが等しいかどうかを示します。
	 * 画面は、画面IDが等しければ、言語や表示順に関係なく同一とみなされます。
	 * GUIInfo は、ユーザー個別に扱われ、そのグループには、key は唯一で、かつ
	 * 同一言語内で扱われるオブジェクトの為、同一とみなします。
	 *
	 * @param   object 比較対象の参照オブジェクト
	 *
	 * @return	引数に指定されたオブジェクトとこのオブジェクトが等しい場合は true、そうでない場合は false
	 */
	@Override
	public boolean equals( final Object object ) {
		if( object instanceof UploadedFile ) {
			return uniqKey.equals( ((UploadedFile)object).uniqKey );
		}

		return false ;
	}

	/**
	 * オブジェクトのハッシュコード値を返します。
	 * このメソッドは、java.util.Hashtable によって提供されるような
	 * ハッシュテーブルで使用するために用意されています。
	 * equals( Object ) メソッドをオーバーライトした場合は、hashCode() メソッドも
	 * 必ず 記述する必要があります。
	 * この実装では、getKey().hashCode() と同値を返します。
	 *
	 * @return  このオブジェクトのハッシュコード値
	 */
	@Override
	public int hashCode() {
		return uniqKey.hashCode() ;
	}

	/**
	 * オブジェクトの識別子として，詳細な画面情報を返します。
	 *
	 * @return  詳細な画面情報
	 */
	@Override
	public String toString() {
		StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
		rtn.append( this.getClass().getName()			).append( CR );
		rtn.append( "  uniqKey  :").append( uniqKey		).append( CR );
		rtn.append( "  filename :").append( filename	).append( CR );
		rtn.append( "  name     :").append( name		).append( CR );
		rtn.append( "  dir      :").append( dir			).append( CR );
		rtn.append( "  original :").append( original	).append( CR );
		rtn.append( "  type     :").append( type		).append( CR );
		return rtn.toString();
	}
}
