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

	/**
	 * コンストラクター
	 *
	 * 雛形EXCELの{&#064;カラム}を含むセルの値と、行列番号を設定します。
	 * 整合性チェックの条件は、先頭が "{&#064;" で、最後が、"}" で、文字列の途中に、
	 * "{&#064;" が含まれていない場合のみとします。
	 *
	 * 内部で、カラムと枝番に分解します。
	 *
	 * @param inkey String
	 * @param rowNo int
	 * @param colNo int
	 */
	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( "}" ) ) {
			String errMsg = "指定のセルのカラム定義が不正です。"
						+ "row,col=[" + rowNo + "," + colNo + "] , key=[" + key + "]" ;
			throw new IllegalArgumentException( errMsg );
		}

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

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

		edbn = tempEdbn;
		clm  = tempClm;

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

	/**
	 * 内部コンストラクター
	 *
	 * 雛形EXCELの{&#064;カラム}を含むセルの値と、行列番号を設定します。
	 * 内部で、カラムと枝番に分解します。
	 *
	 * @param other  ExcelLayoutData
	 * @param newRowNo  int
	 * @param newEdbn   int
	 */
	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 + "]" ;
		}
		hashcode = uniqKey.hashCode() ;
	}

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

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

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

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

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

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

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

	/**
	 * この文字列と指定されたオブジェクトを比較します。
	 *
	 * 引数が null でなく、このオブジェクトと同じ内部文字列(uniqKey)を持つ
	 * ExcelLayoutData オブジェクトである場合にだけ、結果は true になります。
	 *
	 * @param object Object
	 * @return boolean Objectが等しい場合は true、そうでない場合は false
	 */
	@Override
	public boolean equals( final Object object ) {
		if( object instanceof ExcelLayoutData ) {
			return uniqKey.equals( ((ExcelLayoutData)object).uniqKey ) ;
		}
		return false ;
	}

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

//		if( object instanceof ExcelLayoutData ) {
//			ExcelLayoutData other = (ExcelLayoutData)object ;
			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;
//		}
//		throw new ClassCastException();
	}

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