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

import java.util.function.Function;
import java.util.function.IntFunction;						// 6.4.4.2 (2016/04/01)
import java.util.function.Supplier;							// 6.4.4.2 (2016/04/01)

/**
 * 内部にStringBuilderを持った、文字列連結クラスです。
 *
 * 文字列連結時に、取り込む／取り込まないの判断を行う、boolean 付きの
 * appendIf メソッドや、null値を無視する append など、用意しています。
 *
 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
 * @og.rev 6.4.5.0 (2016/04/08) CharSequenceインタフェースの追加
 *
 * @og.group その他
 *
 * @version  6.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public final class OgBuilder implements CharSequence {
	private final StringBuilder buf = new StringBuilder( HybsConst.BUFFER_MIDDLE );

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 */
	public OgBuilder() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * 引数の可変長文字列を追加する appendです。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、追加しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param arys	追加する可変長CharSequence
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder append( final CharSequence... arys ) {
		if( arys != null ) {
			for( final CharSequence str : arys ) {
				if( str != null ) { buf.append( str ); }
			}
		}

		return this;
	}

	/**
	 * 連結文字列を、使用して、可変長引数のCharSequenceを連結して返します。
	 * 連結文字列が、null の場合は、CharSequenceをそのまま連結していきます。
	 * 連結するCharSequenceが null の場合は、連結しません。
	 * 連結文字列は、一番最後は出力されません。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 新規追加
	 *
	 * @param	delimiter	連結文字列
	 * @param	arys	連結する可変長CharSequence
	 *
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder join( final String delimiter , final CharSequence... arys ) {
		if( delimiter == null ) { return append( arys ); }

		if( arys != null ) {
			boolean isAdd = false;
			for( final CharSequence str : arys ) {
				if( str != null && str.length() > 0 ) {
					buf.append( str ).append( delimiter );
					isAdd = true;
				}
			}
			if( isAdd ) { buf.setLength( buf.length()-delimiter.length() ); }	// 最後に追加した delimiter を削除。
		}

		return this;
	}

	/**
	 * 引数の可変長文字列を内部バッファーから削除します。
	 *
	 * 引数の可変長文字列の個々の文字列が null の場合は、なにもしません。
	 * また、内部バッファーに存在しない場合も、何もしません。
	 *
	 * ※ 削除の指定は、CharSequenceではなく文字列です。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 新規追加
	 *
	 * @param arys	削除する可変長文字列
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder delete( final String... arys ) {
		if( arys != null ) {
			for( final String str : arys ) {
				if( str != null ) {
					final int len = str.length();		// 終了の位置を求めるのに使う。
					int st;
					while( ( st = buf.indexOf( str ) ) >= 0 ) {
						buf.delete( st , st+len );
					}
				}
			}
		}

		return this;
	}

	/**
	 * 引数の可変長CharSequenceを追加し、最後に改行コードを追加する appendです。
	 *
	 * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
	 * 引数がnull値の場合は、改行コードのみ追加されます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param arys	追加する文字列可変長(nullは追加しません)
	 * @return	自分自身
	 * @see		#append( CharSequence... )
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCR( final CharSequence... arys ) {
		return append( arys ).append( HybsConst.CR );
	}

	/**
	 * 引数の可変長CharSequenceを追加する appendです。
	 *
	 * 引数の可変長CharSequenceの個々のCharSequenceの中に、一つでも null の場合は、
	 * すべて追加しません。
	 * 例えば、key="AAAA" のような文字列を構築したい場合、AAAA 部分が、nullの
	 * 場合は、key= の箇所も出しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 * @og.rev 6.9.0.2 (2018/02/13) not null の対象に、ゼロ文字列も含むようにします。
	 *
	 * @param arys	追加する可変長CharSequence
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendNN( final CharSequence... arys ) {
		if( arys != null && arys.length > 0 ) {
			final StringBuilder tmp = new StringBuilder();
			for( final CharSequence str : arys ) {
//				if( str == null ) { return this; }		// 一つでも null があれば、すぐに抜ける。
				// 6.9.0.2 (2018/02/13) 一つでも nullか、ゼロ文字列 があれば、すぐに抜ける。
				if( str == null || str.length() == 0 ) { return this; }
				else { tmp.append( str ); }
			}
			buf.append( tmp );
		}
		return this;
	}

	/**
	 * 文字列を追加するかどうか判定するフラグ付きのappendメソッドです。
	 *
	 * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param arys	追加する可変長CharSequence
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendIf( final boolean flag , final CharSequence... arys ) {
		if( flag && arys != null ) {
			for( final CharSequence str : arys ) {
				if( str != null ) { buf.append( str ); }
			}
		}

		return this;
	}

	/**
	 * 引数の可変長CharSequenceを追加し、最後に改行コードを追加する appendです。
	 *
	 * 引数の可変長CharSequenceの個々のCharSequenceが null の場合は、追加しません。
	 * 引数がnull値の場合は、改行コードのみ追加されます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param flag	文字列を追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param arys	追加する可変長CharSequence
	 * @return	自分自身
	 * @see		#appendIf( boolean,CharSequence... )
	 * @og.rtnNotNull
	 */
	public OgBuilder appendIfCR( final boolean flag , final CharSequence... arys ) {
		return appendIf( flag,arys ).append( HybsConst.CR );
	}

	/**
	 * 関数を実行した結果を追加するかどうか判定するフラグ付きのappendメソッドです。
	 * 実行した結果が、null値の場合は、無視します。
	 * 引数が関数型インタフェースなので、遅延実行することが可能です。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @param <T>	関数型インタフェース(Function)の引数(総称型)
	 * @param <R>	関数型インタフェース(Function)の戻り値(総称型)
	 * @param flag	追加するかどうか判定するフラグ(trueの時のみ追加)
	 * @param key	関数の引数(総称型)
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <T,R> OgBuilder appendIf( final boolean flag , final T key , final Function<? super T, ? extends R> func ) {
		if( flag && func != null ) {
			final R obj = func.apply( key );
			if( obj != null ) { buf.append( obj ); }
		}
		return this;
	}

	/**
	 * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
	 * 実行した結果が、null値の場合は、無視します。
	 * 引数が関数型インタフェースなので、遅延実行することが可能です。
	 * 関数の結果は、オブジェクトを返します。それを、内部でappendループします。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.4.2 (2016/04/01) IntFunction に変更。配列ではなく、オブジェクトに変更。
	 *
	 * @param <R>	IntFunctionの戻り値の仮想型
	 * @param st	ループカウンタの初期値(この値を含む)
	 * @param ed	ループカウンタの終了値(この値を含まない)
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <R> OgBuilder appendRoop( final int st , final int ed , final IntFunction<R> func ) {
		for( int i=st; i<ed; i++ ) {
			final R obj = func.apply( i );
			if( obj != null ) {
				buf.append( obj );
			}
		}
		return this;
	}

	/**
	 * 開始から終了までの引数を有する関数を実行した結果を追加するときのappendメソッドです。
	 * 連結文字列に null は指定できません。
	 * 連結文字列は、一番最後には使用しません。
	 *
	 * @og.rev 6.4.4.2 (2016/04/01) 連結文字列を指定。
	 *
	 * @param <R>	IntFunctionの戻り値の仮想型
	 * @param st	ループカウンタの初期値(この値を含む)
	 * @param ed	ループカウンタの終了値(この値を含まない)
	 * @param delimiter	文字列連結する場合の文字列。nullは指定できません。
	 * @param func	関数を実行した結果を追加する関数型(結果がnullの場合は追加しません)
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public <R> OgBuilder appendRoop( final int st , final int ed , final String delimiter , final IntFunction<R> func ) {
		boolean fstFlg = true;
		for( int i=st; i<ed; i++ ) {
			final R obj = func.apply( i );
			if( obj != null ) {
				if( fstFlg ) {
					buf.append( obj );
					fstFlg = false;
				}
				else {
					buf.append( delimiter ).append( obj );
				}
			}
		}
		return this;
	}

	/**
	 * CharSequenceを追加するかどうか判定するフラグ付きのappendメソッドです。
	 * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
	 * 第二引数は、可変長CharSequenceです。
	 * ともに、CharSequenceがnullの場合は、無視します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param flag	CharSequenceを追加するかどうか判定するフラグ
	 * @param trueStr	flagがtrueの場合に追加するCharSequence(一つだけ)
	 * @param falseStr	flagがfalseの場合に追加する可変長CharSequence
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCase( final boolean flag , final CharSequence trueStr , final CharSequence... falseStr ) {
		if( flag ) {
			if( trueStr != null ) { buf.append( trueStr ); }
		}
		else {
			if( falseStr != null ) {
				for( final CharSequence str : falseStr ) {
					if( str != null ) { buf.append( str ); }
				}
			}
		}

		return this;
	}

	/**
	 * CharSequenceを追加するかどうか判定するフラグ付きのappendメソッドです。
	 * trueの場合は、第一引数を、falseの場合は、第二引数を追加します。
	 * 第一引数、第二引数ともに、配列を返すSupplierクラスです。
	 * ともに、個々のCharSequenceがnullの場合は、無視します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 * @og.rev 6.4.4.2 (2016/04/01) 引数を、Supplierクラスに変更して、結果を複数指定できるようにします。
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param flag	CharSequenceを追加するかどうか判定するフラグ
	 * @param trueFunc	flagがtrueの場合に実行するFunctionオブジェクト
	 * @param falseFunc	flagがfalseの場合に追加するFunctionオブジェクト
	 * @return	自分自身
	 * @og.rtnNotNull
	 */
	public OgBuilder appendCase( final boolean flag
										, final Supplier<CharSequence[]> trueFunc
										, final Supplier<CharSequence[]> falseFunc ) {
		if( flag ) {
			final CharSequence[] tStrs = trueFunc.get();
			if( tStrs != null ) {
				for( final CharSequence tStr : tStrs ) {
					if( tStr != null ) { buf.append( tStr ); }
				}
			}
		}
		else {
			final CharSequence[] fStrs = falseFunc.get();
			if( fStrs != null ) {
				for( final CharSequence fStr : fStrs ) {
					if( fStr != null ) { buf.append( fStr ); }
				}
			}
		}

		return this;
	}

	/**
	 * 内部のStringBuilderそのものを返します。
	 *
	 * StringBuilderを関数等に渡して追加処理する場合に、
	 * 内部のStringBuilderを渡して、処理させることが出来ます。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return	内部のStringBuilder
	 * @og.rtnNotNull
	 */
	public StringBuilder getBuilder() {
		return buf;
	}

	/**
	 * 内部のStringBuilderをクリアします。
	 *
	 * 具体的には、StringBuilder#setLength(0) を実行します。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return	内部のStringBuilder
	 */
	public OgBuilder clear() {
		buf.setLength(0);
		return this;
	}

	// ********************************** 以下は、CharSequence インタフェース の実装 **********************************

	/**
	 * 指定されたインデックスのchar値を返します。
	 *
	 * インデックスは、0からlength() - 1の範囲になります。配列のインデックス付けの場合と同じように、
	 * シーケンスの最初のcharのインデックスは0、次の文字のインデックスは1と続きます。 
	 *
	 * インデックスで指定されたchar値がサロゲートの場合、サロゲート値が返されます。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 新規追加
	 *
	 * @param index	返されるchar値のインデックス
	 * @return 指定されたchar値
	 * @see		java.lang.CharSequence#charAt(int)
	 */
	@Override
	public char charAt( final int index ) {
		return buf.charAt( index );
	}

	/**
	 * この文字シーケンスの長さを返します。
	 *
	 * 長さはシーケンス内の16ビットcharの数に等しくなります。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 新規追加
	 *
	 * @return このシーケンスのcharの数
	 * @see		java.lang.CharSequence#length()
	 */
	@Override
	public int length() {
		return buf.length();
	}

	/**
	 * この文字シーケンスの長さを返します。
	 *
	 * 長さはシーケンス内の16ビットcharの数に等しくなります。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 新規追加
	 *
	 * @param start	開始インデックス(この値を含む)
	 * @param end	終了インデックス(この値を含まない)
	 * @return 指定されたサブシーケンス
	 * @see		java.lang.CharSequence#subSequence(int,int)
	 */
	@Override
	public CharSequence subSequence( final int start , final int end ) {
		return buf.subSequence( start , end );
	}

	/**
	 * このシーケンス内のデータを表す文字列を返します。
	 *
	 * 新しいStringオブジェクトが割り当てられ、現在このオブジェクトで表されている
	 * 文字シーケンスを含むように初期化されます。
	 * その後、このStringが返されます。その後このシーケンスが変更されても、Stringの内容には影響ありません。
	 *
	 * @og.rev 6.4.4.1 (2016/03/18) 新規追加
	 *
	 * @return この文字シーケンスの文字列表現。
	 * @see		java.lang.CharSequence#toString()
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return buf.toString();
	}
}
