/*******************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.core.script.bre;

import java.util.regex.Pattern;

import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

/**
 * String functions.
 */
public class BirtStr extends ScriptableObject
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public BirtStr( )
	{
		this.defineProperty( "left", new Function_Left( ), 0 );
		this.defineProperty( "right", new Function_Right( ), 0 );
		this.defineProperty( "concat", new Function_Concat( ), 0 );
		this.defineProperty( "toUpper", new Function_ToUpper( ), 0 );
		this.defineProperty( "toLower", new Function_ToLower( ), 0 );
		this.defineProperty( "trim", new Function_Trim( ), 0 );
		this.defineProperty( "trimLeft", new Function_TrimLeft( ), 0 );
		this.defineProperty( "trimRight", new Function_TrimRight( ), 0 );
		this.defineProperty( "indexOf", new Function_IndexOf( ), 0 );
		this.defineProperty( "search", new Function_Search( ), 0 );
		this.defineProperty( "charLength", new Function_CharLength( ), 0 );
	}

	private class Function_Left extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length > 2 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			if ( args.length == 1 )
			{
				return left(  args[0]==null?null:(String) args[0]);
			}
			else
			{
				return left(  args[0]==null?null:(String) args[0], ( (Number) args[1] ).intValue( ) );
			}
		}

		/**
		 * Returns the first n characters of the string str. If n is 0, an empty
		 * string is returned. If n is greater than the length of str, the
		 * entire string is returned.
		 * 
		 * @param str
		 * @param n
		 * @return
		 */
		private String left( String str, int n )
		{
			if ( n < 0 )
				throw new IllegalArgumentException( );
			if ( str == null )
				return null;
			if ( n == 0 )
				return "";
			if ( n >= str.length( ) )
				return str;
			else
				return str.substring( 0, n );
		}

		/**
		 * Returns the first characters of the string str.
		 * 
		 * @param str
		 * @return
		 */
		private String left( String str )
		{
			return left( str, 1 );
		}
	}

	private class Function_Right extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length > 2 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			if ( args.length == 1 )
			{
				return right( args[0]==null?null:(String) args[0] );
			}
			else
			{
				return right( args[0]==null?null:(String) args[0],
						( (Number) args[1] ).intValue( ) );
			}
		}

		/**
		 * Returns the last n characters of the string str. If n is 0, an empty
		 * string is returned. If n is greater than the length of str, the
		 * entire string is returned.
		 * 
		 * @param str
		 * @param n
		 * @return
		 */
		public String right( String str, int n )
		{
			if ( n < 0 )
				throw new IllegalArgumentException( );
			if ( str == null )
				return null;
			if ( n == 0 )
				return "";
			if ( n >= str.length( ) )
				return str;
			else
				return str.substring( str.length( ) - n, str.length( ) );
		}

		/**
		 * Returns the last characters of the string str.
		 * 
		 * @param str
		 * @return
		 */
		public String right( String str )
		{
			return right( str, 1 );
		}
	}

	private class Function_Concat extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			String result = "";

			for ( int i = 0; i < args.length; i++ )
			{
				result += args[i];
			}

			return result;
		}
	}

	private class Function_ToUpper extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return  args[0]==null?null:( (String) args[0] ).toUpperCase( );
		}
	}

	private class Function_ToLower extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return  args[0]==null?null:( (String) args[0] ).toLowerCase( );
		}
	}

	private class Function_Trim extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return trim(  args[0]==null?null:(String) args[0] );
		}

		/**
		 * Removes all leading and trailing blank characters (space, TAB etc.).
		 * Also, all consecutive blank characters are consolidated into one.
		 * 
		 * @param str
		 * @return
		 */
		private String trim( String str )
		{
			if ( str == null )
				return null;
			else
			{
				String trimStr = str.trim( );
				return trimStr.replaceAll( "\\s+", " " );
			}
		}
	}

	private class Function_TrimLeft extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return trimLeft(  args[0]==null?null:(String) args[0] );
		}

		/**
		 * Removes all leading blanks. Trailing blanks and blanks between words
		 * are not removed.
		 * 
		 * @param str
		 * @return
		 */
		private String trimLeft( String str )
		{
			if ( str == null )
				return null;
			else
			{
				byte[] value = str.getBytes( );
				int st = 0;
				while ( ( st < str.length( ) ) && ( value[st] <= ' ' ) )
				{
					st++;
				}
				return ( st > 0 ) ? str.substring( st, str.length( ) ) : str;
			}
		}
	}

	private class Function_TrimRight extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return trimRight(  args[0]==null?null:(String) args[0] );
		}

		/**
		 * Removes all trailing blanks. Leading blanks and blanks between words
		 * are not removed.
		 * 
		 * @param str
		 * @return
		 */
		private String trimRight( String str )
		{
			if ( str == null )
				return null;
			else
			{
				byte[] value = str.getBytes( );
				int end = str.length( );
				while ( ( end < str.length( ) )
						&& ( value[str.length( ) - 1] <= ' ' ) )
				{
					end--;
				}
				return ( end < str.length( ) ) ? str.substring( 0, end ) : str;
			}
		}

	}

	private class Function_IndexOf extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length > 3 || args.length < 2 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			if ( args.length == 3 )
			{
				return new Integer( indexOf(  args[0]==null?null:(String) args[0],
						 args[1]==null?null:(String) args[1],
						( (Number) args[2] ).intValue( ) ) );
			}
			else
			{
				return new Integer( indexOf(  args[0]==null?null:(String) args[0],  args[1]==null?null:(String) args[1] ) );
			}
		}

		/**
		 * Searches for find_text in str and returns the index of first
		 * occurrence of pattern. Search starts at position start. All index
		 * values are 0-based.If start is omitted, a value of 0 is assumed.
		 * String search is case sensitive.
		 * 
		 * @param find_text
		 * @param str
		 * @param start
		 * @return
		 */
		private int indexOf( String find_text, String str, int start )
		{
			if ( start < 0 )
				throw new IllegalArgumentException( );
			if ( find_text == null
					|| str == null || str.indexOf( find_text ) < 0 )
				return -1;
			else
			{
				return str.indexOf( find_text, start );
			}
		}

		/**
		 * Searches for find_text in str and returns the index of first
		 * occurrence of pattern. Search starts at position 0. All index values
		 * are 0-based. If no matched string found, return -1 String search is
		 * case sensitive.
		 * 
		 * @param find_text
		 * @param str
		 * @return
		 */
		private int indexOf( String find_text, String str )
		{
			return indexOf( find_text, str, 0 );
		}
	}

	private class Function_Search extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length > 3 || args.length < 2 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			if ( args.length == 3 )
			{
				return new Integer( search(  args[0]==null?null:(String) args[0],
						 args[1]==null?null:(String) args[1],
						( (Number) args[2] ).intValue( ) ) );
			}
			else
			{
				return new Integer( search(  args[0]==null?null:(String) args[0],  args[1]==null?null:(String) args[1] ) );
			}
		}

		/**
		 * Similar to indexOf function, except that: (1) string comparison is
		 * case-insensitive (2) pattern string can contain wildcard characters: *
		 * matches any sequence of characters (including empty); ? matches any
		 * single character.
		 * 
		 * @param pattern
		 * @param str
		 * @param start
		 * @return
		 */
		private int search( String pattern, String str, int start )
		{
			if ( start < 0 )
				throw new IllegalArgumentException( );
			if ( pattern == null || str == null )
				return -1;
			else
			{
				String subStr = str.substring( start );

				String newStr = pattern.replaceAll( "\\Q?\\E", "." );
				String finalStr = newStr.replaceAll( "\\Q*\\E", ".*" );
				Pattern p = Pattern.compile( finalStr );
				String[] split = p.split( subStr );

				// there is no match otherwise
				if ( split[0].length( ) != subStr.length( ) )
					return split[0].length( ) + start;

				return -1;
			}
		}

		/**
		 * Similar to indexOf function, except that: (1) string comparison is
		 * case-insensitive (2) pattern string can contain wildcard characters: *
		 * matches any sequence of characters (including empty); ? matches any
		 * single character.
		 * 
		 * @param pattern
		 * @param str
		 * @return
		 */
		private int search( String pattern, String str )
		{
			return search( pattern, str, 0 );
		}
	}

	private class Function_CharLength extends BaseFunction implements Function
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Object call( Context cx, Scriptable scope, Scriptable thisObj,
				java.lang.Object[] args )
		{
			if ( args == null || args.length != 1 )
				throw new IllegalArgumentException( "The number of arguement is incorrect." );
			args = BreUtility.convertToJavaObjects( args );

			return new Integer( charLength(  args[0]==null?null:(String) args[0] ) );
		}

		/**
		 * Returns the number of characters in string.
		 * 
		 * @param str
		 * @return
		 */
		private int charLength( String str )
		{
			if ( str == null )
				return 0;
			else
				return str.length( );
		}
	}

	public String getClassName( )
	{
		return "BirtStr";
	}
}
