package org.maachang.proxy.engine.net;

import java.util.ArrayList;

import org.maachang.util.ArrayBinary;

/**
 * レスポンス情報.
 * 
 * @version 2008/03/25
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class ResponseInfo {
    /** ステータス */
    private String state = null ;
    
    /** ステータスメッセージ */
    private String stateMessage = null ;
    
    /** HTTPバージョン. */
    private String version = null ;
    
    /** ヘッダ情報 */
    private ArrayList<ValueInfo> header = null ;
    
    /** Body情報. */
    private byte[] body = null ;
    
    /**
     * コンストラクタ.
     */
    public ResponseInfo() {
        header = new ArrayList<ValueInfo>() ;
        body = null ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        if( header != null ) {
            header.clear() ;
        }
        state = null ;
        stateMessage = null ;
        version = null ;
        header = null ;
        body = null ;
    }
    
    /**
     * state を取得.
     * @return state が返されます.
     */
    public String getState() {
        return state;
    }
    
    /**
     * state を設定.
     * @param state state を設定します.
     */
    public void setState(String state) {
        this.state = state;
    }
    
    /**
     * stateMessage を取得.
     * @return stateMessage が返されます.
     */
    public String getStateMessage() {
        return stateMessage;
    }
    
    /**
     * stateMessage を設定.
     * @param stateMessage stateMessage を設定します.
     */
    public void setStateMessage(String stateMessage) {
        this.stateMessage = stateMessage;
    }
    
    /**
     * version を取得.
     * @return version が返されます.
     */
    public String getVersion() {
        if( version != null && version.startsWith( "HTTP/" ) ) {
            return version.substring( "HTTP/".length() ) ;
        }
        return version;
    }
    
    /**
     * version を設定.
     * @param version version を設定します.
     */
    public void setVersion(String version) {
        if( version == null || ( version = version.trim() ).length() <= 0 ) {
            return ;
        }
        version = version.toUpperCase() ;
        if( version.startsWith( "HTTP/" ) == false ) {
            version = "HTTP/" + version ;
        }
        this.version = version;
    }
    
    /**
     * body を取得.
     * @return body が返されます.
     */
    public byte[] getBody() {
        return body;
    }
    
    /**
     * body を設定.
     * @param body body を設定します.
     */
    public void setBody(byte[] body) {
        this.body = body;
    }
    
    /**
     * ヘッダをセット.
     * @param key キー名を設定します.
     * @param value ヘッダ要素を設定します.
     */
    public void putHeader( String key,String value ) {
        if( header == null || key == null || ( key = key.trim() ).length() <= 0 ||
            value == null || ( value = value.trim() ).length() <= 0 ) {
            return ;
        }
        ValueInfo info = ValueInfo.getValueInfo( header,key ) ;
        if( info == null ) {
            info = new ValueInfo() ;
            info.setKey( key ) ;
            header.add( info ) ;
        }
        info.putValue( value ) ;
    }
    
    /**
     * ヘッダをセット.
     * @param key キー名を設定します.
     * @param value ヘッダ要素を設定します.
     */
    public void putHeader( String key,String[] value ) {
        if( header == null || key == null || ( key = key.trim() ).length() <= 0 ||
            value == null || value.length <= 0 ) {
            return ;
        }
        ValueInfo info = ValueInfo.getValueInfo( header,key ) ;
        if( info == null ) {
            info = new ValueInfo() ;
            info.setKey( key ) ;
            header.add( info ) ;
        }
        info.putValue( value ) ;
    }
    
    /**
     * ヘッダを追加.
     * @param key キー名を設定します.
     * @param value ヘッダ要素を設定します.
     */
    public void addHeader( String key,String value ) {
        ValueInfo.add( header,key,value ) ;
    }
    
    /**
     * ヘッダを削除.
     * @param key キー名を設定します.
     */
    public void removeHeader( String key ) {
        ValueInfo.remove( header,key ) ;
    }
    
    /**
     * ヘッダを削除.
     * @param key キー名を設定します.
     * @param no 削除項番を設定します.
     */
    public void removeHeader( String key,int no ) {
        ValueInfo.remove( header,key,no ) ;
    }
    
    /**
     * ヘッダを取得.
     * @param key キー名を設定します.
     * @return String[] 要素群が返されます.
     */
    public String[] getHeader( String key ) {
        return ValueInfo.get( header,key ) ;
    }
    
    /**
     * ヘッダを取得.
     * @param key キー名を設定します.
     * @param no 取得項番を設定します.
     * @return String 要素が返されます.
     */
    public String getHeader( String key,int no ) {
        return ValueInfo.get( header,key,no ) ;
    }
    
    /**
     * ヘッダサイズを取得.
     * @param key キー名を設定します.
     * @return int キー名に対する要素数が返されます.
     */
    public int headerSize( String key ) {
        return ValueInfo.size( header,key ) ;
    }
    
    /**
     * ヘッダサイズを取得.
     * @return int 登録されているヘッダ数が返されます.
     */
    public int headerSize() {
        return ValueInfo.size( header ) ;
    }
    
    /**
     * Bodyキャラクタセットを取得.
     * @return String Bodyのcharsetを取得します.
     */
    public String getBodyCharset() {
        String s = ValueInfo.get( header,"Content-Type",0 ) ;
        if( s != null ) {
            int p = s.indexOf( "charset=" ) ;
            if( p > 0 ) {
                return s.substring( p+"charset=".length() ) ;
            }
        }
        return null ;
    }
    
    /** 変換コード. */
    private static final String CHARSET = "ISO-8859-1" ;
    
    /** 改行コード. */
    private static final byte[] ENTER = new byte[] {(byte) 13, (byte) 10} ;
    
    /**
     * レスポンス情報をバイナリに変換.
     * <BR>
     * @param mode [true]の場合ヘッダだけをバイナリに変換します.
     * @return byte[] レスポンス情報がバイナリで返されます.
     * @exception Exception 例外.
     */
    public byte[] responseByBinary()
        throws Exception {
        return responseByBinary( true ) ;
    }
    
    /**
     * レスポンス情報をバイナリに変換.
     * <BR>
     * @param mode [true]の場合ヘッダだけをバイナリに変換します.
     * @return byte[] レスポンス情報がバイナリで返されます.
     * @exception Exception 例外.
     */
    public byte[] responseByBinary(boolean mode)
        throws Exception {
        if( state == null || version == null || header == null ) {
            return null ;
        }
        ArrayBinary ret = new ArrayBinary() ;
        ret.write( new StringBuilder().append( version ).append( " " ).append( state ).toString().getBytes( CHARSET ) ) ;
        if( stateMessage != null && stateMessage.length() > 0 ) {
            ret.write( new StringBuilder().append( " " ).append( stateMessage ).toString().getBytes( CHARSET ) ) ;
        }
        ret.write( ENTER ) ;
        int len = header.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            ValueInfo info = header.get( i ) ;
            int lenJ = info.size() ;
            if( lenJ > 0 ) {
                StringBuilder buf = new StringBuilder() ;
                buf.append( info.getKey() ).append( ": " ) ;
                for( int j = 0 ; j < lenJ ; j ++ ) {
                    if( j != 0 ) {
                        buf.append( ", " ) ;
                    }
                    buf.append( info.getValue( j ) ) ;
                }
                buf.append( "\r\n" ) ;
                ret.write( buf.toString().getBytes( CHARSET ) ) ;
                buf = null ;
            }
        }
        ret.write( ENTER ) ;
        if( mode == true ) {
            if( body != null && body.length > 0 ) {
                ret.write( body ) ;
                ret.write( ENTER ) ;
            }
        }
        return ret.getBinary() ;
    }
    
    /**
     * 内容を文字列に変換.
     * @return String
     */
    public String toString() {
        if( header == null ) {
            return "" ;
        }
        StringBuilder buf = new StringBuilder() ;
        buf.append( " state:" ).
            append( state ).append( "\n" ).
            append( " stateMessage:" ).
            append( stateMessage ).append( "\n" ).
            append( " version:" ).
            append( version ).append( "\n" ).
            append( " header:" ).
            append( header ).append( "\n" ) ;
            if( body != null ) {
                buf.append( " bodyLength:" ).
                    append( body.length ) ;
            }
        return buf.toString() ;
    }
}
