package org.maachang.util;

import java.net.URLEncoder;

import org.maachang.proxy.engine.net.RequestInfo;
import org.maachang.proxy.engine.net.http.HttpParams;

/**
 * 解析系ユーティリティ.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class AnalysisUtil {
    
    private static final String DEF_CHARSET = "UTF8" ;
    
    /**
     * ShiftJis - MS932変換表.
     */
    private static final char[] SJIS_MS932_MAP = {
        '\u301C', '\uFF5E',
        '\u2016', '\u2225',
        '\u2212', '\uFF0D',
        '\u00A2', '\uFFE0',
        '\u00A3', '\uFFE1',
        '\u00AC', '\uFFE2'
    } ;
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,String data,int off )
        throws Exception {
        return binaryIndexOf( binary,data,DEF_CHARSET,off,binary.length ) ;
    }
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,String data,int off,int len )
        throws Exception {
        return binaryIndexOf( binary,data,DEF_CHARSET,off,len ) ;
    }
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,String data,String charset,int off )
        throws Exception {
        return binaryIndexOf( binary,data,charset,off,binary.length ) ;
    }
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,String data,String charset,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 ||
            data == null || data.length() <= 0 ) {
            return -1 ;
        }
        return binaryIndexOf( binary,data.getBytes( charset ),off,len ) ;
    }
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,byte[] data,int off )
        throws Exception {
        return binaryIndexOf( binary,data,off,binary.length ) ;
    }
    
    /**
     * バイナリ検索.
     */
    public static int binaryIndexOf( byte[] binary,byte[] data,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 ||
            data == null || data.length <= 0 ) {
            return -1 ;
        }
        int dataLen = data.length ;
        if( len <= 0 || len >= binary.length ) {
            len = binary.length ;
        }
        int ret = -1 ;
        for( int i = off ; i < len ; i ++ ) {
            if( binary[ i ] == data[ 0 ] && i+dataLen <= len ) {
                ret = i ;
                for( int j = 1 ; j < dataLen ; j ++ ) {
                    if( binary[ i+j ] != data[ j ] ) {
                        ret = -1 ;
                        break ;
                    }
                }
                if( ret != -1 ) {
                    return ret ;
                }
            }
        }
        return -1 ;
    }
    
    /**
     * パラメータ解析.
     * @param request 対象のリクエストを設定します.
     * @return HttpParams パラメータ内容が返されます.
     * @exception Exception 例外.
     */
    public static final HttpParams convertRequestParams( RequestInfo request )
        throws Exception {
        return convertRequestParams( request,DEF_CHARSET ) ;
    }
    
    /**
     * パラメータ解析.
     * @param request 対象のリクエストを設定します.
     * @param charset 対象のキャラクタセットを設定します.
     * @return HttpParams パラメータ内容が返されます.
     * @exception Exception 例外.
     */
    public static final HttpParams convertRequestParams( RequestInfo request,String charset )
        throws Exception {
        HttpParams ret = new HttpParams() ;
        AnalysisUtil.convertAnalysisQuery( ret,request,charset ) ;
        AnalysisUtil.convertAnalysisBody( ret,request,charset ) ;
        return ret ;
    }
    
    /**
     * URLクエリパラメータを解析して、パラメータとして取得.
     */
    public static final HttpParams convertAnalysisQuery( RequestInfo request )
        throws Exception {
        return convertAnalysisQuery( request,DEF_CHARSET ) ;
    }
    /**
     * URLクエリパラメータを解析して、パラメータとして取得.
     */
    public static final HttpParams convertAnalysisQuery( RequestInfo request,String charset )
        throws Exception {
        HttpParams ret = new HttpParams() ;
        convertAnalysisQuery( ret,request,charset ) ;
        return ret ;
    }
    
    /**
     * URLクエリパラメータを解析して、パラメータとして取得.
     */
    public static final void convertAnalysisQuery( HttpParams out,RequestInfo request )
        throws Exception {
        convertAnalysisQuery( out,request,DEF_CHARSET ) ;
    }
    
    /**
     * URLクエリパラメータを解析して、パラメータとして取得.
     */
    public static final void convertAnalysisQuery( HttpParams out,RequestInfo request,String charset )
        throws Exception {
        String query = HttpdUtil.getQueryValue( request.getUrl() ) ;
        if( query != null && ( query = query.trim() ).length() > 0) {
            HttpdUtil.convertQueryByParams( out,query,charset ) ;
        }
    }
    
    /**
     * POST(Body)を解析して、パラメータとして取得.
     */
    public static final HttpParams convertAnalysisBody( RequestInfo request )
        throws Exception {
        return convertAnalysisBody( request,DEF_CHARSET ) ;
    }
    
    /**
     * POST(Body)を解析して、パラメータとして取得.
     */
    public static final HttpParams convertAnalysisBody( RequestInfo request,String charset )
        throws Exception {
        HttpParams ret = new HttpParams() ;
        convertAnalysisBody( ret,request,charset ) ;
        return ret ;
    }
    
    /**
     * POST(Body)を解析して、パラメータとして取得.
     */
    public static final void convertAnalysisBody( HttpParams out,RequestInfo request )
        throws Exception {
        convertAnalysisBody( out,request,DEF_CHARSET ) ;
    }
    
    /**
     * POST(Body)を解析して、パラメータとして取得.
     */
    public static final void convertAnalysisBody( HttpParams out,RequestInfo request,String charset )
        throws Exception {
        if( request.getBody() != null && request.getBody().length > 0 ) {
            String body = new String( request.getBody(),"ISO-8859-1" ) ;
            if( body != null && ( body = body.trim() ).length() > 0) {
                HttpdUtil.convertQueryByParams( out,body,charset ) ;
            }
        }
    }
    
    /**
     * 指定パラメータ内容を指定charsetで変換.
     */
    public static final String convertBodyByCharset( String body,String srcCharset,String destCharset )
        throws Exception {
        if( body == null || ( body = body.trim() ).length() <= 0 ) {
            return null ;
        }
        HttpParams params = new HttpParams() ;
        HttpdUtil.convertQueryByParams( params,body,srcCharset ) ;
        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 ++ ) {
                if( cnt != 0 ) {
                    buf.append( "&" ) ;
                }
                int lenJ = params.size( keys[ i ] ) ;
                for( int j = 0 ; j < lenJ ; j ++ ) {
                    if( cnt != 0 ) {
                        buf.append( "&" ) ;
                    }
                    String v = ( String )params.get( keys[ i ],j ) ;
                    if( v == null || ( v = v.trim() ).length() <= 0 ) {
                        buf.append( keys[ i ] ).append( "=" ) ;
                    }
                    else {
                        buf.append( keys[ i ] ).append( "=" ).
                            append( URLEncoder.encode( v,destCharset ) ) ;
                    }
                    cnt ++ ;
                }
            }
            return buf.toString() ;
        }
        return null ;
    }
    
    /** 数値変換. */
    public static final int convertInt( String code ) {
        int ret = -1 ;
        try {
            ret = Integer.parseInt( code ) ;
        } catch( Exception e ) {
            ret = -1 ;
        }
        return ret ;
    }
    
    /** 指定URLの？を省く. */
    public static String cutGetParam( String url ) {
        int x = url.indexOf( "?" ) ;
        if( x <= 0 ) {
            return url ;
        }
        return url.substring( 0,x ) ;
    }
    
    /**
     * 指定文字コードをMS932に変換.
     */
    public static final char toMS932(char c) {
        int len = SJIS_MS932_MAP.length ;
        for (int i = 0; i < len ; i += 2) {
            if (c == SJIS_MS932_MAP[i]) {
                c = SJIS_MS932_MAP[i + 1];
                break;
            }
        }
        return c;
    }
    
    /**
     * 指定文字コードをShiftJISに変換.
     */
    public static final char toSJIS(char c) {
        int len = SJIS_MS932_MAP.length ;
        for (int i = 0; i < len ; i += 2) {
            if (c == SJIS_MS932_MAP[i + 1]) {
                c = SJIS_MS932_MAP[i];
                break;
            }
        }
        return c;
    }
    
    /**
     * 指定文字列をMS932に変換.
     */
    public static final String toMS932(String str) {
        if (str == null) {
            return null;
        }
        char[] c = new char[str.length()];
        int len = str.length() ;
        for (int i=0; i<len; i++) {
            c[i] = toMS932(str.charAt(i));
        }
        return new String(c);
    }
    
    /**
     * 指定文字列をShiftJISに変換.
     */
    public static final String toSJIS(String str) {
        if (str == null) {
            return null;
        }
        char[] c = new char[str.length()];
        int len = str.length() ;
        for (int i=0; i<len; i++) {
            c[i] = toSJIS(str.charAt(i));
        }
        return new String(c);
    }
    
    /**
     * バイナリの内容をバイナリ変換.
     */
    public static final String toStringByBinary( byte[] bin ) {
        if( bin == null || bin.length <= 0 ) {
            return "" ;
        }
        StringBuilder buf = new StringBuilder() ;
        int len = bin.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                buf.append( "," ) ;
            }
            buf.append( "0x" ).append( Integer.toHexString( ( int )( bin[ i ] & 0x000000ff ) ) ) ;
        }
        return buf.toString() ;
    }
}
