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

import java.io.File;
import java.io.PrintWriter;

import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.FixLengthData;
import org.opengion.fukurou.util.StringUtil;
// import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.AbstractTableFilter;
import org.opengion.hayabusa.db.DBTableModel;

/**
 * <p>TableFilter_DBARG_OUT は、TableFilter インターフェースを継承した、DBTableModel 処理用の
 * 実装クラスです。<br />
 *
 * ここでは、テーブル一覧の検索結果より、GF05 のテーブルカラム定義テーブルから
 * 必要な情報を取得し、テーブル作成スクリプトを作成します。
 * 出力ファイルは、テーブル名＋"S.sql" という命名規則で作成します。
 * 検索では、(SYSTEM_ID,TBLSYU,TABLE_NAME,NAME_JA,TABLESPACE_NAME,INITIAL_EXTENT,NEXT_EXTENT,COMMENTS)
 * の項目を取得する必要があります。
 *
 * @version  0.9.0  2000/10/17
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.1,
 */
public class TableFilter_DBARG_OUT extends AbstractTableFilter {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.1.1.0 (2009/12/01)" ;

	private static final String[] KEYS = new String[] { "OBJ_NAME","CLM","CLS_NAME","USE_LENGTH","CLM_NAME" };

	private static final int OBJ_NAME		= 0;
	private static final int CLM			= 1;
	private static final int CLS_NAME		= 2;
	private static final int USE_LENGTH		= 3;
	private static final int CLM_NAME		= 4;

	private static final String ENCODE = "Windows-31J" ;
//	private static final String CR     = HybsSystem.CR ;	// 5.1.1.0 (2009/12/01) CR 定義をAbstractTableFilterで行う。

	private static final int X = FixLengthData.X ;
	private static final int K = FixLengthData.K ;

	/**
	 * DBTableModel処理を実行します。<br />
	 *
	 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。
	 *
	 * @return 処理結果のDBTableModel
	 */
	public DBTableModel execute() {
		int[] clmNo = getTableColumnNo( KEYS );

		File dir = new File( getValue( "DIR" ) );
		if( ! dir.exists() && !dir.mkdirs() ) {
			String errMsg = "所定のフォルダが作成できませんでした。[" + dir + "]" ;
			// 4.3.4.4 (2009/01/01)
			throw new HybsSystemException( errMsg );
		}

		// カラム、クラス(桁数)、名称
		FixLengthData fixData = new FixLengthData(4);

		int[] addLen = new int[] { 1,5,5,1 };	// 各データ間のスペース
		int[] type   = new int[] { X,X,X,K };	// 各データの種別 X:半角 S:ゼロ埋め K:全角混在
		fixData.setAddLength( addLen );
		fixData.setType( type );

		String[] data  = null;
		String oldObjName = null;

		int rowCnt = table.getRowCount();
		for( int row=0; row<rowCnt; row++ ) {
			String objName = null;
			try {
				data = table.getValues( row );
				objName = data[clmNo[OBJ_NAME]];

				boolean blk = ! objName.equals( oldObjName ) ;
				if( row > 0 && blk ) {
					PrintWriter writer = FileUtil.getPrintWriter( new File( dir,oldObjName + ".sql" ),ENCODE );
					writer.println( makeHeadLine( oldObjName ) );
					writer.print( fixData.getAllFixData() );
					writer.println( makeEndLine( oldObjName ) );
					writer.close();
					fixData.clear();
				}

				String[] outData = makeLineList( clmNo,data,blk );
				fixData.addListData( outData );

				oldObjName = objName ;
			}
			catch( RuntimeException ex ) {
				ErrorMessage errMessage = makeErrorMessage( "TableFilter_DBARG_OUT Error",ErrorMessage.NG );
				errMessage.addMessage( row+1,ErrorMessage.NG,"ARG",ex.getMessage() );
				errMessage.addMessage( row+1,ErrorMessage.NG,"ARG",StringUtil.array2csv( data ) );
				errMessage.addMessage( row+1,ErrorMessage.NG,"ARG","OBJ_NAME=[" + objName + "]" );
			}
		}

		// 常に、一回り遅れてデータ出力している為、最後のデータを出力しておく必要がある。
		PrintWriter writer = FileUtil.getPrintWriter( new File( dir,oldObjName + ".sql" ),ENCODE );
		writer.println( makeHeadLine( oldObjName ) );
		writer.print( fixData.getAllFixData() );
		writer.println( makeEndLine( oldObjName ) );
		writer.close();
		fixData.clear();

		return table;
	}

	/**
	 * ヘッダーとして使用する文字列を作成します。
	 * 
	 * @param  objName String
	 * @return  String ヘッダーとして使用する文字列
	 */
	private String makeHeadLine( final String objName ) {

		StringBuilder buf = new StringBuilder();
		buf.append( "DROP TYPE "              ).append( objName ).append( "_ARRAY;"    ).append( CR );
		buf.append( "CREATE OR REPLACE TYPE " ).append( objName ).append( " AS OBJECT" ).append( CR );
		buf.append( " (" );

		return buf.toString() ;
	}

	/**
	 * 各行に相当する文字列の配列を作成します。
	 * 
	 * @param  clmNo int[]
	 * @param  data String[]
	 * @param  first boolean
	 * @return  String[]
	 */
	private String[] makeLineList( final int[] clmNo,final String[] data,final boolean first ) {
		// カンマ、カラム、クラス(桁数)、名称
		String[] outData = new String[4];

		outData[0] = ( first ) ? "   " : " , " ;			// カンマ
		outData[1] = data[clmNo[CLM]] ;						// カラム

		String clsName = data[clmNo[CLS_NAME]];
		if( clsName.startsWith( "CLOB" ) || clsName.startsWith( "DATE" ) ) {
			data[clmNo[USE_LENGTH]] = null;
		}
		String useLen = data[clmNo[USE_LENGTH]];
		if( useLen != null && ! useLen.equals( "0" ) ) {
			outData[2] = clsName + "(" + useLen + ")" ;		// クラス(桁数)
		}
		else {
			outData[2] = clsName ;
		}

		String nameJA = data[clmNo[CLM_NAME]] ;					// 名称
		if( nameJA != null ) {
			outData[3] = "-- " + nameJA ;
		}

		return outData ;
	}

	/**
	 * 最後の行に相当する文字列を作成します。
	 * 
	 * @param  objName String
	 * @return  String 最後の行に相当する文字列
	 */
	private String makeEndLine( final String objName ) {

		StringBuilder buf = new StringBuilder();
		buf.append( "  ) ;" ).append( CR );
		buf.append( "/"     ).append( CR );
		buf.append( CR );
		buf.append( "CREATE OR REPLACE TYPE " ).append( objName ).append( "_ARRAY" );
		buf.append( " AS VARRAY(1000) OF "    ).append( objName ).append( ";"      ).append( CR );
		buf.append( "/"     );

		return buf.toString() ;
	}
}
