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

import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.model.DataModel;
import org.opengion.fukurou.model.NativeType;
import org.opengion.fukurou.util.StringUtil;
import static org.opengion.fukurou.system.HybsConst.CR;				// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring

import java.util.Arrays;

/**
 * LineModel は、データの１行分を管理する為の TableModel と類似の実装クラスです。
 *
 * データの１行分を LineModel に割り当てます。
 * カラム番号は、0 から始まります。カラム名よりカラム番号を求める場合に、
 * 存在しない場合は、-1 を返します。
 * カラム番号が -1 の場合は、処理を行いません。
 *
 * 注意：このクラスは、同期処理されていません。
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class LineModel implements DataModel<Object> {		// 4.3.3.6 (2008/11/15) Generics警告対応
	/** タブセパレータ  */
	private static final char TAB = '\t';		// 6.0.2.5 (2014/10/31) タブ区切り文字を char 化します。

	private	String[]	names	;
	private	Object[]	data	;
	private	int			dtSize	;
	private	int			rowNo	= -1;

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

	/**
	 * このオブジェクトを初期化します。
	 * 指定の引数分の内部配列を作成します。
	 *
	 * @param   columnCount カラム数
	 */
	public void init( final int columnCount ) {
		if( columnCount <= 0 ) {
			final String errMsg = "内部配列の数量が 0か、負です。count=[" + columnCount + "]";
			throw new OgRuntimeException( errMsg );
		}
		dtSize	= columnCount;
		names	= new String[dtSize];
		data	= new Object[dtSize];
	}

	/**
	 * カラム名配列を指定して、このオブジェクトを初期化します。
	 *
	 * @param   clmNms カラム名配列(可変長引数)
	 */
	public void init( final String... clmNms ) {
		if( clmNms == null || clmNms.length == 0 ) {		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
			final String errMsg = "カラム名配列が、指定されていません。";
			throw new OgRuntimeException( errMsg );
		}
		dtSize	= clmNms.length;
		names	= new String[dtSize];
		System.arraycopy( clmNms,0,names,0,dtSize );
		data	= new Object[dtSize];
	}

	/**
	 * 名前をセットします。
	 * 指定のカラム番号が、-1 の場合は、なにもしません。
	 *
	 * @param   clmNo	 値が変更されるカラム番号
	 * @param   key	 新しい名前
	 */
	public void setName( final int clmNo,final String key ) {
		if( clmNo >= 0 ) { names[clmNo] = key; }
	}

	/**
	 * カラム番号に対するカラム名を取得します。
	 * 指定のカラム番号が、-1 の場合は、null を返します。
	 *
	 * @param   clmNo  カラム番号 最初のカラム番号は0、2番目のカラム番号は 1、などとする。
	 *
	 * @return  カラム名
	 */
	public String getName( final int clmNo ) {
		return clmNo >= 0 ? names[clmNo] : null ;			// 6.0.2.5 (2014/10/31) refactoring
	}

	/**
	 * カラム名配列を返します。
	 * 配列オブジェクトは、clone されたコピーを返しますので、
	 * 内容を書き換えたい場合は、setName() メソッドを使用してください。
	 *
	 * @return	カラム名配列
	 * @og.rtnNotNull
	 */
	@Override
	public String[] getNames() {
		return names.clone();
	}

	/**
	 * column に対応した 値を登録します。
	 * 指定のカラム番号が、-1 の場合は、なにもしません。
	 *
	 * @param   clmNo	値が変更されるカラム番号
	 * @param   value	新しい値(nullも可)
	 */
	public void setValue( final int clmNo,final Object value ) {
		if( clmNo >= 0 ) { data[clmNo] = value; }
	}

//	/**
//	 * column および row にあるセルのオブジェクト値を設定します。
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) インターフェースの見直しにより、追加
//	 *
//	 * @param   value	新しい値。null も可
//	 * @param   rowNo	値が変更される行
//	 * @param   clmNo	値が変更される列
//	 */
//	public void setValueAt( final String value, final int rowNo, final int clmNo ) {
//		setValue( clmNo,value );		// 順番注意。
//	}

	/**
	 * column にあるセルの属性値をObjectに変換して返します。
	 * 指定のカラム番号が、-1 の場合は、null を返します。
	 *
	 * @param   clmNo  値が参照されるカラム番号
	 *
	 * @return  指定されたセルの値 Object
	 */
	public Object getValue( final int clmNo ) {
		return clmNo >= 0 ? data[clmNo] : null ;			// 6.0.2.5 (2014/10/31) refactoring
	}

	/**
	 * row および clm にあるセルの属性値をStringに変換して返します。
	 *
	 * @og.rev 6.3.2.0 (2015/07/10) LineModelで、Formatter処理できるように、対応します。
	 *
	 * @param   rowNo     値が参照される行
	 * @param   clmNo     値が参照される列
	 *
	 * @return  指定されたセルの値
	 */
	@Override
//	public String getValue( final int row, final int clm) {
	public Object getValue( final int rowNo, final int clmNo ) {
//		final String errMsg = "このクラスでは実装されていません。";
//		throw new UnsupportedOperationException( errMsg );
		return getValue( clmNo ) ;		// 6.3.2.0 (2015/07/10)
	}

	/**
	 * 属性値配列をセットします。
	 * このメソッドでは、カラム名配列は更新しません。配列数が異なる場合や、
	 * 属性値配列が null の場合は設定できません。
	 * 設定は、配列オブジェクトのコピーをセットしますので、登録元の配列オブジェクトを
	 * 書き換えた場合でも、このオブジェクトの内部値は変更されませんので、
	 * 副作用を期待したコーディングは、行わないで下さい。
	 * 注意：値オブジェクト配列自身はコピーしますが、個々のオブジェクトそのものは、
	 * arraycopy しているだけです。個々のオブジェクトの状態変化に対しては、
	 * 各クライアント側で対応が必要です。
	 *
	 * @param	values	セットする値配列
	 * @param	rno		処理中の行番号
	 */
	@Override
	public void setValues( final Object[] values, final int rno ) {
		if( values == null ) {
			final String errMsg = "値配列が、 null です。row=[" + rno + "]";
			throw new OgRuntimeException( errMsg );
		}
		else if( names.length != values.length ) {
			final String errMsg = "カラム名配列と異なる要素の属性値配列は登録できません。" + CR
							+ " names.length=[" + names.length + "],"
							+ " values.length[" + values.length + "],"
							+ " row=[" + rno + "]" 	+ CR
							+ " names=" + StringUtil.array2csv( names )
							+ " values=" + Arrays.toString( values ) ;		// 5.1.8.0 (2010/07/01) errMsg 修正
			throw new OgRuntimeException( errMsg );
		}

		rowNo = rno;
		System.arraycopy( values,0,data,0,values.length );
	}

	/**
	 * 属性値を配列で返します。
	 * 配列オブジェクトは、clone されたコピーを返しますので、
	 * 内容を書き換えたい場合は、setValue() メソッドを使用してください。
	 *
	 * @return  指定されたセルの属性値
	 * @og.rtnNotNull
	 */
	public Object[] getValues() {
		return data.clone();
	}

	/**
	 * row にあるセルの属性値を配列で返します。
	 *
	 * @og.rev 6.3.2.0 (2015/07/10) LineModelで、Formatter処理できるように、対応します。
	 *
	 * @param   row     値が参照される行
	 *
	 * @return  指定されたセルの属性値
	 */
	@Override
//	public String[] getValues( final int row ) {
	public Object[] getValues( final int row ) {
		final String errMsg = "このクラスでは実装されていません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * 行データモデル属性に、処理中の行番号情報を設定します。
	 * 一般に、setValue( int clm, Object value ) との併用時に使用します。
	 *
	 * @param	rno	処理中の行番号
	 */
	public void setRowNo( final int rno ) {
		rowNo = rno;
	}

	/**
	 * 行データモデル属性の、処理中の行番号情報を返します。
	 *
	 * @return  処理中の行番号
	 */
	public int getRowNo() {
		return rowNo;
	}

	/**
	 * columnName 名称に対応する カラム番号を返します。存在しない場合は、-1 を返します。
	 *
	 * @param   columnName  値が参照されるカラム番号
	 *
	 * @return  指定されたセルのカラム番号(存在しない場合は、-1)
	 */
	@Override
	public int getColumnNo( final String columnName ) {
		int clmNo = -1;
		if( columnName != null ) {
			for( int i=0; i<dtSize; i++ ) {
				if( columnName.equalsIgnoreCase( names[i] ) ) {
					clmNo = i;
					break;
				}
			}
		}

		return clmNo;
	}

//	/**
//	 * カラム名配列に対応する カラム番号配列を返します。
//	 *
//	 * これは、#getColumnNo( String,) に対する 複数のカラム名を検索した
//	 * 場合と同じです。
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) 新規追加
//	 *
//	 * @param	clmNms 	値が参照されるカラム名配列(可変長引数)
//	 *
//	 * @return  指定されたセルのカラム番号配列。
//	 * @og.rtnNotNull
//	 */
//	public int[] getColumnNos( final String... clmNms ) {
//		final int[] clmNos = new int[clmNms.length];
//
//		for( int i=0; i<clmNms.length; i++ ) {
//			clmNos[i] = getColumnNo( clmNms[i] );
//		}
//
//		return clmNos ;
//	}

	/**
	 * データテーブル内の列の数を返します。
	 *
	 * @return  モデルの列数
	 */
	public int size() {
		return dtSize ;
	}

	/**
	 * この内部データの名前配列を連結した文字列で、出力します。
	 *
	 * @return	内部データの名前配列を連結した文字列
	 */
	public String nameLine() {
		String rtn = "No Data";

		if( dtSize > 0 ) {
			final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
			buf.append( "#Name" );
			for( int clm=0; clm<dtSize; clm++ ) {
				buf.append( TAB );
				buf.append( names[clm] );
			}
			rtn = buf.toString() ;
		}
		return rtn ;
	}

	/**
	 * この内部データの名前配列を連結した文字列で、出力します。
	 *
	 * @return	名前配列を連結した文字列
	 */
	public String dataLine() {
		String rtn = "No Data";

		if( dtSize > 0 ) {
			final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
			buf.append( rowNo );
			for( int clm=0; clm<dtSize; clm++ ) {
				buf.append( TAB );
				buf.append( data[clm] );
			}
			rtn = buf.toString() ;
		}
		return rtn ;
	}

	/**
	 * この内部データを見える形で出力します。
	 *
	 * @return	内部データを見える形
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return nameLine() + CR + dataLine() ;
	}

	/**
	 * clm のNativeタイプを返します。
	 * Nativeタイプはorg.opengion.fukurou.model.NativeTypeで定義されています。
	 *
	 * @og.rev 4.1.1.2 (2008/02/28) 新規追加
	 * @og.rev 5.1.8.0 (2010/07/01) NativeType#getType(String) のメソッドを使用するように変更。
	 *
	 * @param  clm      値が参照される列
	 *
	 * @return Nativeタイプ
	 * @see org.opengion.fukurou.model.NativeType
	 */
	@Override
	public NativeType getNativeType( final int clm ) {
		return NativeType.getType( data[clm].toString() );
	}
}
