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


import java.io.IOException;
import java.util.Map;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBColumn;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.resource.CodeData;
import org.opengion.hayabusa.resource.ResourceManager;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;



/**
 * JSONからテーブルモデルへの変換を行うためのAbstractクラスです。
 * plugin.jsonの各クラスが実際の実装となります。
 * 
 * 内部処理的にはtableReaderと似たような形を取ります。
 * 
 * http://qiita.com/kenichi_nakamura/items/be73e37ec870e5845ed9
 * https://www.mkyong.com/java/how-to-convert-java-map-to-from-json-jackson/
 * http://www.hiihah.info/index.php?Java%EF%BC%9AJackson%E3%82%92%E3%81%A4%E3%81%8B%E3%81%A3%E3%81%A6JSON%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80
 *
 * @og.group 画面表示
 * @og.rev 5.9.21.0 (2017/6/2) 新規作成
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public abstract class AbstractJsonReader implements JsonReader {
	protected ObjectMapper mapper = new ObjectMapper();
	
	private ResourceManager resource = null;
	private int		maxRowCount	= HybsSystem.sysInt( "DB_MAX_ROW_COUNT" ) ;
	
	protected DBTableModel	table		= null;
	protected DBColumn[]	dbColumn	= null;
	
	private int		  skipRowCount	= 0;		//  データの読み飛ばし設定
	private boolean	  useRenderer	= false;	
	
	// 5.2.1.0 (2010/10/01) コードリソース毎のラベル逆引きマップ
	private Map<?,?>[] maps = null;				// ワイルドカードを指定

	private boolean	  useDebug	= false;		//  デバッグ情報の出力するかどうか
	
	/**
	 * JSON形式のテキストをパースしてMAPに変換します。
	 * keyはStringで、valueはObject型で持ちます。
	 *
	 * @return JSONをMapに分解したもの
	 */
	@SuppressWarnings("unchecked")
	protected Map<String, Object>  parse( final String jsonText ){
		Map<String, Object> objmap = null;
		try {
			//System.out.println(jsonText);
			objmap = mapper.readValue(jsonText, Map.class);
		}
		catch( JsonParseException ex ) {
			String errMsg = "パースエラー";
			throw new HybsSystemException( errMsg,ex );
		}
		catch( JsonMappingException ex ) {
			String errMsg = "マッピングエラー";
			throw new HybsSystemException( errMsg,ex );
		}
		catch( IOException ex ) {
			String errMsg = "読込みエラー";
			throw new HybsSystemException( errMsg,ex );
		}
		
		return objmap;
	}
	
	
	/**
	 * DBColumn オブジェクトをDBTable に設定します。
	 *
	 *
	 * @param names カラム名配列
	 */
	protected void setTableDBColumn( final String[] names ) {
		dbColumn = new DBColumn[names.length] ;	 // 3.5.4.5 追加
		
		// サイズ0の場合はinitする
		if( table.getColumnCount() == 0){
			table.init( names.length );
		}
		
		for( int i=0; i<names.length; i++ ) {
			DBColumn clm = resource.makeDBColumn( names[i] );
			table.setDBColumn( i,clm );
			dbColumn[i] = clm;	
		}
		
		if( useRenderer ) {
			maps = new Map<?,?>[names.length];		//  ワイルドカードを指定
			for( int i=0; i<names.length; i++ ) {
				CodeData cd = dbColumn[i].getCodeData();
				if( cd != null ) { maps[i] = cd.makeLabelMap(); }
				else             { maps[i] = null; }
			}
		}
	}
	
	/**
	 * DBTableModelオブジェクトに、１行分のデータを追加します。
	 *
	 *
	 * @param values １行分のデータ配列
	 */
	protected void setTableColumnValues( final String[] values ) {
		if( useRenderer ) {
			int clmSize = values.length;
			for( int clmNo=0; clmNo<clmSize; clmNo++ ) {
				if( maps[clmNo] != null ) {
					String val = values[clmNo];
					if( val == null ) { val = ""; }
					else {
						String tmp = (String)maps[clmNo].get( val );
						if( tmp != null ) { values[clmNo] = tmp; }
						else {
							int sp = val.indexOf( ':' );
							if( sp >= 0 ) {
								values[clmNo] = val.substring( 0,sp );
							}
						}
					}
				}
			}
		}

		table.addColumnValues( values );
	}
	
	/**
	 * 内部の DBTableModel を返します。
	 *
	 * @return	テーブルモデル
	 */
	public DBTableModel getDBTableModel() {
		return table;
	}
	
	/**
	 * リソースマネージャーをセットします。
	 * これは、言語(ロケール)に応じた DBColumn をあらかじめ設定しておく為に
	 * 必要です。
	 * リソースマネージャーが設定されていない、または、所定のキーの DBColumn が
	 * リソースに存在しない場合は、内部で DBColumn オブジェクトを作成します。
	 *
	 *
	 * @param  resource リソースマネージャー
	 */
	public void setResourceManager( final ResourceManager resource ) {
		this.resource = resource;
	}
	
	/**
	 * DBTableModelのデータとして登録する最大件数をこの値に設定します
	 *		(初期値:DB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。
	 * サーバーのメモリ資源と応答時間の確保の為です。
	 *
	 * @return  最大検索件数
	 */
	public int getMaxRowCount() {
		return maxRowCount;
	}
	
	/**
	 * DBTableModelのデータとして登録する最大件数をこの値に設定します
	 *		(初期値:DB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。
	 * サーバーのメモリ資源と応答時間の確保の為です。
	 *
	 * @param   maxRowCount 最大検索件数
	 */
	public void setMaxRowCount( final int maxRowCount ) {
		this.maxRowCount = ( maxRowCount > 0 ) ? maxRowCount : Integer.MAX_VALUE ;
	}
	
	/**
	 * データの読み始めの初期値を取得します。
	 *
	 * データの読み始めの初期値を指定します。
	 * 先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
	 * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
	 * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
	 * ＃NAME属性や、columns 属性は、有効です。
	 *
	 *
	 * @return	読み始めの初期値
	 */
	public int getSkipRowCount() {
		return skipRowCount ;
	}
	
	/**
	 * データの読み飛ばし件数を設定します。
	 *
	 * @param	count 読み始めの初期値
	 */
	public void setSkipRowCount( final int count ) {
		skipRowCount = count;
	}
	
	/**
	 * 読取処理でラベルをコードリソースに逆変換を行うかどうかを指定します。
	 *
	 *
	 * @param	useRenderer	コードリソースのラベル変換を行うかどうかを指定
	 */
	public void setUseRenderer( final boolean useRenderer ) {
		this.useRenderer = useRenderer;
	}
	
	/**
	 * デバッグ情報を出力するかどうかを指定します。
	 *
	 *
	 * @param	useDebug	デバッグ情報を出力するかどうかを指定
	 */
	public void setDebug( final boolean useDebug ) {
		this.useDebug = useDebug;
	}

	/**
	 * デバッグ情報を出力するかどうかを取得します。
	 *
	 *
	 * @return	デバッグ情報を出力するかどうか(true:する/false:しない)
	 */
	protected boolean isDebug() {
		return useDebug ;
	}
	
	/**
	 * １行のMapデータを テーブルモデルにセットするための配列にします。
	 *
	 * names配列も与えて、その順に読みます。
	 *
	 *
	 * @return	分割された文字列配列
	 */
	protected String[] readData( final Map<String,Object> mapdata, final String[] names ) {
		String[] rtnData = new String[ names.length ];
		
		for( int i=0; i<names.length; i++){
			rtnData[i] = (String)mapdata.get( names[i] );
		}

		return rtnData;
	}
}
