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

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.maachang.comet.ServiceDef;
import org.maachang.comet.conf.PackagesConfig;
import org.maachang.comet.httpd.HttpdBinary;
import org.maachang.comet.httpd.HttpdParams;
import org.maachang.comet.httpd.HttpdResponse;
import org.maachang.comet.httpd.engine.HttpdUtil;
import org.maachang.manager.GlobalManager;
import org.maachang.util.StringUtil;

/**
 * 内部JS関連定義.
 * 
 * @version 2007/08/24
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class JsDef {
    private JsDef() {
    }
    
    /**
     * JS定義名 : リミットオプション.
     */
    public static final String[] JS_OPTION = { "$limit","$offset","$order" } ;
    
    /**
     * JS変数名 : 強制終了時のResult.
     */
    public static final String JS_EXIT_BY_RESULT = "_$_$exit$of$result$_$_" ;
    
    /**
     * 指定InputStreamをHttpdResponseに出力.
     * <BR><BR>
     * 指定InputStreamをHttpdResponseに出力します.
     * <BR>
     * @param response 対象のレスポンスを設定します.
     * @parm data 対象のInputStreamを設定します.
     * @exception Exception 例外.
     */
    public static final void putResponseByData( HttpdResponse response,InputStream data )
        throws Exception {
        if( response == null || data == null ) {
            if( data != null ) {
                try {
                    data.close() ;
                } catch( Exception e ) {
                }
            }
            return ;
        }
        BufferedInputStream bi = null ;
        if( ( data instanceof BufferedInputStream ) == false ) {
            bi = new BufferedInputStream( data ) ;
        }
        else {
            bi = ( BufferedInputStream )data ;
        }
        OutputStream output = null ;
        try {
            output = response.getOutput() ;
            byte[] bin = new byte[ 512 ] ;
            for( ;; ) {
                int len = bi.read( bin ) ;
                if( len <= 0 ) {
                    if( len <= -1 ) {
                        break ;
                    }
                    continue ;
                }
                output.write( bin,0,len ) ;
            }
            bi.close() ;
            bi = null ;
        } catch( Exception e ) {
            throw e ;
        } finally {
            if( bi != null ) {
                try {
                    bi.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
    /**
     * 指定オブジェクト配列の長さを取得.
     * <BR><BR>
     * 指定オブジェクト配列の長さを取得します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @return int 配列の長さが返されます.
     */
    public static final int arrayLength( Object[] objs ) {
        if( objs == null ) {
            return 0 ;
        }
        return objs.length ;
    }
    
    /**
     * 指定オブジェクト配列の項番を指定して配列内容を取得.
     * <BR><BR>
     * 指定オブジェクト配列の項番を指定して配列内容を取得します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @param no 対象の項番を設定します.
     * @return Object 対象のオブジェクトが返されます.
     */
    public static final Object getArray( Object[] objs,int no ) {
        if( objs == null ) {
            return null ;
        }
        return objs[ no ] ;
    }
    
    /**
     * 指定オブジェクト配列の項番を指定して配列内容を設定.
     * <BR><BR>
     * 指定オブジェクト配列の項番を指定して配列内容を設定します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @param no 対象の項番を設定します.
     * @param value 対象の項番に内容を設定します.
     */
    public static final void setArray( Object[] objs,int no,Object value ) {
        if( objs == null ) {
            return ;
        }
        objs[ no ] = value ;
    }
    
    /**
     * 対象がバイナリであるかチェック.
     * <BR><BR>
     * 対象がバイナリであるかチェックします.
     * <BR>
     * @param value チェック対象の情報を設定します.
     * @return boolean [true]の場合、バイナリです.
     */
    public static final boolean isBinary( Object value ) {
        if( value == null ) {
            return false ;
        }
        if( value instanceof byte[] ) {
            return true ;
        }
        return false ;
    }
    
    /**
     * 指定オブジェクト配列の長さを取得.
     * <BR><BR>
     * 指定オブジェクト配列の長さを取得します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @return int 配列の長さが返されます.
     */
    public static final int binaryLength( byte[] objs ) {
        if( objs == null ) {
            return 0 ;
        }
        return objs.length ;
    }
    
    /**
     * 指定オブジェクト配列の項番を指定して配列内容を取得.
     * <BR><BR>
     * 指定オブジェクト配列の項番を指定して配列内容を取得します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @param no 対象の項番を設定します.
     * @return Object 対象のオブジェクトが返されます.
     */
    public static final Object getBinary( byte[] objs,int no ) {
        if( objs == null ) {
            return null ;
        }
        return objs[ no ] ;
    }
    
    /**
     * 指定オブジェクト配列の項番を指定して配列内容を設定.
     * <BR><BR>
     * 指定オブジェクト配列の項番を指定して配列内容を設定します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @param no 対象の項番を設定します.
     * @param value 対象の項番に内容を設定します.
     */
    public static final void setBinaryByByte( byte[] objs,int no,byte value ) {
        if( objs == null ) {
            return ;
        }
        objs[ no ] = value ;
    }
    
    /**
     * 指定オブジェクト配列の項番を指定して配列内容を設定.
     * <BR><BR>
     * 指定オブジェクト配列の項番を指定して配列内容を設定します.
     * <BR>
     * @param objs 対象の配列を設定します.
     * @param no 対象の項番を設定します.
     * @param value 対象の項番に内容を設定します.
     */
    public static final void setBinary( byte[] objs,int no,Object value ) {
        if( objs == null ) {
            return ;
        }
        if( value instanceof String ) {
            try {
                long data = Long.parseLong( ( String )value ) ;
                objs[ no ] = ( byte )( data & 0x000000ff ) ;
            }catch( Exception e ) {
            }
        }
        else if( value instanceof Short ) {
            objs[ no ] = ( byte )( ( ( Short )value ).shortValue() & 0x000000ff ) ;
        }
        else if( value instanceof Integer ) {
            objs[ no ] = ( byte )( ( ( Integer )value ).intValue() & 0x000000ff ) ;
        }
        else if( value instanceof Long ) {
            objs[ no ] = ( byte )( ( ( Long )value ).longValue() & 0x000000ff ) ;
        }
    }
    
    /**
     * バイナリを生成.
     * <BR><BR>
     * バイナリ情報を生成します.
     * <BR>
     * @param size 対象のバイナリ情報を設定します.
     * @return byte[] 対象のバイナリ情報が返されます.
     */
    public static final byte[] createBinary( int size ) {
        if( size <= 0 ) {
            return null ;
        }
        return new byte[ size ] ;
    }
    
    /**
     * 指定バイナリを文字列に変換.
     * <BR><BR>
     * 指定バイナリを文字列に変換します.
     * <BR>
     * @param binary 対象のバイナリを設定します.
     * @param charset 対象のキャラクタセットを設定します.
     * @return String 変換された文字列が返されます.
     * @exception Exception 例外.
     */
    public static final String convertBinaryByString( byte[] binary,String charset )
        throws Exception {
        if( binary == null || binary.length <= 0 ) {
            return "" ;
        }
        if( charset == null || ( charset = charset.trim() ).length() <= 0 ) {
            charset = "UTF8" ;
        }
        return new String( binary,charset ) ;
    }
    
    /**
     * 指定文字列をバイナリに変換.
     * <BR><BR>
     * 指定文字列をバイナリに変換します.
     * <BR>
     * @param string 対象の文字列を設定します.
     * @param charset 対象のキャラクタセットを設定します.
     * @return byte[] 対象のバイナリが返されます.
     */
    public static final byte[] convertStringByBinary( String string,String charset )
        throws Exception {
        if( string == null || string.length() <= 0 ) {
            return new byte[ 0 ] ;
        }
        if( charset == null || ( charset = charset.trim() ).length() <= 0 ) {
            charset = "UTF8" ;
        }
        return string.getBytes( charset ) ;
    }
    
    /**
     * バイナリ内容を表示.
     * <BR><BR>
     * バイナリ内容を表示します.
     * <BR>
     * @param binary 対象のバイナリ情報を設定します.
     * @return String 表示対象内容が返されます.
     */
    public static final String toBinaryString( byte[] binary ) {
        if( binary == null || binary.length <= 0 ) {
            return "" ;
        }
        StringBuilder buf = new StringBuilder() ;
        int len = binary.length ;
        buf.append( "[" ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                buf.append( "," ) ;
            }
            String c = Integer.toHexString( ( binary[ i ] & 0x000000ff ) ) ;
            if( c.length() == 1 ) {
                c = "0" + c ;
            }
            buf.append( "0x" ).append( c ) ;
        }
        buf.append( "]" ) ;
        return buf.toString() ;
    }
    
    /**
     * 指定されたプリミティブ型に変換.
     * <BR><BR>
     * 指定されたプリミティブ型に変換します.
     * <BR>
     * @parma type 対象のタイプを設定します.
     * @param value 変換対象の内容を設定します.
     * @return Object 変換された内容が返されます.
     */
    public static final Object convertPrimitive( String type,Object value ) {
        if( type == null || ( type = type.trim() ).length() <= 0 ) {
            return value ;
        }
        type = type.toLowerCase() ;
        if( "bool".equals( type ) || "boolean".equals( type ) ) {
            return new Boolean( String.valueOf( value ) ) ;
        }
        if( "char".equals( type ) || "character".equals( type ) ) {
            String s = String.valueOf( value ) ;
            if( s != null && s.length() > 0 ) {
                return new Character( s.charAt( 0 ) ) ;
            }
        }
        if( "short".equals( type ) ) {
            return new Short( String.valueOf( value ) ) ;
        }
        if( "int".equals( type ) || "integer".equals( type ) ) {
            return new Integer( String.valueOf( value ) ) ;
        }
        if( "long".equals( type ) ) {
            return new Long( String.valueOf( value ) ) ;
        }
        if( "float".equals( type ) ) {
            return new Float( String.valueOf( value ) ) ;
        }
        if( "double".equals( type ) ) {
            return new Double( String.valueOf( value ) ) ;
        }
        return value ;
    }
    
    /**
     * スクリプトデフォルトパッケージIDを取得.
     * <BR><BR>
     * スクリプトデフォルトパッケージIDを取得します.
     * <BR>
     * @return int スクリプトデフォルトパッケージIDが返されます.
     */
    public static final int getDefaultPackageId() {
        PackagesConfig pconf = ( PackagesConfig )GlobalManager.getValue(
            ServiceDef.MANAGER_BY_PACKAGES ) ;
        if( pconf == null ) {
            return -1 ;
        }
        return pconf.getUpdateId() ;
    }
    
    /**
     * スクリプトデフォルトパッケージを取得.
     * <BR><BR>
     * スクリプトデフォルトパッケージを取得します.
     * <BR>
     * @param buf 追加対象のStringBuilderを設定します.
     * @return int スクリプトデフォルトパッケージ更新IDが返されます.
     * @exception Exception 例外.
     */
    public static final int pushDefaultPackage( StringBuilder buf )
        throws Exception {
        PackagesConfig pconf = ( PackagesConfig )GlobalManager.getValue(
            ServiceDef.MANAGER_BY_PACKAGES ) ;
        if( pconf == null ) {
            return -1 ;
        }
        int updateId = pconf.getUpdateId() ;
        String[] pk = pconf.getPackages() ;
        if( pk != null && pk.length > 0 ) {
            int len = pk.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( pk[i] != null ) {
                    buf.append( pk[i] ).
                        append( "\n" ) ;
                }
            }
        }
        return updateId ;
    }
    
    /**
     * SimpleDateFormatで、日付文字列を解析.
     * <BR>
     * @param format 対象のフォーマットを設定します.
     * @param value 変換する文字列を設定します.
     * @return long 変換された数値情報が返されます.
     * @exception Exception 例外.
     */
    public static final long convertDateFormatByString( String format,String value )
        throws Exception {
        long ret = -1L ;
        SimpleDateFormat fmt = new SimpleDateFormat( format,Locale.US ) ;
        synchronized( fmt ) {
            Date date = fmt.parse( value ) ;
            ret = date.getTime() ;
        }
        return ret ;
    }
    
    /**
     * SimpleDateFormatで、指定数値を文字列変換.
     * <BR>
     * @param format 対象のフォーマットを設定します.
     * @param value 変換する数値を設定します.
     * @return String 変換された文字列情報が返されます.
     * @exception Exception 例外.
     */
    public static final String convertDateFormatByLong( String format,long value )
        throws Exception {
        String ret = null ;
        SimpleDateFormat fmt = new SimpleDateFormat( format,Locale.US ) ;
        synchronized( fmt ) {
            ret = fmt.format( new Date( value ) ) ;
        }
        return ret ;
    }
    
    /**
     * 指定オブジェクトのクラス名を取得.
     * <BR><BR>
     * 指定オブジェクトのクラス名を取得します.
     * <BR>
     * @param obj 対象のオブジェクトを設定します.
     * @return String 対象のクラス名が返されます.
     */
    public static final String objectClassName( Object obj ) {
        if( obj == null ) {
            return "null" ;
        }
        return obj.getClass().getName() ;
    }
    
    /**
     * HTMLに表示可能な形式に変換.
     * <BR><BR>
     * @param mode [true]の場合、改行情報を&lt;br&gt;に変換します.
     * @param o 対象の情報を設定します.
     * @return String 変換された内容が返されます.
     */
    public static final String convertByViewHtml( boolean mode,String o ) {
        if( o == null || ( o = o.trim() ).length() <= 0 ) {
            return "" ;
        }
        o = StringUtil.changeString(o, "&", "&amp;");
        o = StringUtil.changeString(o, "<", "&lt;");
        o = StringUtil.changeString(o, ">", "&gt;");
        o = StringUtil.changeString(o, "\'", "&#039;");
        o = StringUtil.changeString(o, "\"", "&#034;");
        if( mode == true ) {
            o = StringUtil.changeString(o, "\n", "<br>");
        }
        return o ;
    }
    
    /**
     * JSON形式変換.
     * <BR><BR>
     * @param o 対象の情報を設定します.
     * @return String 変換された内容が返されます.
     */
    public static final String convertByJson( String o ) {
        if( o == null || o.length() <= 0 ) {
            return "" ;
        }
        o = StringUtil.upIndentCote( o ) ;
        o = StringUtil.changeString(o, "\r", "\\r");
        return StringUtil.changeString(o, "\n", "\\n");
    }
    
    /**
     * 文字列内コーテーション変換.
     * @param mode [true]の場合、ダブルコーテーションを変換します.
     * @param o 対象の文字列を設定します.
     * @return String 変換文字列が返されます.
     */
    public static final String convetByCote( boolean mode,String o ) {
        if( o == null || o.length() <= 0 ) {
            return "" ;
        }
        int bafCode = -1 ;
        char target = ( mode == false ) ? '\'' : '\"' ;
        int len = o.length() ;
        StringBuilder buf = new StringBuilder() ;
        for( int i = 0 ; i < len ; i ++ ) {
            char c = o.charAt( i ) ;
            if( target == c ) {
                if( bafCode == '\\' ) {
                    buf.append( "\\\\" ) ;
                }
                else {
                    buf.append( "\\" ) ;
                }
            }
            buf.append( c ) ;
            bafCode = c ;
        }
        return buf.toString() ;
    }
    
    /**
     * 改行を文字列変換する.
     * @param o 対象の情報を設定します.
     * @return String 変換された内容が返されます.
     */
    public static final String convertByEnter( String o ) {
        if( o == null || o.length() <= 0 ) {
            return "" ;
        }
        o = StringUtil.changeString(o, "\r", "\\r");
        return StringUtil.changeString(o, "\n", "\\n");
    }
    
    /**
     * 指定内容をバイナリで取得.
     * <BR><BR>
     * 指定内容をバイナリで取得します.
     * <BR>
     * @param o 対象のオブジェクトを設定します.
     * @return byte[] 対象のバイナリ情報が返されます.
     */
    public static final byte[] getParamByBinary( Object o ) {
        if( o == null || ( o instanceof HttpdBinary ) == false ) {
            return null ;
        }
        return ( ( HttpdBinary )o ).getBinary() ;
    }
    
    /**
     * 指定内容をバイナリサイズで取得.
     * <BR><BR>
     * 指定内容をバイナリサイズで取得します.
     * <BR>
     * @param o 対象のオブジェクトを設定します.
     * @return int 対象のバイナリサイズが返されます.
     */
    public static final int getParamByBinaryLength( Object o ) {
        if( o == null || ( o instanceof HttpdBinary ) == false ) {
            return -1 ;
        }
        return ( ( HttpdBinary )o ).getLength() ;
    }
    
    /**
     * 指定内容のオリジナル名を取得.
     * <BR><BR>
     * 指定内容のオリジナル名を取得します.
     * <BR>
     * @param o 対象のオブジェクトを設定します.
     * @return String 対象のオリジナル名が返されます.
     */
    public static final String getParamByOriginalName( Object o ) {
        if( o == null || ( o instanceof HttpdBinary ) == false ) {
            return null ;
        }
        return ( ( HttpdBinary )o ).getOriginalName() ;
    }
    
    /**
     * 指定パラメータをエンコード/デコード.
     * @param mode [true]の場合、エンコードします.
     * @param body 対象のBody情報を設定します.
     * @param charset 対象のcharsetを設定します.
     * @return String 変換された内容が返されます.
     */
    public static final String convertURL( boolean mode,String body,String charset ) {
        if( body == null || ( body = body.trim() ).length() <= 0 ) {
            return null ;
        }
        if( charset == null || ( charset = charset.trim() ).length() <= 0 ) {
            charset = "UTF8" ;
        }
        try {
            HttpdParams params = new HttpdParams() ;
            HttpdUtil.convertQueryByParams( params,body,charset ) ;
            StringBuilder buf = new StringBuilder() ;
            String[] keys = params.getKeys() ;
            if( keys != null ) {
                int len = keys.length ;
                int cnt = 0 ;
                for( int i = 0 ; i < len ; i ++ ) {
                    int lenJ = params.size( keys[ i ] ) ;
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        if( cnt != 0 ) {
                            buf.append( "&" ) ;
                        }
                        String v = ( String )params.getParam( keys[ i ],j ) ;
                        if( v == null || ( v = v.trim() ).length() <= 0 ) {
                            buf.append( keys[ i ] ).append( "=" ) ;
                        }
                        else if( mode == true ) {
                            buf.append( keys[ i ] ).append( "=" ).
                                append( URLEncoder.encode( v,charset ) ) ;
                        }
                        else if( mode == true ) {
                            buf.append( keys[ i ] ).append( "=" ).
                                append( v ) ;
                        }
                        cnt ++ ;
                    }
                }
                return buf.toString() ;
            }
        } catch( Exception e ) {
        }
        return null ;
    }
}

