/*
 * 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.io;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.opengion.fukurou.db.ConnectionFactory;
import org.opengion.fukurou.db.DBFunctionName;
import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.db.Transaction;
import org.opengion.fukurou.db.TransactionReal;
import org.opengion.fukurou.util.ApplicationInfo;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.TagBuffer;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBColumn;
import org.opengion.hayabusa.db.DBColumnConfig;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.db.DBTableModelUtil;
import org.opengion.hayabusa.resource.ResourceManager;
import org.opengion.hayabusa.resource.UserInfo;

/**
 * クライアントのファイルをサーバーにアップロードするタグです。
 *
 * アップロード後の属性は、DBTableModel に格納することにより、通常のデータと
 * 同様の取り出し方が可能です。
 * また、通常のファイルアップロード時の、form で使用する、enctype="multipart/form-data"
 * を指定した場合の、他のリクエスト情報も、{&#064;XXXX} 変数で取り出すことが可能です。
 *
 * この upload タグでは、アップロード後に、指定のファイル名に変更する機能があります。
 * file 登録ダイアログで指定した name に、"_NEW" という名称を付けたリクエスト値を
 * ファイルのアップロードと同時に送信することで、この名前にファイルを付け替えます。
 * また、アップロード後のファイル名は、name 指定の名称で、取り出せます。
 * クライアントから登録したオリジナルのファイル名は、name に、"_ORG" という名称
 * で取り出すことが可能です。
 *
 * 新ファイル名に拡張子が設定されていない場合は、オリジナルファイル名の拡張子をセット
 * します。
 *
 * @og.formSample
 * ●形式：&lt;og:upload fileURL="…" maxPostSize="…" /&gt;
 * ●body：なし
 *
 * ●使用例 :
 *
 * 【query.jsp】
 *       &lt;form method="POST" action="result.jsp" enctype="multipart/form-data" target="RESULT"&gt;
 *       &lt;table summary="layout" &gt;
 *       &lt;tr&gt;&lt;og:input type="text" name="submitter" value="{&#064;USER.JNAME}" size="20" msg="MSG0014" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;
 *           &lt;og:input type="file" name="file1"      size="30" msg="MSG0015" /&gt;
 *           &lt;og:input             name="file1_NEW"  size="10" lbl="FILENAME" /&gt;
 *       &lt;/tr&gt;&lt;tr&gt;
 *           &lt;og:input type="file" name="file2"      size="30" msg="MSG0015" /&gt;
 *           &lt;og:input             name="file2_NEW"  size="10" lbl="FILENAME" /&gt;
 *       &lt;/tr&gt;&lt;tr&gt;
 *           &lt;og:input type="file" name="file3"      size="30" msg="MSG0015" /&gt;
 *           &lt;og:input             name="file3_NEW"  size="10" lbl="FILENAME" /&gt;
 *       &lt;/tr&gt;&lt;tr&gt;
 *           &lt;og:column name="writable"     value="false"  /&gt;
 *       &lt;/tr&gt;
 *       &lt;/table&gt;
 *
 * 【result.jsp】
 *       &lt;og:upload
 *           fileURL     = "{&#064;USER.ID}"
 *       /&gt;
 *       &lt;br /&gt;
 *       &lt;og:message msg="MSG0003" comment="ファイルの登録が完了しました。" /&gt;
 *
 *       &lt;og:view
 *           command      = "NEW"
 *           viewFormType = "HTMLTable"
 *           writable     = "{&#064;writable}"
 *       /&gt;
 *
 *       &lt;table&gt;
 *       &lt;tr&gt;&lt;og:input name="submitter" value="{&#064;submitter}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="writable"  value="{&#064;writable}"  /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="directory" value="{&#064;directory}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file1"     value="{&#064;file1}"     /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file1_NEW" value="{&#064;file1_NEW}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file1_ORG" value="{&#064;file1_ORG}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file2"     value="{&#064;file2}"     /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file2_NEW" value="{&#064;file2_NEW}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file2_ORG" value="{&#064;file2_ORG}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file3"     value="{&#064;file3}"     /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file3_NEW" value="{&#064;file3_NEW}" /&gt;&lt;/tr&gt;
 *       &lt;tr&gt;&lt;og:input name="file3_ORG" value="{&#064;file3_ORG}" /&gt;&lt;/tr&gt;
 *       &lt;/table&gt;
 *
 * @og.group ファイル入力
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class FileManager {
	public static enum ACTION { VIEW, INPUT, UPLOAD };
	
	private static final String FS					= File.separator;
	private static final String US					= "/";

	private static final String DEFAULT_FILE_PATH	= HybsSystem.sys( "FILEMG_DEFAULT_FILE_PATH" );
	private static final String DEFAULT_FILE_URL	= HybsSystem.sys( "FILEMG_DEFAULT_FILE_URL" );

	private static final String DEFAULT_SYFILE		= "**";

	private final String systemId;
	private final String syfiles;
	private final String refKey;
	private final String searchKeys;

	private final String[] syfilesArr;
	private final String[] searchKeyArr;

	private String			dbid		= null;
	private Transaction		tran		= null;
	private ResourceManager res			= null;
	private UserInfo		user		= null;
	private DBFunctionName	dbName		= null;

	private boolean			isInit		= true;


	public FileManager( final String systemId, final String syfiles, final String refKey, final String searchKeys ) {
		this.systemId	= systemId;
		this.syfiles	= syfiles;
		this.refKey		= refKey;
		this.searchKeys = searchKeys;

		syfilesArr		= StringUtil.csv2Array( this.syfiles );
		searchKeyArr	= StringUtil.csv2Array( this.syfiles );
	}

	public void setDbid( final String dbid ) {
		this.dbid = dbid;
	}

	public void setTransaction( final Transaction tran ) {
		this.tran = tran;
	}

	public void setResource( final ResourceManager res ) {
		this.res = res;
	}

	public void setUser( final UserInfo user ) {
		this.user = user;
	}

	public void init() {
		if( tran == null ) {
			tran = new TransactionReal( dbid, new ApplicationInfo() );
		}
		this.dbName = DBFunctionName.getDBName( ConnectionFactory.getDBName( dbid ) );

		isInit = false;
	}

	public DBTableModel view() {
		String names = "SYNAME,FILE_NAME,CONTENTS";
		String[] namesArr = StringUtil.csv2Array( names );

		DBTableModel table = DBTableModelUtil.newDBTable();
		table.init( namesArr.length );
		for( int i=0; i<namesArr.length; i++ ) {
			DBColumn dbColumn = res.makeDBColumn( namesArr[i] );
			table.setDBColumn( i,dbColumn );
		}

		FileList fl = new FileList();
		FileData[] fdArr = fl.getFileList();

		for( FileData fd : fdArr ) {
			String[] vals = fd.get( namesArr );
			for( int i=0; i<vals.length; i++ ) {
				if( "FILE_NAME".equals( namesArr[i] ) && vals[i] != null && vals[i].length() > 0 ) {
					String fileUrl = fd.get( "FILE_URL" );
					String url = StringUtil.nval( fileUrl, DEFAULT_FILE_URL );
					if( !url.startsWith( "http://" ) && !url.startsWith( "https://" ) && !url.startsWith( US ) ) {
						url = StringUtil.urlAppend( US + HybsSystem.sys( "CONTEXT_NAME"), url );
					}
					url = StringUtil.urlAppend( url, fd.get( "UNIQ_ID" ) );
					String fileAddr = url + US + fd.get( "SEQNO" ) + "." + fd.get( "SUFFIX" );

					TagBuffer tag = new TagBuffer( "a" );
					tag.add( "href", fileAddr );
					tag.setBody( vals[i] );
					vals[i]= tag.makeTag();
				}
			}
			table.addColumnValues( vals );
		}
		return table;
	}

	public DBTableModel input( final int freeCount ) {
		String names = "SYNAME,UPFILE,CONTENTS,DESCRIPTION,HINAGATA";
		String[] namesArr = StringUtil.csv2Array( names );

		DBTableModel table = DBTableModelUtil.newDBTable();
		table.init( namesArr.length );
		for( int i=0; i<namesArr.length; i++ ) {
			DBColumn dbColumn = res.makeDBColumn( namesArr[i] );
			if( "UPFILE".equals( namesArr[i] ) ) {
				DBColumnConfig config = dbColumn.getConfig();
				config.setEditor( "UPLOAD" );
				dbColumn = new DBColumn( config );
			}
			table.setDBColumn( i,dbColumn );
		}

		for( String syfile : syfilesArr ) {
			TemplateData ft = new TemplateData( syfile );
			String[] vals = ft.get( namesArr );
			for( int i=0; i<vals.length; i++ ) {
				if( "HINAGATA".equals( namesArr[i] ) && vals[i] != null && vals[i].length() > 0 ) {
					TagBuffer tag = new TagBuffer( "a" );
					tag.add( "href", vals[i] );
					tag.setBody( vals[i] );
					vals[i]= tag.makeTag();
				}
			}
			table.addColumnValues( vals );
		}

		for( int i=0; i<freeCount; i++ ) {
			TemplateData ft = new TemplateData( DEFAULT_SYFILE );
			String[] vals = ft.get( namesArr );
			table.addColumnValues( vals );
		}

		return table;
	}

	public void upload( final String tmpFileDir, final String[] fileNameArr, final String[] contentArr, final String guikey ) {
		for( int i=0; i<fileNameArr.length; i++ ) {
			String fileName = fileNameArr[i];
			if( fileName != null && fileName.length() > 0 ) {
				String syfile = ( i < syfilesArr.length ? syfilesArr[i] : DEFAULT_SYFILE );
				FileData fd = new FileData( syfile );
				fd.insert( tmpFileDir + fileName, contentArr[i], guikey );
			}
		}
	}

//	private String getSyfilesWhere( final String syfiles ) {
//		StringBuilder buf = new StringBuilder();
//		String[] syfilesArr = StringUtil.csv2Array( syfiles );
//		buf.append( " and SYFILE in (" );
//		for( int i=0; i<syfilesArr.length; i++ ) {
//			if( i > 0 ) { buf.append( "','" ); }
//			buf.append( '\'' ).append( syfilesArr[i] ).append( '\'' );
//		}
//		buf.append( ")" );
//
//		return buf.toString();
//	}

	private static final String GE38_NAMES =
		" SYFILE,SYNAME,DESCRIPTION,HINAGATA,SAVE_FOLDER"
		+ ",FILE_URL,ICON_URL,ROLES,SU_LMT,DY_LMT,FGANGO,FGZIP"
		+ ",SIZE_LMT,SUFFIX_LMT";

	private final String[] GE38_NAME_ARR =
		StringUtil.csv2Array( GE38_NAMES );

	private static final String GE38_SELECT =
		"select " + GE38_NAMES + " from GE38 where SYSTEM_ID=? and SYFILE=? and FGJ='1'";

	private class TemplateData {

		private Map<String,String> data = new HashMap<String,String>();

		public TemplateData( final String syfile ) {
			if( DEFAULT_SYFILE.equals( syfile ) ) {
				data.put( "SYFILE", DEFAULT_SYFILE );
				data.put( "SU_LMT", "0" );
				data.put( "DY_LMT", "0" );
				data.put( "SIZE_LMT", "0" );
				data.put( "FGANGO", "0" );
				data.put( "FGZIP", "0" );
			}
			else {
				String[] ge38Args = { systemId, syfile };
				String[][] ge38Vals = DBUtil.dbExecute( GE38_SELECT, ge38Args, tran, dbid );

				if( ge38Vals.length <= 0 ) {
					String msg = "指定されたファイル種別は、マスタに存在しません。[ファイル種別=" + syfile + "]";
					throw new HybsSystemException( msg );
				}

				for( int i=0; i<GE38_NAME_ARR.length; i++ ) {
					data.put( GE38_NAME_ARR[i], ge38Vals[0][i] );
				}
			}
		}

		/**
		 * @return the syFile
		 */
		public String get( final String key ) {
			return data.get( key );
		}

		/**
		 * @return the syFile
		 */
		public String[] get( final String[] keyArr ) {
			String[] valArr = new String[keyArr.length];
			for( int i=0; i<valArr.length; i++ ) {
				valArr[i] = data.get( keyArr[i] );
			}
			
			return valArr;
		}
	}

	private static final String GE39_NAMES =
		" UNIQ_ID,SEQNO,SYFILE,SUFFIX,REF_KEY,SYSTEM_ID,SYNAME,SAVE_FOLDER"
		+ ",FILE_URL,ICON_URL,ROLES,SU_LMT,DY_LMT,FGANGO,FGZIP"
		+ ",FILE_NAME,CONTENTS,FG_NEW,SIZE_FILE,DYFILESET,USRTOROKU,SETGUIID,FGKOSHIN,FGKOKAI"
		+ ",SRCHKEY1,SRCHKEY2,SRCHKEY3,SRCHKEY4,SRCHKEY5,SRCHKEY6,SRCHKEY7,SRCHKEY8,SRCHKEY9,SRCHKEY10";

	private final String[] GE39_NAME_ARR =
		StringUtil.csv2Array( GE39_NAMES );

	private final String GE39_REFKEY_SELECT =
		"select " + GE39_NAMES + " from GE39 where REF_KEY=? and FG_NEW='1' and FGJ='1' order by UNIQ_ID desc, SEQNO desc";

	private class FileList {

		private final List<FileData> list = new ArrayList<FileData>();

		public FileList() {
			String[] ge39Args = { refKey };
			String[][] ge39Vals = DBUtil.dbExecute( GE39_REFKEY_SELECT, ge39Args, tran, dbid );

			if( ge39Vals.length > 0 ) {
				for( int i=0; i<ge39Vals.length; i++ ) {
					list.add( new FileData( ge39Vals[i] ) );
					System.out.println( ge39Vals[i][2] );
				}
			}
		}

		public FileData[] getFileList() {
			return list.toArray( new FileData[0] );
		}

	}

	private final String GE39_REFKEY_SYFILE =
		"select " + GE39_NAMES + " from GE39 where REF_KEY=? and SYSTEM_ID=? and SYFILE=? and FG_NEW='1' and FGJ='1' order by UNIQ_ID desc, SEQNO desc";

	private final String GE39_INSERT = "insert into GE39 ("
		+ " UNIQ_ID,SEQNO,SYFILE,SUFFIX,REF_KEY,SYSTEM_ID,SYNAME,SAVE_FOLDER"
		+ ",FILE_URL,ICON_URL,ROLES,SU_LMT,DY_LMT,FGANGO,FGZIP"
		+ ",FILE_NAME,CONTENTS,FG_NEW,SIZE_FILE,DYFILESET,USRTOROKU,SETGUIID,FGKOSHIN,FGKOKAI"
		+ ",SRCHKEY1,SRCHKEY2,SRCHKEY3,SRCHKEY4,SRCHKEY5,SRCHKEY6,SRCHKEY7,SRCHKEY8,SRCHKEY9,SRCHKEY10"
		+ ",FGJ,DYSET,PGSET,USRSET,DYUPD,PGUPD,USRUPD"
		+ ") values ( "
		+ " ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"
		+ ",?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"
		+ ",?,?,?,?,?,?,? )";

	private final String GE39_FGNEW_UPDATE =
		"update GE39 set FG_NEW='0' where UNIQ_ID=? and FG_NEW='1'";

	private class FileData {

		private final String syfile;
		private final String uniqId;
		private final String seqNo;

		private final boolean exists;

		private final Map<String,String> data = new HashMap<String,String>();

		public FileData( final String syfile ) {
			this.syfile = syfile;

			if( DEFAULT_SYFILE.equals( this.syfile ) ) {
				exists = false;
			}
			else {
				String[] ge39Args = { refKey, systemId, syfile };
				String[][] ge39Vals = DBUtil.dbExecute( GE39_REFKEY_SYFILE, ge39Args, tran, dbid );

				if( ge39Vals.length <= 0 ) {
					exists = false;
				}
				else {
					exists = true;
					for( int i=0; i<GE39_NAME_ARR.length; i++ ) {
						data.put( GE39_NAME_ARR[i], ge39Vals[0][i] );
					}
				}
			}

			uniqId = ( exists ? data.get( "UNIQ_ID" ) : "-1" );
			seqNo =  ( exists ? data.get( "SEQNO" ) : "-1" );
		}

		public FileData( final String[] vals ) {
			if( DEFAULT_SYFILE.equals( this.syfile ) || vals == null || vals.length == 0 ) {
				exists = false;
			}
			else {
				exists = true;
				for( int i=0; i<GE39_NAME_ARR.length; i++ ) {
					data.put( GE39_NAME_ARR[i], vals[i] );
				}
			}

			uniqId = vals[0];
			seqNo = vals[1];
			syfile = vals[2];
		}

		/**
		 * @return the syFile
		 */
		public String get( final String key ) {
			return data.get( key );
		}

		/**
		 * @return the syFile
		 */
		public String[] get( final String[] keyArr ) {
			String[] valArr = new String[keyArr.length];
			for( int i=0; i<valArr.length; i++ ) {
				valArr[i] = data.get( keyArr[i] );
			}
			
			return valArr;
		}

		public void insert( final String tmpFileName, final String content, final String guikey ) {
			String newUniq = ( exists ? uniqId : String.valueOf( dbName.getSequence( "GE39S02", tran ) ) );
			String newSeq =  String.valueOf( Integer.valueOf( seqNo ) + 1 );

			if( exists ) {
				String[] ge39UpdArgs = { uniqId };
				DBUtil.dbExecute( GE39_FGNEW_UPDATE, ge39UpdArgs, tran, dbid );		
			}

			TemplateData td = new TemplateData( syfile );

			String name = getName( tmpFileName );
			String suffix = getSuffix( tmpFileName );

			String saveFolder = td.get( "SAVE_FOLDER" );
			String directory = StringUtil.nval( saveFolder, DEFAULT_FILE_PATH );
			directory = HybsSystem.url2dir( directory );
			directory = StringUtil.urlAppend( directory, uniqId );
			String newFileName = directory + FS + newSeq + "." + suffix;

			new File( directory ).mkdirs();
			FileUtil.copy( tmpFileName, newFileName );
			
			String ymdh = HybsSystem.getDate( "yyyyMMddHHmmss" ) ;
			String[] ge39InsArgs = {
					newUniq, newSeq, syfile, suffix, refKey, systemId, td.get( "SYNAME" ), saveFolder
					,td.get( "FILE_URL" ), td.get( "ICON_URL" ), td.get( "ROLES" ), td.get( "SU_LMT" ), td.get( "DY_LMT" ), td.get( "FGANGO" ), td.get( "FGZIP" )
					,name, content, "1", "0", ymdh, user.getUserID(), guikey, "1", "1"
					,( searchKeyArr.length > 0 ?  searchKeyArr[0] : null ), ( searchKeyArr.length > 0 ?  searchKeyArr[0] : null ), null, null, null, null, null, null, null, null
					,"1", ymdh, guikey, user.getUserID(), ymdh, guikey, user.getUserID()
			};

			DBUtil.dbExecute( GE39_INSERT, ge39InsArgs, tran, dbid );
		}
	}

	/**
	 * ファイル名から拡張子(小文字)を求めます。
	 * 
	 * @param fileName
	 * @return 拡張子(小文字)
	 */
	public static String getName( final String fileName ) {
		String name = null;
		if( fileName != null ) {
			name = new File( fileName ).getName();
			int sufIdx = name.lastIndexOf( '.' );
			if( sufIdx >= 0 ) {
				name = name.substring( 0, sufIdx );
			}
		}
		return name;
	}

	/**
	 * ファイル名から拡張子(小文字)を求めます。
	 * 
	 * @param fileName
	 * @return 拡張子(小文字)
	 */
	public static String getSuffix( final String fileName ) {
		String suffix = null;
		if( fileName != null ) {
			int sufIdx = fileName.lastIndexOf( '.' );
			if( sufIdx >= 0 ) {
				suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN );
			}
		}
		return suffix;
	}
}