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

import org.opengion.fukurou.util.Closer ;
import org.opengion.fukurou.util.LogWriter;

import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.hssf.eventusermodel.HSSFRequest ;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener ;
import org.apache.poi.hssf.record.SSTRecord ;
import org.apache.poi.hssf.record.Record ;
import org.apache.poi.hssf.record.BOFRecord  ;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.UnicodeString;

import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 【EXCEL取込】雛形EXCELシートの解析処理を行う為の、HSSFListener 拡張クラスです。
 * このオブジェクトは、HSSFRequest クラスの addListenerForAllRecords メソッドに渡す
 * HSSFListener インターフェースを実装しています。また、雛形EXCEL を処理後、ExcelLayout
 * 管理クラスを取得することが出来ます。
 *
 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class HybsHSSFListener implements HSSFListener {
	private SSTRecord sstrec;
	private int       sheetNo = -1;
	private int       sheetSize = 0;
	private boolean   trace  = false;

	private ExcelLayout laynot = null;

	/**
	 * 雛形EXCELの {&#064;カラム} 解析情報を設定します。
	 *
	 * 雛形EXCELは、HSSFListener を使用して、イベント駆動で取得します。その場合、
	 * {&#064;カラム}を含むセルを見つける都度、このメソッドを呼び出して、{&#064;カラム}の
	 * 位置（行列番号）を設定します。
	 * データEXCELからデータを読み出す場合は、ここで登録したカラムの行列より、読み込みます。
	 * 具体的には、HSSFListener#processRecord( Record )で、SSTRecord.sid の 情報をキープしておき、
	 * LabelSSTRecord.sid 毎に、{&#064;カラム}を含むかチェックし、含む場合に、このメソッドに
	 * 解析情報を設定します。
	 * 
	 * @og.rev 4.3.4.0 (2008/12/01) POI3.2対応 引数の型変更(SSTRecord の toString() 化)
	 *
	 * @param record Record
	 */
	public void processRecord( final Record record ) {
		switch ( record.getSid() ) {
			// Workbook での シート毎に呼ばれます。先行するため、この回数をシート数として数えます。
			case BoundSheetRecord.sid:
				// 4.0.0.0 (2007/11/29) 入れ子if の統合
				if( trace && record instanceof BoundSheetRecord ) {
					BoundSheetRecord bsr = (BoundSheetRecord) record;
					System.out.println("Sheet named: " + bsr.getSheetname() );
				}
				sheetSize ++ ;
				break;
			// TYPE_WORKSHEET の場合、シート開始毎に呼ばれます。
			case BOFRecord.sid:
				if( record instanceof BOFRecord ) {
					BOFRecord bof = (BOFRecord) record;
					if(bof.getType() == BOFRecord.TYPE_WORKBOOK) {
						if( trace ) { System.out.println("Encountered workbook"); }
					}
					else if(bof.getType() == BOFRecord.TYPE_WORKSHEET) {
						// シート開始毎に呼ばれるため、現在のシート番号を得るのに使用します。
						sheetNo++ ;
						if( trace ) { System.out.println("Encountered sheet [" + sheetNo + "]" ); }
					}
				}
				break;
			// すべてのレコードにアクセスするために、一度だけ呼ばれます。ここで初期化しておきます。
			case SSTRecord.sid:
				if( record instanceof SSTRecord ) {
					sstrec = (SSTRecord) record;
					laynot = new ExcelLayout( sheetSize );
					if( trace ) { System.out.println( "SSTRecord:[" + sstrec.getNumUniqueStrings() + "]" ); }
			//		for( int k=0; k<sstrec.getNumUniqueStrings(); k++ )	{
			//			String val = sstrec.getString(k);
			//			System.out.println("SSTRecord id=[" + k + "]=[" + val + "]" );
			//		}
				}
				break;
			// 文字型のレコードです。
			case LabelSSTRecord.sid:
				if( record instanceof LabelSSTRecord ) {
					LabelSSTRecord lrec = (LabelSSTRecord) record;

					int   row  = lrec.getRow();
					short col  = lrec.getColumn();
	// POI3.0		String val = sstrec.getString( lrec.getSSTIndex() ) ;
					UnicodeString ustr = sstrec.getString( lrec.getSSTIndex() ) ;
					String val = ustr.getString();

					// カラムに、{@を含む場合は、laynot に雛形情報を追加します。
					// 整合性チェックは、ExcelLayoutData クラスで行います。
					if( val != null && val.indexOf( "{@" ) >= 0 ) {
						if( trace ) { System.out.println("String cell id,sheet,row,col=[" + lrec.getSSTIndex() + "," + sheetNo + "," + row + "," + col + "] => " + val ); }
						laynot.addModel( sheetNo,val,row,col );
					}
				}
				break;
			default :
				LogWriter.log( "想定外のイベントを受け取りました。=[" + record.getSid() + "]" );
				break;
		}
	}

	/**
	 * 雛形EXCELシートの {&#064;カラム} 解析データ管理クラスを返します。
	 *
	 * 雛形EXCELを解析し終わった後で、解析データ管理クラスを取り出します。
	 * 雛形EXCELと、データEXCELが１対１ではないため、リアルタイム処理が出来ません。
	 * 一旦、すべての雛形EXCELを解析し終わった後で、データEXCEL処理を行う必要があります。
	 *
	 * @return ExcelLayout 雛形EXCELシート
	 */
	public ExcelLayout getLayout() { return laynot; }

	/**
	 * 雛形EXCEL のシート数を返します。
	 *
	 * @return int 雛形シート数
	 */
	public int getSheetSize() { return sheetSize; }

	/**
	 * 処理経過情報を表示するかどうかを指定します(初期値：false)
	 *
	 * イベント毎の状況を、標準出力に出力するかどうかのフラグです。
	 * 初期値は、false です。
	 *
	 * @param flag boolean
	 */
	public void setTrace( final boolean flag ) { trace = flag; }

	/**
	 * EXCELレイアウト情報を取得します。
	 *
	 * POIのイベント処理(HSSFListener)で、各イベントごとに処理を行います。
	 * イベントは、BOFRecord、BoundSheetRecord、SSTRecord、LabelSSTRecord について
	 * 発生するように設定しています。
	 *
	 * @param file File	処理するEXCELファイル
	 * @param trace boolean	標準出力にトレース情報を出力するかどうかを指定します。
	 * @return ExcelLayout EXCELレイアウト情報
	 * @exception IOException
	 */
	public static ExcelLayout makeExcelLayout( final File file ,final boolean trace ) throws IOException {

		FileInputStream fin = null;
		InputStream din = null;
		final HybsHSSFListener event ;
		try {
			fin = new FileInputStream( file );
			POIFSFileSystem poifs = new POIFSFileSystem( fin );
			din = poifs.createDocumentInputStream( "Workbook" );
			HSSFRequest req = new HSSFRequest();

			event = new HybsHSSFListener();
			event.setTrace( trace );		// 標準出力にトレース情報を出力します。

		//	req.addListenerForAllRecords( event );	// 全イベントを処理
			req.addListener( event,BOFRecord.sid );
			req.addListener( event,BoundSheetRecord.sid );
			req.addListener( event,SSTRecord.sid );
			req.addListener( event,LabelSSTRecord.sid );

			HSSFEventFactory factory = new HSSFEventFactory();
			factory.processEvents( req,din );
		}
		finally {
			Closer.ioClose( fin );		// 4.0.0 (2006/01/31) close 処理時の IOException を無視
			Closer.ioClose( din );		// 4.0.0 (2006/01/31) close 処理時の IOException を無視
	//		fin.close();
	//		din.close();
		}

		return event.getLayout();
	}
}
