/*
 * Parser.java Copyright (C) 2003 Gerardo Horvilleur Martinez
 * 
 * This program 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 program 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 program; 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.parser;

import static net.cellcomputing.himawari.library.EqVariableType.type_color;
import static net.cellcomputing.himawari.library.EqVariableType.type_float;
import static net.cellcomputing.himawari.library.EqVariableType.type_hpoint;
import static net.cellcomputing.himawari.library.EqVariableType.type_integer;
import static net.cellcomputing.himawari.library.EqVariableType.type_invalid;
import static net.cellcomputing.himawari.library.EqVariableType.type_matrix;
import static net.cellcomputing.himawari.library.EqVariableType.type_normal;
import static net.cellcomputing.himawari.library.EqVariableType.type_point;
import static net.cellcomputing.himawari.library.EqVariableType.type_string;
import static net.cellcomputing.himawari.library.EqVariableType.type_vector;
import static net.cellcomputing.himawari.parser.Global.DeclareParameterType;
import static net.cellcomputing.himawari.parser.keywords.AbstractKeywordParser.TK_EOF;
import static net.cellcomputing.himawari.parser.keywords.AbstractKeywordParser.TK_NUMBER;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.HashMap;
import java.util.StringTokenizer;

import net.cellcomputing.himawari.library.EqVariableType;
import net.cellcomputing.himawari.library.RendermanInterface;
import net.cellcomputing.himawari.main.Globalmain;
import net.cellcomputing.himawari.parser.keywords.KeywordParser;
import net.cellcomputing.himawari.util.CellCipher;
import net.cellcomputing.himawari.util.HimawariLogger;

public strictfp class Parser {
	
	private HimawariLogger logger = HimawariLogger.getLogger();
	
	private final static String KEYWORD_PREFIX = "net.cellcomputing.himawari.parser.keywords.Keyword";
	private HashMap<String, KeywordParser> keywordParsers = new HashMap<String, KeywordParser>();
	private static int sizeType = 1;
	private static boolean tipType = false;
	
	/**
	 * ribファイルの読み込み解析
	 * 
	 * @param filename	ファイル名
	 * @throws Exception
	 */
	public void parse(String filename) throws Exception {
		System.out.println(filename);

		//-- 2007/02/01 nttdata 暗号化のため変更

		Reader reader = null;
		if(Globalmain.g_enc_flag.value){
			// ファイルが暗号文の場合の処理
			reader = CellCipher.getReader(filename);
		}else{
			//ファイルが平分の場合の処理
			reader = new FileReader(filename);
		}
		Tokenizer st = new Tokenizer(new BufferedYenEscapeReader(reader));
		//--ここまで追加

		st.commentChar('#');
		int tk;
		while ((tk = st.nextToken()) != StreamTokenizer.TT_EOF) {
			try {
				if (tk != StreamTokenizer.TT_WORD)
					throw new Exception(
						"Expected keyword " + filename + " at line "  +  st.lineno());
				String keyword = st.sval;
				KeywordParser kp = getKeyWordParser(keyword);
				
				kp.parse(st);
				kp.clearParameterList();
				kp = null;
				
			} catch (Exception pe) {
				logger.error(pe.getClass().getSimpleName() + ": " + pe.getMessage() + "\n");
				HimawariLogger.outputException( pe );
			}
		}
//		fr.close(); 2006/11/13 nttdata 暗号化のため変更
		reader.close();
	}
	
	public void parse(String filename, InputStream is) throws Exception {
		System.out.println(filename);
//		FileReader fr = new FileReader(filename);
		Tokenizer st = new Tokenizer(new BufferedReader(new InputStreamReader(is)));
		st.commentChar('#');
		int tk;
		while ((tk = st.nextToken()) != StreamTokenizer.TT_EOF) {
			try {
				if (tk != StreamTokenizer.TT_WORD)
					throw new Exception(
						"Expected keyword " + filename + " at line "  +  st.lineno());
				String keyword = st.sval;
				KeywordParser kp = getKeyWordParser(keyword);
				
				kp.parse(st);
			} catch (Exception pe) {
				logger.error(pe.getClass().getSimpleName() + ": " + pe.getMessage() + "\n");
				HimawariLogger.outputException( pe );
			}
		}
//		fr.close();
	}
	
	/**
	 * プロシージャ名から、対応するクラスのインスタンスを生成。
	 * 
	 * @param keyword	プロシージャ名
	 * @return kp	生成インスタンス
	 * @throws Exception
	 */
	private KeywordParser getKeyWordParser(String keyword) throws Exception {
		KeywordParser kp = (KeywordParser) keywordParsers.get(keyword.toLowerCase());
		if (kp == null) {
			char c = keyword.charAt(0);
			if (Character.isLowerCase(c))
				keyword = Character.toUpperCase(c) + keyword.substring(1);
			kp = (KeywordParser) Class.forName(KEYWORD_PREFIX + keyword).newInstance();
			kp.setParser(this);
			keywordParsers.put(keyword.toLowerCase(), kp);
		}
		return kp;
	}

	/**
	 * パラメータタイプの解析。
	 * 定義されているかどうかをチェック。
	 * 
	 * @param Name	パラメータ名
	 * @param Type	パラメータ型
	 */
	public static void ParserDeclare( final String Name, final String Type)
	{
		if( type_invalid == DeclareParameterType(Name, Type).getValue() )
			HimawariLogger.getLogger().error( "RiDeclare: Unknown type [" + Name + ", " + Type + "]\n");

		RendermanInterface.RiDeclare( Name, Type);
	}

	/**
	 * 定義されたパラメータ型に対応するパラメータタイプを返す。
	 * 
	 * @param Source	パラメータ型
	 * @return	パラメータタイプ
	 */
	public static EqVariableType ParseParameterType( final String Source )
	{
		if( Source == null || Source.length() == 0 ){
			HimawariLogger.getLogger().error("Cannot Parse Empty Parameter Type\n");
			
			return new EqVariableType( type_invalid );
		}
		
		sizeType = 1;
		setParamterSize( Source );
		
		// Break the source string into whitespace delimited "words" ...
		StringTokenizer p_type = new StringTokenizer( Source );
		
		// Figure out the parameter type ...
		while( p_type.hasMoreTokens() )
		{				
			String cleanword = CleanParameterType( p_type.nextToken() );
			
			//記憶クラス //XXX: 実はfacevaryingは、aqsisでは、実装されていないようです。とりあえず･･･
			if( cleanword.equals("varying") || cleanword.equals("vertex") ||  cleanword.equals("facevarying")){
				tipType = true;
				continue;
			}
				
			//パラメータタイプ
			if(cleanword.equals( "int" ) || cleanword.equals( "integer" ) ) {
				return new EqVariableType( type_integer );
			}
			if(cleanword.equals( "float" ))	{
				return new EqVariableType( type_float );
			}
			if(cleanword.equals( "string" )) {
				return new EqVariableType( type_string );
			}
			if(cleanword.equals( "hpoint" )){
				sizeType *= 4;  
				return new EqVariableType( type_hpoint );
			}
			if(cleanword.equals( "point" )) {
				sizeType *= 3;
				return new EqVariableType( type_point );
			}
			if(cleanword.equals( "color" ))	{
				sizeType *= 3;
				return new EqVariableType( type_color );
			}
			if(cleanword.equals( "vector" )) {
				sizeType *= 3;
				return new EqVariableType( type_vector );
			}
			if(cleanword.equals( "normal" )) {
				sizeType *= 3;
				return new EqVariableType( type_normal );
			}
			if(cleanword.equals( "matrix" )) {
				sizeType *= 16;
				return new EqVariableType( type_matrix );
			}
		}
	
		sizeType = 0;
		return new EqVariableType( type_invalid );
	}

	/**
	 * 必要な文字列を取り出す。
	 * パラメータ定義型の'[]'以外。
	 * 
	 * @param word	元文字列
	 * @return	cleanword	取り出した文字列
	 */
	private static String CleanParameterType( String word )
	{
		String cleanword = word;

		if( word == null )
			return null;
		
		int index = word.indexOf("[");
		if( index > 0 ) 
			cleanword = word.substring( 0, index );
		
		return cleanword;
	}


	/**
	 * パラメータタイプの配列サイズを取り出してセットする。
	 * 
	 * 
	 * @param Source　タイプ文字列
	 */
	private static void setParamterSize( String Source )
	{
		int open = Source.indexOf("[");
		int close = Source.lastIndexOf("]");
		if( open < 1 ) {
			return ;
		} 
		else if( open + 1 >= close ) {
			sizeType = 0;
			return ;
		}
		
		StringReader lbrace = new StringReader( Source.substring( open + 1, close ));
		Tokenizer contents = new Tokenizer( lbrace );
		int ttype = 0;
		
		try {
			ttype = contents.nextToken();
			if( ttype == TK_NUMBER && contents.nextToken() == TK_EOF && contents.nval%1 == 0 )
				sizeType *= contents.nval;
			else
				sizeType = 0;
		}catch (Exception e) { 
			e.printStackTrace();
		}
	}
	
	/**
	 * パラメータタイプの配列サイズのゲッタ。
	 * 
	 * @return
	 */
	public static int getParameterSize()
	{
		return sizeType ;
	}
	
	/**
	 * パラメータタイプの配列サイズのゲッタ。
	 * 
	 * @return
	 */
	public static boolean getParameterTip()
	{
		return tipType ;
	}
	


}
