/*
 * 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.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.system.Closer ;
import org.opengion.fukurou.system.DateSet;							// 6.4.2.0 (2016/01/29)

import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.RecordFormatException;					// 6.4.6.0 (2016/05/27) poi-3.15

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFCell;
// import org.apache.poi.hssf.usermodel.HSSFDateUtil;				// 7.3.0.0 (2021/01/06) Deprecated.
import org.apache.poi.ss.usermodel.DateUtil;						// 7.3.0.0 (2021/01/06)
import org.apache.poi.hssf.usermodel.HSSFRichTextString;

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

import java.text.NumberFormat ;
import java.text.DecimalFormat ;
import java.util.Iterator ;

/**
 * 【EXCEL取込】雛形EXCELシートの解析処理を行う為の、HSSFListener 拡張クラスです。
 * このオブジェクトは、HSSFRequest クラスの addListenerForAllRecords メソッドに渡す
 * HSSFListener インターフェースを実装しています。また、雛形EXCEL を処理後、ExcelLayout
 * 管理クラスを取得することが出来ます。
 *
 *   ※ 最新のPOIでは、org.apache.poi.ss.usermodel を使う事で、2003形式、2007形式に対応させます。
 *      report パッケージは保守対象外なので、あえて修正しません。
 *
 * @og.rev 3.8.0.0 (2005/06/07) 新規追加
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ExcelDataPickup {

	private final ExcelLayout	layout	;

	private final InputStream	in	;
	private final HSSFWorkbook	wb	;
	private NumberFormat numFormat	;

	private final boolean debug		;

	/**
	 * 雛形EXCELを処理済みのExcelLayoutオブジェクトと、
	 * データEXCELファイル名よりオブジェクトを構築します。
	 *
	 * 内部で、HSSFWorkbook を構築します。
	 *
	 * @param	layout	雛形EXCELを処理済みのExcelLayoutオブジェクト
	 * @param	filename	データEXCELファイル名
	 * @param debug デバッグフラグ
	 */
	public ExcelDataPickup( final ExcelLayout layout,final File filename, final boolean debug ) {
		this.layout	= layout;
		this.debug	= debug;

		try {
			in = new FileInputStream( filename );

			final POIFSFileSystem fs = new POIFSFileSystem( in );
			wb = new HSSFWorkbook( fs );
		}
		catch( final IOException ex) {
			final String errMsg = "ファイル読込みエラー[" + filename.getName() + "]"  ;
			throw new HybsSystemException( errMsg,ex );
		}
		catch( final RecordFormatException ex) {
			final String errMsg = "無効の形式/データが使用されています。[" + filename.getName() + "]"
					+ "現バージョンのPOIでは読み取ることが出来ません。"
					+ "例えば、自動フィルタの設定されたシートが含まれる場合などです。" ;
			throw new HybsSystemException( errMsg,ex );
		}
	}

	/**
	 * データEXCELファイル名のシート数を返します。
	 *
	 * 内部で、HSSFWorkbook を構築します。
	 *
	 * @return	シート数
	 */
	public int getSheetSize() {
		return wb.getNumberOfSheets();
	}

	/**
	 * データEXCELファイル名のデータをピックアップします。
	 *
	 * この処理を行うと、ExcelLayout オブジェクトにデータEXCEL解析結果を
	 * 保存しますので、処理側でその結果を取り出して使用します。
	 *
	 * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
	 *
	 * @param	modelSheetNo	雛形シート番号
	 * @param	sheetNo	データシート番号
	 * @param	loopClm	繰返必須カラム(なければ通常の１対１処理)
	 */
	public void execute( final int modelSheetNo, final int sheetNo, final String loopClm ) {

		final HSSFSheet sheet = wb.getSheetAt( sheetNo );
		layout.dataClear();

		if( debug ) { System.out.println( sheetNo + ":" + wb.getSheetName( sheetNo ) ); }

		final Iterator<ExcelLayoutData> ite = layout.getLayoutDataIterator( modelSheetNo,loopClm ) ;
		while( ite.hasNext() ) {
			final ExcelLayoutData data = ite.next();
			final String clm  = data.getClm();
			final int    edbn = data.getEdbn();
			final int    row  = data.getRowNo();
			final short  col  = data.getColNo();

	//		if( clm.indexOf( debugClm ) >= 0 ) { debug = true; }
	//		else { debug = false; }

			final String val = getValue( sheet,row,col );
			layout.addData( clm,edbn,val );
			if( debug ) { System.out.println( data.toString() + "=[" + val + "]" ); }
		}
	}

	/**
	 * シートオブジェクト(HSSFSheet)から行列番号を指定して値を取り出します。
	 * 行オブジェクト(HSSFRow)や列オブジェクト(HSSFCell)が存在しない場合は、nullを返します。
	 * それ以外は、各セルタイプに応じて、処理を行います。
	 * 関数タイプ(HSSFCell.CELL_TYPE_FORMULA)の場合は、まず、文字列として取り出し、ゼロ文字列の
	 * 場合は、数字タイプとして取り出します。
	 *
	 * @og.rev 3.8.0.9 (2005/10/17) 結果を rtrim(右スペース削除)します。
	 * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
	 * @og.rev 3.9.0.5 (2008/11/27) POI3.2対応 引数の型変更(short⇒int)
	 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX)
	 *
	 * @param	sheet	EXCELのシートオブジェクト
	 * @param	row		行番号
	 * @param	col		列番号
	 *
	 * @return	セルの値
	 */
	@SuppressWarnings(value={"deprecation"})	// poi-3.15
	private String getValue( final HSSFSheet sheet,final int row, final int col ) {

		final HSSFRow  oRow  = sheet.getRow(row);
		if( oRow == null ) { return null; }
		final HSSFCell oCell = oRow.getCell(col);
		if( oCell == null ) { return null; }

		String strText = "";
		HSSFRichTextString richText ;
	//	final int nCellType = oCell.getCellType();			// 6.5.0.0 (2016/09/30) poi-3.12
	//	switch( nCellType ) {								// 6.5.0.0 (2016/09/30) poi-3.12
		switch( oCell.getCellTypeEnum() ) {					// 6.5.0.0 (2016/09/30) poi-3.15
	//		case HSSFCell.CELL_TYPE_NUMERIC:				// 6.5.0.0 (2016/09/30) poi-3.12
			case NUMERIC:									// 6.5.0.0 (2016/09/30) poi-3.15
					strText = getNumericTypeString( oCell );
					break;
	//		case HSSFCell.CELL_TYPE_STRING:					// 6.5.0.0 (2016/09/30) poi-3.12
			case STRING:									// 6.5.0.0 (2016/09/30) poi-3.15
	// POI3.0		strText = oCell.getStringCellValue();
					richText = oCell.getRichStringCellValue();
					strText =  richText.getString();
					if( debug ) { System.out.print( "String :" ); }
					break;
	//		case HSSFCell.CELL_TYPE_FORMULA:				// 6.5.0.0 (2016/09/30) poi-3.12
			case FORMULA:									// 6.5.0.0 (2016/09/30) poi-3.15
	//				strText = oCell.getCellFormula();
	// POI3.0		strText = oCell.getStringCellValue();
					richText = oCell.getRichStringCellValue();
					strText =  richText.getString();
					if( strText == null || strText.isEmpty() ) {
						strText = getNumericTypeString( oCell );
					}
					else {
						if( debug ) { System.out.print( "Formula:" ); }
					}
					break;
	//		case HSSFCell.CELL_TYPE_BOOLEAN:				// 6.5.0.0 (2016/09/30) poi-3.12
			case BOOLEAN:									// 6.5.0.0 (2016/09/30) poi-3.15
					strText = String.valueOf(oCell.getBooleanCellValue());
					if( debug ) { System.out.print( "Boolean:" ); }
					break;
	//		case HSSFCell.CELL_TYPE_BLANK :					// 6.5.0.0 (2016/09/30) poi-3.12
			case BLANK  :									// 6.5.0.0 (2016/09/30) poi-3.15
					if( debug ) { System.out.print( "Blank  :" ); }
					break;
	//		case HSSFCell.CELL_TYPE_ERROR :					// 6.5.0.0 (2016/09/30) poi-3.12
			case ERROR  :									// 6.5.0.0 (2016/09/30) poi-3.15
					if( debug ) { System.out.print( "Error  :" ); }
					break;
			default :
	//				if( debug ) { System.out.print( "Other " + nCellType + ":" ); }
					if( debug ) { System.out.print( "Other " + oCell.getCellTypeEnum() + ":" ); }	// 6.5.0.0 (2016/09/30) poi-3.15
				break;
		}

		return StringUtil.rTrim( strText );		// 3.8.0.9 (2005/10/17)
	}

	/**
	 * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。
	 *
	 * @og.rev 3.8.1.1 (2005/11/21) デバッグ用コメント修正
	 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
	 *   ※ 最新のPOIでは、org.apache.poi.ss.usermodel を使う事で、2003形式、2007形式に対応させます。
	 *      report パッケージは保守対象外なので、あえて修正しません。
	 * @og.rev 7.3.0.0 (2021/01/06) HSSFDateUtil(Deprecated) → DateUtil (poi-4.1.2)
	 *
	 * @param oCell Cellオブジェクト
	 *
	 * @return	数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。
	 */
	private String getNumericTypeString( final HSSFCell oCell ) {
		final String strText ;

		final double dval = oCell.getNumericCellValue() ;
	//	if( HSSFDateUtil.isCellDateFormatted( oCell ) ) {
	//		strText = DateSet.getDate( HSSFDateUtil.getJavaDate( dval ).getTime() , "yyyyMMddHHmmss" );

		if( DateUtil.isCellDateFormatted( oCell ) ) {				// 7.3.0.0 (2021/01/06) poi-4.1.2
			strText = DateSet.getDate( DateUtil.getJavaDate( dval ).getTime() , "yyyyMMddHHmmss" );
			if( debug ) { System.out.print( "Date   :" ); }
		}
		else {
			// 3.8.0.9 (2005/10/17) 数字について、NumberFormat を行います。
			if( numFormat == null ) {
				numFormat = NumberFormat.getInstance();
				if( numFormat instanceof DecimalFormat ) {
					((DecimalFormat)numFormat).applyPattern( "#.####" );
				}
			}
			strText = numFormat.format( dval );
			if( debug ) { System.out.print( "Numeric:" ); }
		}
		return strText ;
	}

	/**
	 * EXCEL をオープンした InputStream を閉じます。
	 */
	public void close() {
		Closer.ioClose( in );		// 4.0.0 (2006/01/31) close 処理時の IOException を無視
	}
}
