package org.maachang.comet.httpd.engine.script;

import java.util.ArrayList;

import org.maachang.util.StringUtil;


/**
 * スクリプト変換.
 * <BR><BR>
 * MaachangComet独自定義のスクリプトを変換します.
 * 
 * @version 2007/08/29
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class ConvertScript {
    
    /**
     * メソッド変換.
     * <BR><BR>
     * 対象文字列がメソッド変換対象の場合は、変換処理を実施します.
     * <BR>
     * @param string 対象の文字列を設定します.
     * @return String 変換された情報が返されます.
     */
    public static final String convertMethod( String string ) {
        return convertMethod( true,0,string ) ;
    }
    
    /**
     * 変換処理.
     * <BR><BR>
     * スクリプトを変換します.
     * <BR>
     * @param script 対象のスクリプトを設定します.
     * @return String 変換されたスクリプト情報が返されます.
     * @exception Exception 例外.
     */
    public static final String convert( String script )
        throws Exception {
        return convert( true,0,script ) ;
    }
    
    /**
     * 変換処理.
     */
    private static final String convert( boolean mode,int scope,String script )
        throws Exception {
        if( script == null || script.length() <= 0 ||
            script.indexOf( "@" ) == -1 ) {
            return script ;
        }
        int len = script.length() ;
        StringBuilder buf = new StringBuilder(( int )(len*1.25f)) ;
        int cote = -1 ;
        char b = 0 ;
        int st = -1 ;
        int parenthesesCnt = 0 ;
        boolean cm1 = false ;
        boolean cm2 = false ;
        boolean scopeFlag = false ;
        for( int i = 0 ; i < len ; i ++ ) {
            char c = script.charAt( i ) ;
            // コメント情報.
            if( cm1 == true || cm2 == true ) {
                buf.append( c ) ;
                if( cm1 == true && ( c == '\r' || c == '\n' ) ) {
                    cm1 = false ;
                }
                else if( cm2 == true && c == '*' && len > i+1 && script.charAt( i+1 ) == '/' ) {
                    cm2 = false ;
                }
                b = c ;
                continue ;
            }
            // コーテーション内の場合.
            if( cote != -1 ) {
                if( st == -1 ) {
                    buf.append( c ) ;
                }
                // 同一のコーテーションを検知.
                if( c == cote && ( ( i != 0 && b != '\\' ) || i == 0 ) ) {
                    cote = -1 ;
                }
            }
            // コーテーションを検知.
            else if( ( c == '\'' || c == '\"' ) &&
                ( ( i != 0 && b != '\\' ) || i == 0 ) ) {
                if( st == -1 ) {
                    buf.append( c ) ;
                }
                cote = c ;
            }
            // コメント開始.
            else if( st == -1 && c == '/' ) {
                buf.append( c ) ;
                if( len > i+1 && script.charAt( i+1 ) == '/' ) {
                    cm1 = true ;
                }
                else if( len > i+1 && script.charAt( i+1 ) == '*' ) {
                    cm2 = true ;
                }
            }
            // スコープ内処理.
            else if( scopeFlag == false && scope > 0 && ( c == '(' || c == ')' ) ) {
                if( c == '(' ) {
                    parenthesesCnt ++ ;
                }
                else {
                    parenthesesCnt -- ;
                }
                if( parenthesesCnt <= 0 ) {
                    if( st != -1 ) {
                        scopeFlag = true ;
                        i -- ;
                    }
                }
                if( st == -1 ) {
                    buf.append( c ) ;
                }
            }
            // 特殊内容を検知.
            else if( c == '@' && st == -1 && check( script,i ) == true ) {
                st = i ;
                parenthesesCnt = 0 ;
            }
            // 特殊条件の終端を検知.
            else if( st != -1 && ( c == ';' || c == '\r' || c == '\n' || 
                i+1 == len || scopeFlag == true ||
                ( c == '%' && len > i+1 && script.charAt( i+1 ) == '>' ) ) ) {
                String one = null ;
                if( scopeFlag == false && i+1 == len ) {
                    one = script.substring( st,len ) ;
                }
                else {
                    one = script.substring( st,i ) ;
                }
                if( c == ';' || scopeFlag == true ||
                    ( c == '%' && len > i+1 && script.charAt( i+1 ) == '>' ) ) {
                    one = convertMethod( false,scope,one ) ;
                    if( one.indexOf( "@" ) != -1 ) {
                        one = convert( false,scope+1,one ) ;
                    }
                    if( scopeFlag == true && i+1 != len ) {
                        buf.append( one ) ;
                        if( parenthesesCnt <= -1 ) {
                            buf.append( c ) ;
                            parenthesesCnt = 0 ;
                        }
                    }
                    else {
                        buf.append( one ).append( c ) ;
                    }
                }
                else {
                    one = convertMethod( mode,scope,one ) ;
                    if( one.indexOf( "@" ) != -1 ) {
                        one = convert( mode,scope+1,one ) ;
                    }
                    buf.append( one ) ;
                }
                if( c == '\r' || c == '\n' ) {
                    buf.append( c ) ;
                }
                st = -1 ;
                scopeFlag = false ;
            }
            // その他.
            else if( st == -1 ) {
                buf.append( c ) ;
            }
            b = c ;
        }
        if( st != -1 ) {
            String one = script.substring( st,len-1 ) ;
            one = convertMethod( mode,scope,one ) ;
            if( one.indexOf( "@" ) != -1 ) {
                one = convert( mode,scope+1,one ) ;
            }
            buf.append( one ) ;
        }
        return buf.toString() ;
    }
    
    /**
     * メソッド変換.
     */
    private static final String convertMethod( boolean mode,int scope,String string ) {
        if( string.startsWith( "@" ) ) {
            if( check( string,0 ) == false ) {
                return string ;
            }
            string = string.substring( "@".length(),string.length() ) ;
            string = pushSpace( string ) ;
            int scnt = StringUtil.getCount( string,'(' ) ;
            int ecnt = StringUtil.getCount( string,')' ) ;
            if( scnt != 0 || ecnt != 0 ) {
                string = cutParentheses( true,scope,string ) ;
                string = cutParentheses( false,scope,string ) ;
            }
            ArrayList<String> lst = StringUtil.cutString( true,true,string," 　\t;" ) ;
            if( lst != null && lst.size() > 0 ) {
                int len = lst.size() ;
                StringBuilder buf = new StringBuilder() ;
                buf.append( lst.get( 0 ) ) ;
                buf.append( " (" ) ;
                for( int i = 1 ; i < len ; i ++ ) {
                    if( i != 1 ) {
                        buf.append( " " ) ;
                    }
                    buf.append( lst.get( i ) ) ;
                }
                buf.append( " )" ) ;
                if( mode == true ) {
                    buf.append( ";" ) ;
                }
                string = buf.toString() ;
            }
        }
        return string ;
    }
    
    /**
     * メソッド用の括弧が存在する場合、それを除外.
     */
    private static final String cutParentheses( boolean mode,int scope,String string ) {
        if( string.indexOf( "(" ) == -1 && string.indexOf( ")" ) == -1 ) {
            return string ;
        }
        ArrayList<String> lst = null ;
        if( mode == true ) {
            lst = StringUtil.parseString( string,"(" ) ;
        }
        else {
            lst = StringUtil.parseString( string,")" ) ;
        }
        if( lst.size() <= 0 ) {
            return string ;
        }
        int len = lst.size() ;
        if( len == 1 ) {
            if( mode == true && string.endsWith( "(" ) ) {
                string.substring( 0,string.length()-1 ) ;
            }
            if( mode == false && string.startsWith( ")" ) ) {
                string.substring( 1,string.length() ) ;
            }
            return string ;
        }
        if( mode == true ) {
            boolean notFirst = false ;
            String start = lst.get( 0 ) ;
            int startLen = start.length() ;
            for( int i = 0 ; i < startLen ; i ++ ) {
                char c = start.charAt( i ) ;
                if( c == ' ' || c == '　' || c == '\r' ||
                    c == '\n' || c == '\t' ) {
                    notFirst = true ;
                    break ;
                }
            }
            if( notFirst == true ) {
                StringBuilder buf = new StringBuilder() ;
                if( len > 1 ) {
                    for( int i = 0 ; i < len ; i ++ ) {
                        if( i != 0 ) {
                            buf.append( "(" ) ;
                        }
                        buf.append( lst.get( i ) ) ;
                    }
                }
                else if( len == 1 ) {
                    buf.append( lst.get( 0 ) ) ;
                }
                string = buf.toString().trim() ;
            }
            else {
                StringBuilder buf = new StringBuilder() ;
                buf.append( lst.get( 0 ) ).append( " " ) ;
                if( len > 2 ) {
                    for( int i = 1 ; i < len ; i ++ ) {
                        if( i != 1 ) {
                            buf.append( "(" ) ;
                        }
                        buf.append( lst.get( i ) ) ;
                    }
                }
                else if( len == 2 ) {
                    buf.append( lst.get( 1 ) ) ;
                }
                string = buf.toString().trim() ;
            }
        }
        else {
            if( lst.get( len-1 ).length() <= 0 || ";".equals( lst.get( len-1 ) ) ) {
                len -- ;
            }
            StringBuilder buf = new StringBuilder() ;
            for( int i = 0 ; i < len ; i ++ ) {
                buf.append( lst.get( i ) ) ;
                buf.append( ")" ) ;
            }
            string = buf.toString().trim() ;
            if( scope == 0 && string.endsWith( ")" ) ) {
                string = string.substring( 0,string.length() - 1 ) + " ;" ;
            }
        }
        return string ;
    }
    
    /**
     * メソッドチェック用.
     */
    private static final boolean check( String string,int no ) {
        int len = string.length() ;
        if( len <= no+1 ) {
            return false ;
        }
        char c = string.charAt( no+1 ) ;
        if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ) {
            return true ;
        }
        return false ;
    }
    
    /**
     * 括弧の間にスペースをセット.
     */
    private static final String pushSpace( String string ) {
        if( string == null || ( string = string.trim() ).length() <= 0 ) {
            return "" ;
        }
        int cote = -1 ;
        char b = 0 ;
        int len = string.length() ;
        StringBuilder buf = new StringBuilder( ( int )( string.length()*1.25f ) ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            char c = string.charAt( i ) ;
            if( cote != -1 ) {
                if( c == cote && ( ( i != 0 && b != '\\' ) || i == 0 ) ) {
                    cote = -1 ;
                }
                buf.append( c ) ;
            }
            else if( ( c == '\'' || c == '\"' ) &&
                ( ( i != 0 && b != '\\' ) || i == 0 ) ) {
                cote = c ;
                buf.append( c ) ;
            }
            else if( c == '(' || c == ')' ) {
                if( b != ' ' ) {
                    buf.append( " " ).append( c ) ;
                }
                else {
                    buf.append( c ) ;
                }
            }
            else {
                buf.append( c ) ;
            }
            b = c ;
        }
        return buf.toString() ;
    }
}
