/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.dialect.function;
import org.hibernate.type.StandardBasicTypes;

/**
 * A {@link SQLFunction} implementation that emulates the ANSI SQL trim function
 * on dialects which do not support the full definition.  However, this function
 * definition does assume the availability of ltrim, rtrim, and replace functions
 * which it uses in various combinations to emulate the desired ANSI trim()
 * functionality.
 *
 * @author Steve Ebersole
 */
public class AnsiTrimEmulationFunction extends AbstractAnsiTrimEmulationFunction {
	/**
	 * The default {@code ltrim} function name
	 */
	public static final String LTRIM = "ltrim";

	/**
	 * The default {@code rtrim} function name
	 */
	public static final String RTRIM = "rtrim";

	/**
	 * The default {@code replace} function name
	 */
	public static final String REPLACE = "replace";

	/**
	 * The placeholder used to represent whitespace
	 */
	public static final String SPACE_PLACEHOLDER = "${space}$";

	/**
	 * The SQLFunctionTemplate pattern for the trimming leading spaces
	 */
	public static final String LEADING_SPACE_TRIM_TEMPLATE = LTRIM + "(?1)";

	/**
	 * The SQLFunctionTemplate pattern for the trimming trailing spaces
	 */
	public static final String TRAILING_SPACE_TRIM_TEMPLATE = RTRIM + "(?1)";

	/**
	 * The SQLFunctionTemplate pattern for the trimming both leading and trailing spaces
	 */
	public static final String BOTH_SPACE_TRIM_TEMPLATE = LTRIM + "(" + RTRIM + "(?1))";

	/**
	 * The SQLFunctionTemplate pattern for the trimming both leading and trailing spaces, with the optional FROM keyword.
	 * Different because we need to skip the FROM keyword in the SQLFunctionTemplate processing
	 */
	public static final String BOTH_SPACE_TRIM_FROM_TEMPLATE = LTRIM + "(" + RTRIM + "(?2))";

	/**
	 * A template for the series of calls required to trim non-space chars from the beginning of text.
	 * <p/>
	 * NOTE : essentially we:<ol>
	 * <li>replace all space chars with the text '${space}$'</li>
	 * <li>replace all the actual replacement chars with space chars</li>
	 * <li>perform left-trimming (that removes any of the space chars we just added which occur at the beginning of the text)</li>
	 * <li>replace all space chars with the replacement char</li>
	 * <li>replace all the '${space}$' text with space chars</li>
	 * </ol>
	 */
	public static final String LEADING_TRIM_TEMPLATE =
			REPLACE + "(" +
				REPLACE + "(" +
					LTRIM + "(" +
						REPLACE + "(" +
							REPLACE + "(" +
								"?1," +
								"' '," +
								"'" + SPACE_PLACEHOLDER + "'" +
							")," +
							"?2," +
							"' '" +
						")" +
					")," +
					"' '," +
					"?2" +
				")," +
				"'" + SPACE_PLACEHOLDER + "'," +
				"' '" +
			")";

	/**
	 * A template for the series of calls required to trim non-space chars from the end of text.
	 * <p/>
	 * NOTE: essentially the same series of calls as outlined in {@link #LEADING_TRIM_TEMPLATE} except that here,
	 * instead of left-trimming the added spaces, we right-trim them to remove them from the end of the text.
	 */
	public static final String TRAILING_TRIM_TEMPLATE =
			REPLACE + "(" +
				REPLACE + "(" +
					RTRIM + "(" +
						REPLACE + "(" +
							REPLACE + "(" +
								"?1," +
								"' '," +
								"'" + SPACE_PLACEHOLDER + "'" +
							")," +
							"?2," +
							"' '" +
						")" +
					")," +
					"' '," +
					"?2" +
				")," +
				"'" + SPACE_PLACEHOLDER + "'," +
				"' '" +
			")";

	/**
	 * A template for the series of calls required to trim non-space chars from both the beginning and the end of text.
	 * <p/>
	 * NOTE: again, we have a series of calls that is essentially the same as outlined in {@link #LEADING_TRIM_TEMPLATE}
	 * except that here we perform both left (leading) and right (trailing) trimming.
	 */
	public static final String BOTH_TRIM_TEMPLATE =
			REPLACE + "(" +
				REPLACE + "(" +
					LTRIM + "(" +
						RTRIM + "(" +
							REPLACE + "(" +
								REPLACE + "(" +
									"?1," +
									"' '," +
									"'" + SPACE_PLACEHOLDER + "'" +
								")," +
								"?2," +
								"' '" +
							")" +
						")" +
					")," +
					"' '," +
					"?2" +
				")," +
				"'" + SPACE_PLACEHOLDER + "'," +
				"' '" +
			")";

	private final SQLFunction leadingSpaceTrim;
	private final SQLFunction trailingSpaceTrim;
	private final SQLFunction bothSpaceTrim;
	private final SQLFunction bothSpaceTrimFrom;

	private final SQLFunction leadingTrim;
	private final SQLFunction trailingTrim;
	private final SQLFunction bothTrim;

	/**
	 * Constructs a new AnsiTrimEmulationFunction using {@link #LTRIM}, {@link #RTRIM}, and {@link #REPLACE}
	 * respectively.
	 *
	 * @see #AnsiTrimEmulationFunction(String,String,String)
	 */
	public AnsiTrimEmulationFunction() {
		this( LTRIM, RTRIM, REPLACE );
	}

	/**
	 * Constructs a <tt>trim()</tt> emulation function definition using the specified function calls.
	 *
	 * @param ltrimFunctionName The <tt>left trim</tt> function to use.
	 * @param rtrimFunctionName The <tt>right trim</tt> function to use.
	 * @param replaceFunctionName The <tt>replace</tt> function to use.
	 */
	public AnsiTrimEmulationFunction(String ltrimFunctionName, String rtrimFunctionName, String replaceFunctionName) {
		leadingSpaceTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				LEADING_SPACE_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
		);

		trailingSpaceTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				TRAILING_SPACE_TRIM_TEMPLATE.replaceAll( RTRIM, rtrimFunctionName )
		);

		bothSpaceTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				BOTH_SPACE_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
						.replaceAll( RTRIM, rtrimFunctionName )
		);

		bothSpaceTrimFrom = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				BOTH_SPACE_TRIM_FROM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
						.replaceAll( RTRIM, rtrimFunctionName )
		);

		leadingTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				LEADING_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
						.replaceAll( RTRIM, rtrimFunctionName )
						.replaceAll( REPLACE,replaceFunctionName )
		);

		trailingTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				TRAILING_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
						.replaceAll( RTRIM, rtrimFunctionName )
						.replaceAll( REPLACE,replaceFunctionName )
		);

		bothTrim = new SQLFunctionTemplate(
				StandardBasicTypes.STRING,
				BOTH_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
						.replaceAll( RTRIM, rtrimFunctionName )
						.replaceAll( REPLACE,replaceFunctionName )
		);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveBothSpaceTrimFunction() {
		return bothSpaceTrim;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveBothSpaceTrimFromFunction() {
		return bothSpaceTrimFrom;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveLeadingSpaceTrimFunction() {
		return leadingSpaceTrim;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveTrailingSpaceTrimFunction() {
		return trailingSpaceTrim;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveBothTrimFunction() {
		return bothTrim;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveLeadingTrimFunction() {
		return leadingTrim;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected SQLFunction resolveTrailingTrimFunction() {
		return trailingTrim;
	}
}
