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

/**
 * 雛形EXCELの {&#064;カラム} 解析情報 を管理するローカルクラスです。
 *
 * このクラスは、雛形EXCELより取得した、{&#064;カラム} および、{&#064;カラム_枝番}より
 * カラム、行番号、列番号、枝番を取り出し、セルと１対１で管理する為のクラスです。
 * 枝番が付かないカラムは、ヘッダー情報になる為、枝番を、－１ で設定します。
 * シート単位に呼び出す必要があります。
 *
 * ｢注意：このクラスは、equals と一致しない自然な順序を持っています。｣
 *
 * @og.group 帳票システム
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class ExcelLayoutData implements Comparable<ExcelLayoutData> {
	private final int		edbn	;
	private final String	key		;
	private final String	clm		;
	private final int		rowNo	;
	private final short		colNo	;

	private final String	uniqKey ;
	private final int		hcode	;			// 6.4.1.1 (2016/01/16) PMD refactoring. Field hashcode has the same name as a method

	/**
	 * コンストラクター
	 *
	 * 雛形EXCELの{&#064;カラム}を含むセルの値と、行列番号を設定します。
	 * 整合性チェックの条件は、先頭が "{&#064;" で、最後が、"}" で、文字列の途中に、
	 * "{&#064;" が含まれていない場合のみとします。
	 *
	 * 内部で、カラムと枝番に分解します。
	 *
	 * @param	inkey	処理カラム({&#064;カラム}形式)
	 * @param	rowNo	行番号
	 * @param	colNo	列番号
	 */
	public ExcelLayoutData( final String inkey , final int rowNo ,final short colNo ) {
		this.key   = inkey.trim();
		this.rowNo = rowNo;
		this.colNo = colNo;

		if( ! key.startsWith( "{@" ) || !key.endsWith( "}" ) ) {
			final String errMsg = "指定のセルのカラム定義が不正です。"
						+ "row,col=[" + rowNo + "," + colNo + "] , key=[" + key + "]" ;
			throw new IllegalArgumentException( errMsg );
		}

		String tempClm = key.substring( 2,key.length()-1 );	// {@AAA_X} を AAA_X に変換する。
		final int idx = tempClm.lastIndexOf( '_' );
		int tempEdbn = -1;

		if( idx > 0 ) {
			try {
				tempEdbn = Integer.parseInt( tempClm.substring( idx+1 ) );
				tempClm  = tempClm.substring( 0,idx );
			}
			catch( final NumberFormatException ex ) {	// ヘッダーにアンダーバーが使用されているケース
//				final String errMsg2 = "ヘッダーにアンダーバーが使用されています。[" + tempClm + "]" ;
				final String errMsg2 = "ヘッダーにアンダーバーが使用されています。[" + tempClm + "] ERR=" + ex.getMessage() ;		// 6.9.7.0 (2018/05/14) PMD
				LogWriter.log( errMsg2 );
			}
		}

		edbn = tempEdbn;
		clm  = tempClm;

		if( edbn < 0 ) {
			uniqKey = clm + ":[" + rowNo + "," + colNo + "]" ;
		}
		else {
			uniqKey = clm + "_" + edbn + ":[" + rowNo + "," + colNo + "]" ;
		}
		hcode = uniqKey.hashCode() ;
	}

	/**
	 * 内部コンストラクター
	 *
	 * 雛形EXCELの{&#064;カラム}を含むセルの値と、行列番号を設定します。
	 * 内部で、カラムと枝番に分解します。
	 *
	 * @param	other		ExcelLayoutDataオブジェクト
	 * @param	newRowNo	行番号
	 * @param	newEdbn		列番号
	 */
	private ExcelLayoutData( final ExcelLayoutData other , final int newRowNo, final int newEdbn ) {
		key   = other.key;
		rowNo = newRowNo;
		colNo = other.colNo;

		edbn = newEdbn;
		clm  = other.clm;

		if( edbn < 0 ) {
			uniqKey = clm + ":[" + rowNo + "," + colNo + "]" ;
		}
		else {
			uniqKey = clm + "_" + edbn + ":[" + rowNo + "," + colNo + "]" ;
		}
		hcode = uniqKey.hashCode() ;
	}

	/**
	 * 枝番を取得します。
	 *
	 * @return	ハッシュコード
	 */
	public int getEdbn() { return edbn ; }

	/**
	 * カラム名を取得します。
	 *
	 * @return	カラム名
	 */
	public String getClm() { return clm ; }

	/**
	 * 行番号を取得します。
	 *
	 * @return	行番号
	 */
	public int getRowNo() { return rowNo ; }

	/**
	 * 列番号を取得します。
	 *
	 * @return	列番号
	 */
	public short getColNo() { return colNo ; }

	/**
	 * 登録時のオリジナルのキー({&#064;カラム_枝番})を取得します。
	 *
	 * @return	オリジナルのキー
	 */
	public String getKey() { return key ; }

	/**
	 * 行番号と枝番が異なる新しい ExcelLayoutData を返します。
	 *
	 * @param	newRowNo	行番号
	 * @param	newEdbn		枝番
	 *
	 * @return	新しいExcelLayoutData
	 * @og.rtnNotNull
	 */
	public ExcelLayoutData copy( final int newRowNo, final int newEdbn ) {
		return new ExcelLayoutData( this,newRowNo,newEdbn );
	}

	/**
	 * このオブジェクトのハッシュコードを取得します。
	 * 内部文字列(uniqKey)のハッシュコードと同じ値です。
	 *
	 * @return	ハッシュコード
	 */
	@Override
	public int hashCode() { return hcode ; }

	/**
	 * この文字列と指定されたオブジェクトを比較します。
	 *
	 * 引数が null でなく、このオブジェクトと同じ内部文字列(uniqKey)を持つ
	 * ExcelLayoutData オブジェクトである場合にだけ、結果は true になります。
	 *
	 * @param	object	比較するオブジェクト
	 *
	 * @return	Objectが等しい場合は true、そうでない場合は false
	 */
	@Override
	public boolean equals( final Object object ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return object instanceof ExcelLayoutData && uniqKey.equals( ((ExcelLayoutData)object).uniqKey ) ;

	}

	/**
	 * 自然比較メソッド
	 * インタフェース Comparable の 実装です。
	 * edbn 、clm 、rowNo 、colNo の順で比較します。
	 *
	 * @param	other	比較対象のObject
	 *
	 * @return	このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
	 */
	@Override
	public int compareTo( final ExcelLayoutData other ) {
		if( uniqKey.equals( other.uniqKey ) ) { return 0; }

		if( edbn  != other.edbn ) { return edbn - other.edbn; }
		if( ! clm.equals( other.clm ) ) { return clm.compareTo( other.clm ); }
		if( rowNo  != other.rowNo ) { return rowNo - other.rowNo; }
		return colNo - other.colNo;
	}

	/**
	 * このクラスの文字列表現を返します。
	 *
	 * 内部文字列(uniqKey)と同じ値です。
	 * ヘッダー：clm + ":[" + rowNo + "," + colNo + "]"
	 * 明細    ：clm + "_" + edbn + ":[" + rowNo + "," + colNo + "]"
	 *
	 * @return	文字列表現
	 */
	@Override
	public String toString() {
		return uniqKey;
	}
}
