// Aqsis
// Copyright (c) 1997 - 2001, Paul C. Gregory
//
// Contact: pgregory@aqsis.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.library;

import net.cellcomputing.himawari.exception.XqException;

import static net.cellcomputing.himawari.library.EqVariableClass.*;
import static net.cellcomputing.himawari.library.EqVariableType.*;
/**
 * 
 * 
 * 
 * @author NTT DATA Corporation
 */
/**
 * CqInlineParse
 * 内部的な文字列の構文解析をする。
 * 主にDeclare等で使われています。
 * 使用できる構文は、
 * A グループ{uniform, constant, varying, vertex, facevarying} 
 * B　グループ{float, point, hpoint, vector, normal, color, string, matrix, integer, int}
 * 
 * 1：A B Ka
 * 2：A B[2] ka
 * 3:B ka
 * 4:B[2] Ka 
 * 
 * それ以外はエラーになります。
 * 
 * Parseに与えた構文が正しければ
 * isInline()で正しいかどうかを取得することができます。
 * GetClass()はEqVariableClass型を返します。
 * getType()はEqVariableType型を返します。
 * getQuantity()は配列であった場合に配列のサイズを返します。
 * getIdentifier()はKaなどの最後の部分を返します。
 * 
 * @author NTT DATA Corporation
 */
public strictfp class CqInlineParse {
	
	public static long huniform = "uniform".hashCode();
	public static long hconstant = "constant".hashCode();
	public static long hvarying = "varying".hashCode();
	public static long hvertex = "vertex".hashCode();
	public static long hfacevarying = "facevarying".hashCode();

	public static long hfloat = "float".hashCode();
	public static long hpoint = "point".hashCode();
	public static long hhpoint = "hpoint".hashCode();
	public static long hvector = "vector".hashCode();
	public static long hnormal = "normal".hashCode();
	public static long hcolor = "color".hashCode();
	public static long hstring = "string".hashCode();
	public static long hmatrix = "matrix".hashCode();
	public static long hinteger = "integer".hashCode();
	public static long hint = "int".hashCode();

	public static long hleft = "[".hashCode();
	public static long hright = "]".hashCode();
	
	
	private int number_of_words;
	private String[] word = new String[ 7 ];

	private boolean inline_def;
	private EqVariableClass tc = new EqVariableClass(0);
	private EqVariableType tt = new EqVariableType(0);
	private int size;
	private String identifier = new String();

	private boolean is_class ( final String str )
    {
        long param = str.hashCode();

            if ( ( param == hconstant ) ||
                 ( param == huniform ) ||
                 ( param == hvarying ) ||
                 ( param == hvertex ) ||
                 ( param == hfacevarying ) )
            return true;
        return false;
    }

    private boolean is_type ( final String str )
    {
        long param = str.hashCode();

            if ( ( param == hfloat ) ||
                 ( param == hpoint ) ||
                 ( param == hvector ) ||
                 ( param == hnormal ) ||
                 ( param == hcolor ) ||
                 ( param == hstring ) ||
                 ( param == hmatrix ) ||
                 ( param == hhpoint ) ||
                 ( param == hinteger ) ||
                 ( param == hint ) ) return true;
        return false;
    }
	    
//	  check if this int is >0 too
    private boolean is_int ( final String str )
    {
    	int j;
    	try
    	{
    		j = Integer.parseInt(str);
    	}
    	catch(NumberFormatException e)
    	{
    		return false;
    	}
//        int i;
//        p_int j;
//        i = sscanf( str.c_str(), "%d", j );
        if (  j <= 0 )  return false;
        return true;
    }

    private EqVariableClass get_class ( final String str )
    {
             long param = str.hashCode();

            if ( param == hconstant ) return  new EqVariableClass(class_constant) ;
            if ( param == huniform ) return new EqVariableClass(class_uniform);
            if ( param == hvarying ) return new EqVariableClass(class_varying);
            if ( param == hvertex ) return new EqVariableClass(class_vertex);
            if ( param == hfacevarying ) return new EqVariableClass(class_facevarying);
        return ( new EqVariableClass( class_constant ) );
    }

    private EqVariableType get_type ( final String str )
    {
            long param = str.hashCode();

            if ( param == hfloat ) return new EqVariableType( type_float );
            if ( param == hpoint ) return new EqVariableType( type_point );
            if ( param == hvector ) return new EqVariableType( type_vector );
            if ( param == hnormal ) return new EqVariableType( type_normal );
            if ( param == hcolor ) return new EqVariableType( type_color );
            if ( param == hstring ) return new EqVariableType( type_string );
            if ( param == hmatrix ) return new EqVariableType( type_matrix );
            if ( param == hhpoint ) return new EqVariableType( type_hpoint );
            if ( param == hinteger ) return new EqVariableType( type_integer );
            if ( param == hint ) return new EqVariableType( type_integer );

        return ( new EqVariableType( type_float ) );
    }
    
//    private int get_size ( final String str )
//    {
//        int i;
//            i = Integer.parseInt( str );
//        return i;
//    }

    private void check_syntax() throws XqException
    {
        // number_of_words =0 ---> ERROR
        // number_of_words =1 ---> not an inline def
        // number_of_words =2 ---> type id
        // number_of_words =3 ---> class type id
        // number_of_words =4 ---> ERROR
        // number_of_words =5 ---> type [ size ] id
        // number_of_words =6 ---> class type [ size ] id
        // number_of_words =7 ---> ERROR

        switch ( (int)number_of_words )
        {
        case 0:
            throw new XqException ( "void parameter declaration" );
        case 4:
        case 7:
            throw new XqException ( "Bad inline declaration" );
        case 1: inline_def = false;
            break;
        case 2:
        	//lc( word[ 0 ] );
        	word[0] = word[0].toLowerCase();
            if ( is_type( word[ 0 ] ) == false )
            {
                throw new XqException ( "Bad inline declaration" );
            }
            inline_def = true;
            tc = new EqVariableClass(class_uniform);
            tt = get_type( word[ 0 ] );
            size = 1;
            identifier = word[ 1 ];
            break;
        case 3:
            //lc( word[ 0 ] );
            word[0] = word[0].toLowerCase();
        	//lc( word[ 1 ] );
            word[1] = word[1].toLowerCase();
            
            if ( ( is_class( word[ 0 ] ) == false ) || ( is_type( word[ 1 ] ) == false ) )
            {
                throw new XqException ( "Bad inline declaration" );
            }
            inline_def = true;
            tc = get_class( word[ 0 ] );
            tt = get_type( word[ 1 ] );
            size = 1;
            identifier = word[ 2 ];
            break;
        case 5:
            //lc( word[ 0 ] );
            word[0] = word[0].toLowerCase();
            if ( ( is_type( word[ 0 ] ) == false ) || ( word[ 1 ].compareTo("[")!=0 ) ||
                    ( is_int( word[ 2 ] ) == false ) || ( word[ 3 ].compareTo("]") != 0  ) )
            {
                throw new XqException ( "Bad inline declaration" );
            }
            inline_def = true;
            tc = new EqVariableClass( class_uniform );
            tt = get_type( word[ 0 ] );
            size = Integer.parseInt( word[ 2 ] );
            identifier = word[ 4 ];
            break;
        case 6:
            //lc( word[ 0 ] );]
        	word[0] = word[0].toLowerCase();
            //lc( word[ 1 ] );
        	word[1] = word[1].toLowerCase();
        	
            if ( ( is_class( word[ 0 ] ) == false ) || ( is_type( word[ 1 ] ) == false ) ||
                    ( word[ 2 ].compareTo("[") != 0 ) || ( is_int( word[ 3 ] ) == false ) ||
                    ( word[ 4 ].compareTo("]") != 0 ) )
            {
                throw new XqException ( "Bad inline declaration" );
            }
            inline_def = true;
            tc = get_class( word[ 0 ] );
            tt = get_type( word[ 1 ] );
            size = Integer.parseInt( word[ 3 ] );
            identifier = word[ 5 ];
            break;
        }
    }
    
//    private void lc( String str )
//    {
//        for ( int i = 0;i < str.length();i++ )
//        {
//            str[ i ] = tolower( str[ i ] );
//        }
//    }

	public CqInlineParse()
	{}
	
	public void destracts()
	{}

	public void parse ( String str ) throws XqException
    {
        int i, j;
        int sp;
        int sz;
        boolean start_found;

        sp = 0;
        sz = 1;
        j = 0;
        start_found = false;

        for ( i = 0;( i < str.length() ) && ( j < 7 );i++ )
        {
            switch ( str.charAt( i ) )
            {
            case ' ':
            case '\t':
            case '\n':
                if ( start_found == true )
                {
                    word[ j ] = str.substring( sp, sp+sz );
                    j++;
                    sz = 1;
                }
                start_found = false;
                break;
            case '#':
                throw new XqException ( "'#' character not allowed in strings" );
            case '\"':
                throw new XqException ( "'\"' character not allowed in strings" );
            case '[':
            case ']':
                if ( start_found == true )
                {
                    word[ j ] = str.substring( sp, sp+sz );
                    j++;
                    start_found = false;
                }
                sp = i;
                sz = 1;
                if( j > 6 )
                	break;
                word[ j ] = str.substring( sp, sp+sz );
                j++;
                break;
            default:
                if ( start_found == true )
                {
                    sz += 1;
                    break;
                }
                start_found = true;
                sp = i;
                sz = 1;
            }
        }
        // if there is no space at the end of the string,
        // the previous loop will not notice the end of the word,
        // and so will 'forget' to store it.
        if ( start_found == true )
        {
            word[ j ] = str.substring( sp, sp+sz );
            j++;
        }
        number_of_words = j;
        try
        {
            check_syntax ();
        }
        catch(XqException e )
        {
//        	e.printStackTrace();
            String strError = new String( e.strReason() );
            strError += " : ";
            strError += str;
            throw new XqException( strError ) ;
        }
    }

    public boolean isInline()
    {
        return inline_def;
    }
    public EqVariableClass GetClass()
    {
        return tc;
    }
    public EqVariableType getType()
    {
        return tt;
    }
    public int getQuantity()
    {
        return size;
    }
    public String getIdentifier()
    {
        return identifier;
    }
}
