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

import java.io.PrintWriter;
import java.util.List;
import java.util.Map ;
import java.util.LinkedHashMap ;

import org.odftoolkit.odfdom.OdfFileDom;
import org.odftoolkit.odfdom.doc.office.OdfOfficeAutomaticStyles;
import org.odftoolkit.odfdom.doc.office.OdfOfficeMasterStyles;
import org.odftoolkit.odfdom.doc.style.OdfStyle;
import org.odftoolkit.odfdom.doc.style.OdfStyleFooter;
import org.odftoolkit.odfdom.doc.style.OdfStyleFooterLeft;
import org.odftoolkit.odfdom.doc.style.OdfStyleHeader;
import org.odftoolkit.odfdom.doc.style.OdfStyleHeaderLeft;
import org.odftoolkit.odfdom.doc.style.OdfStyleMasterPage;
import org.odftoolkit.odfdom.doc.style.OdfStylePageLayout;
import org.odftoolkit.odfdom.doc.style.OdfStyleParagraphProperties;
import org.odftoolkit.odfdom.doc.style.OdfStyleTableCellProperties;
import org.odftoolkit.odfdom.doc.table.OdfTableCell;
import org.odftoolkit.odfdom.doc.table.OdfTableRow;
import org.odftoolkit.odfdom.dom.element.style.StylePageLayoutPropertiesElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.w3c.dom.Node;

import org.opengion.fukurou.model.NativeType;
import org.opengion.fukurou.util.HybsEntry;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBColumn;
import org.opengion.hayabusa.db.DBTableModel;

/**
 * Calcファイルの書き出しクラスです。
 *
 * このクラスでは、通常の出力クラスと異なり、データ部分には、データではなく、
 * {&#064;カラム名_行番号}が出力されます。
 *
 * この出力結果は、通常、Calc帳票システムの雛形を作成するための、元情報として
 * 利用することを想定しています。
 *
 * writeTableParam タグで key="Size" を指定できます。これは、作成するレコードのデータ件数です。
 * 初期値は、２５件です。
 *
 * @og.group ファイル出力
 *
 * @version  5.0
 * @author	 Hiroki Nakamura
 * @since    JDK6.0,
 */
public class TableWriter_CalcDef extends TableWriter_Calc {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.4.2.0 (2016/01/29)" ;

	private static final int INIT_DATA_SIZE	= 25;

	private int dataSize = INIT_DATA_SIZE;

	// 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。
	/** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
	private static final Map<String,String> KEYS_MAP = new LinkedHashMap<>();
	static {
		KEYS_MAP.put( "SIZE" , "レコードのデータ件数(初期値:25)" );
	}

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public TableWriter_CalcDef() {
		super();		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
	}

	/**
	 * PrintWriter に DBTableModelのテーブル情報を書き込みます。
	 * このクラスでは，データを ダブルコーテーション(")で囲みます。
	 * PrintWriter に DBTableModelのテーブル情報を書き込みます。
	 *
	 * @og.rev 5.1.8.0 (2010/07/01) コメント出力(CalcDefAno)追加による対応
	 * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
	 *
	 * @param	table  DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	@Override
	protected void writeData( final DBTableModel table,final PrintWriter writer ) {
		for( int r=0; r<dataSize; r++ ) {
			final OdfTableRow row = new OdfTableRow( contentDom );

			if( useNumber ) {
				final String val = "ROWNO_" + r ;
				row.appendCell( createTextCell( contentDom, val, null, true, true ) );
			}

			// 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
//			boolean[] cellType = new boolean[numberOfColumns];
			final boolean[] cellType = new boolean[numberOfColumns];
			for( int i=0; i<numberOfColumns; i++ ) {
				final int clm = clmNo[i];
				if( clm < 0 ) { continue; }			// 6.0.1.2 (2014/08/08) カラム飛ばし

				final NativeType nativeType = dbColumn[clm].getNativeType();
				switch( nativeType ) {
					case INT    :
					case LONG   :
					case DOUBLE :
						cellType[i] = true ;
						break;
					case STRING :
					case CALENDAR :
					default :
						cellType[i] = false ;
						break;
				}
			}

			for( int i=0; i<numberOfColumns; i++ ) {
				final int clm = clmNo[i];
				if( clm < 0 ) { continue; }			// 6.0.1.2 (2014/08/08) カラム飛ばし

				final String val = table.getColumnName( clm ) + "_" + r ;
				row.appendCell( createTextCell( contentDom, val, table.getDBColumn( clm ), cellType[i], false ) );
				row.setStyleName( "ro1" );
			}

			sheet.appendRow( row );
		}
	}

	/**
	 * テキストコンテンツ用のセルを生成する。
	 *
	 * @og.rev 5.1.8.0 (2010/07/01) コメント出力(CalcDefAno)追加による対応
	 *
	 * @param	contentDom	OdfFileDomオブジェクト
	 * @param	content		コンテンツ
	 * @param	col			DBColumnオブジェクト
	 * @param	isCellTypeNumber	[true:数字型/false:文字型]
	 * @param	isNumberList		[true:数字リスト=999/false:通常]
	 *
	 * @return	テキストコンテンツ用のセル
	 */
	protected OdfTableCell createTextCell( final OdfFileDom contentDom, final String content, final DBColumn col, final Boolean isCellTypeNumber, final Boolean isNumberList ) {
		final OdfTableCell cell = super.createTextCell( contentDom, "{@" + content + "}", false, isNumberList );
		if( isNumberList ) {
			final OdfStyle style = contentAutoStyles.newStyle( OdfStyleFamily.TableCell );
			style.setProperty( OdfStyleTableCellProperties.TextAlignSource, "fix" );
			style.setProperty( OdfStyleTableCellProperties.RepeatContent, "false" );
			style.setProperty( OdfStyleParagraphProperties.TextAlign, "end" );
			style.setProperty( OdfStyleParagraphProperties.MarginRight, "0cm" );
			final String cellStyleName = style.getStyleNameAttribute();
			cell.setStyleName( cellStyleName );
		}

		return cell;
	}

	/**
	 * デフォルトで用意されているStylesを調整します。
	 *
	 * ヘッダー表示しない
	 * フッターを数字のみにして、右端に出す
	 * ページレイアウトを横にする
	 * ページの設定を、拡大縮小モードを「印刷範囲をページ数に合わせる」に設定(1ページ)
	 */
	@Override
	protected void resetAutoStylesAndMasterStyles() {
		try {
			// AutomaticStyles調整
			final OdfOfficeAutomaticStyles oas = wb.getStylesDom().getAutomaticStyles();
			final OdfStylePageLayout spl = oas.getPageLayout( "pm1" );
			spl.setProperty( StylePageLayoutPropertiesElement.PageHeight, "21.00cm" );
			spl.setProperty( StylePageLayoutPropertiesElement.PageWidth , "29.70cm" );
			spl.setProperty( StylePageLayoutPropertiesElement.PrintOrientation, "landscape" );
			spl.setProperty( StylePageLayoutPropertiesElement.ScaleToPages, "1" );

			// MasterStyles調整
			final OdfOfficeMasterStyles oms = wb.getOfficeMasterStyles();
			final OdfStyleMasterPage smp = oms.getMasterPage( "Default" );

			// MasterPageのデフォルトで用意されているノードを削除
			Node fcd = smp.getFirstChild();
			while( fcd != null ) {
				smp.removeChild( fcd );
				fcd = smp.getFirstChild();
			}

			// MasterPageのノードを定義と追加
			final OdfStyleHeader		sh	= new OdfStyleHeader(     wb.getStylesDom() );
			final OdfStyleHeaderLeft	shl	= new OdfStyleHeaderLeft( wb.getStylesDom() );
			sh.setStyleDisplayAttribute(  false );
			shl.setStyleDisplayAttribute( false );
			smp.appendChild( sh  );
			smp.appendChild( shl );
			final OdfStyleFooter		sf	= new OdfStyleFooter(     wb.getStylesDom() );
			final OdfStyleFooterLeft	sfl	= new OdfStyleFooterLeft( wb.getStylesDom() );
			sf.newStyleRegionRightElement().newTextPElement().newTextPageNumberElement();
			sfl.setStyleDisplayAttribute( false );
			smp.appendChild( sf  );
			smp.appendChild( sfl );
		}
//		catch( final Exception ex ) {
		catch( final Throwable th ) {		// 6.4.9.1 (2016/08/05)
			final String errMsg = "AutomaticStylesとMasterStyles調整できません";
			throw new HybsSystemException( errMsg, th );
		}
	}

	/**
	 * パラメーターリストをセットします。
	 * 内部は、HybsEntry クラスを持っています。
	 * 引数が、null の場合は、何もしません。
	 *
	 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。
	 *
	 * @param	listParam	HybsEntryリスト
	 */
	@Override
	public void setParam( final List<HybsEntry> listParam ) {
		if( listParam != null && !listParam.isEmpty() ) {
			for( final HybsEntry entry : listParam ) {

				final String key = entry.getKey() ;		// 5.6.6.1 (2013/07/12) keys の整合性チェック
				checkParam( key , KEYS_MAP );

				final String val = entry.getValue() ;		// 5.6.6.1 (2013/07/12) val を先に取得
				// 6.0.0.1 (2014/04/25) These nested if statements could be combined
				if( val != null && val.length() > 0 && "Size".equalsIgnoreCase( key ) ) {
					dataSize = Integer.parseInt( val );
				}
			}
		}
	}
}
