package org.maachang.comet.httpd ;

import java.util.ArrayList;
import java.util.HashMap;

import org.maachang.comet.httpd.engine.HttpdDef;
import org.maachang.conf.ConvIniParam;

/**
 * HTTPD-ヘッダ群.
 *
 * @version 2007/08/20
 * @author  masahito suzuki
 * @since   MaachangComet 1.00
 */
public class HttpdHeaders {
    
    /**
     * ヘッダ管理.
     */
    private HashMap<String,ArrayList<Object>> headers = null ;
    
    /**
     * コンストラクタ.
     */
    public HttpdHeaders() {
        
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.headers = null ;
    }
    
    /**
     * ヘッダオブジェクトをマージ.
     * <BR><BR>
     * 指定オブジェクトをマージします.
     * <BR>
     * @param header 対象のヘッダオブジェクトを設定します.
     */
    public void add( HttpdHeaders header ) {
        if( header == null || header.size() <= 0 ) {
            return ;
        }
        String[] keys = header.getKeys() ;
        if( keys != null ) {
            int len = keys.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                int lenJ = header.size( keys[ i ] ) ;
                for( int j = 0 ; j < lenJ ; j ++ ) {
                    this.addHeader( keys[ i ],header.getHeader( keys[ i ],j ) ) ;
                }
            }
        }
    }
    
    /**
     * パラメータ追加.
     * <BR><BR>
     * パラメータを追加します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     */
    public void addHeader( String key,String value ) {
        pushHeader( false,key,value ) ;
    }
    
    /**
     * パラメータ設定.
     * <BR><BR>
     * パラメータを設定します.<BR>
     * この条件で設定した場合、以前に登録したkeyに
     * 対する内容はクリアされます.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param value 対象の要素を設定します.
     */
    public void setHeader( String key,String value ) {
        pushHeader( true,key,value ) ;
    }
    
    /**
     * パラメータを追加／設定.
     */
    private void pushHeader( boolean mode,String key,String value ) {
        Object val = null ;
        if( key == null ) {
            return ;
        }
        if( headers == null ) {
            headers = new HashMap<String,ArrayList<Object>>() ;
        }
        if( value == null ) {
            val = new NullObjectHeader() ;
        }
        else {
            val = value ;
        }
        if( mode == true ) {
            ArrayList<Object> lst = new ArrayList<Object>() ;
            lst.add( val ) ;
            headers.put( key,lst ) ;
        }
        else {
            ArrayList<Object> lst = headers.get( key ) ;
            if( lst == null ) {
                lst = new ArrayList<Object>() ;
                headers.put( key,lst ) ;
            }
            lst.add( val ) ;
        }
    }
    
    /**
     * パラメータを削除.
     * <BR><BR>
     * 対象のパラメータを削除します.
     * <BR>
     * @param key 対象のキー名を設定します.
     */
    public void removeHeader( String key ) {
        if( key == null || headers == null ) {
            return ;
        }
        headers.remove( key ) ;
    }
    
    /**
     * パラメータを削除.
     * <BR><BR>
     * 対象のパラメータを削除します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param no 対象の項番を設定します.
     */
    public void removeHeader( String key,int no ) {
        if( key == null || headers == null || no < 0 ) {
            return ;
        }
        ArrayList<Object> lst = headers.get( key ) ;
        if( lst == null || lst.size() <= no ) {
            return ;
        }
        lst.remove( no ) ;
        if( lst.size() <= 0 ) {
            headers.remove( key ) ;
        }
    }
    
    /**
     * パラメータを取得.
     * <BR><BR>
     * 対象のパラメータを取得します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @return String 対象の要素情報が返されます.
     */
    public String getHeader( String key ) {
        return getHeader( key,0 ) ;
    }
    
    /**
     * パラメータを取得.
     * <BR><BR>
     * 対象のパラメータを取得します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @param no 対象の項番を設定します.
     * @return String 対象の要素情報が返されます.
     */
    public String getHeader( String key,int no ) {
        if( key == null || headers == null || no < 0 ) {
            return null ;
        }
        ArrayList<Object> lst = headers.get( key ) ;
        if( lst == null || lst.size() <= no ) {
            return null ;
        }
        Object ret = lst.get( no ) ;
        if( ret == null || ( ret instanceof NullObjectHeader ) ) {
            return null ;
        }
        return ( String )ret ;
    }
    
    /**
     * ヘッダ群を取得.
     * <BR><BR>
     * 対象のヘッダ群を取得します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @return HashMap<String,String> パースされたヘッダ情報が返されます.
     */
    public HashMap<String,String> getHeaders( String key ) {
        if( key == null || headers == null ) {
            return null ;
        }
        ArrayList<Object> lst = headers.get( key ) ;
        if( lst == null || lst.size() <= 0 ) {
            return null ;
        }
        HashMap<String,String> ret = new HashMap<String,String>() ;
        int len = lst.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            Object val = lst.get( i ) ;
            if( val == null || ( val instanceof NullObjectHeader ) ) {
                continue ;
            }
            String str = ( String )val ;
            int p = str.indexOf( "=" ) ;
            if( p != -1 ) {
                String k = str.substring( 0,p ) ;
                String v = str.substring( p+1,str.length() ) ;
                if( ( k = k.trim() ).length() > 0 && ( v = v.trim() ).length() > 0 ) {
                    if( v.startsWith( "\"" ) || v.startsWith( "\'" ) ) {
                        v = v.substring( 1,v.length()-1 ) ;
                    }
                    ret.put( k,v ) ;
                }
            }
            else {
                ret.put( str.trim(),"" ) ;
            }
        }
        if( ret.size() > 0 ) {
            return ret ;
        }
        return null ;
    }
    
    /**
     * パラメータ数を取得.
     * <BR><BR>
     * 対象のパラメータ数を取得します.
     * <BR>
     * @return int パラメータ数が返されます.
     */
    public int size() {
        return ( headers == null ) ? 0 : headers.size() ;
    }
    
    /**
     * パラメータ数を取得.
     * <BR><BR>
     * 対象のパラメータ数を取得します.
     * <BR>
     * @param key 対象のキー名を設定します.
     * @return int パラメータ数が返されます.
     */
    public int size( String key ) {
        if( key == null || headers == null ) {
            return 0 ;
        }
        ArrayList<Object> lst = headers.get( key ) ;
        if( lst == null ) {
            return 0 ;
        }
        return lst.size() ;
    }
    
    /**
     * キー名群を取得.
     * <BR><BR>
     * 対象のキー名群を取得します.
     * <BR>
     * @return String[] キー名群が返されます.
     */
    public String[] getKeys() {
        if( headers == null || headers.size() <= 0 ) {
            return null ;
        }
        Object[] keys = headers.keySet().toArray() ;
        if( keys != null ) {
            int len = keys.length ;
            String[] ret = new String[ len ] ;
            for( int i = 0 ; i < len ; i ++ ) {
                ret[ i ] = ( String )keys[ i ] ;
            }
            return ret ;
        }
        return null ;
    }
    
    /**
     * 指定キーが存在するかチェック.
     * <BR><BR>
     * 指定キーが存在するかチェックします.
     * <BR>
     * @param key 対象のキー名を取得します.
     * @return boolean [true]の場合、存在します.
     */
    public boolean isKey( String key ) {
        if( headers == null ) {
            return false ;
        }
        return ( headers.get( key ) == null ) ? false : true ;
    }
    
    /**
     * KeepAlive情報を取得.
     * <BR><BR>
     * KeepAlive情報を取得します.
     * <BR>
     * @return long KeepAlive情報が返されます.<BR>
     *              [-1L]が返された場合、KeepAlive情報は存在しません.
     */
    public long getKeepAlive() {
        String s = getHeader( "Keep-Alive" ) ;
        if( s != null ) {
            return ConvIniParam.getLong( s ) ;
        }
        return -1L ;
    }
    
    /**
     * コネクションヘッダにKeepAliveが存在するかチェック.
     * <BR><BR>
     * コネクションヘッダにKeepAliveが存在するかチェックします.
     * <BR>
     * @return boolean [true]の場合、KeepAliveが存在します.
     */
    public boolean isKeepAlive() {
        return ( getHeader( HttpdDef.VALUE_CONNECTION ) != null &&
            "keep-alive".equals( getHeader( HttpdDef.VALUE_CONNECTION ).toLowerCase() ) ) ;
    }
    
    /**
     * 文字列に変換.
     */
    public String toString() {
        String[] keys = getKeys() ;
        if( keys != null ) {
            StringBuilder buf = new StringBuilder() ;
            int len = keys.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( i != 0 ) {
                    buf.append( "," ) ;
                }
                ArrayList<Object> lst = headers.get( keys[ i ] ) ;
                if( lst != null ) {
                    int lenJ = lst.size() ;
                    for( int j = 0 ; j < lenJ ; j ++ ) {
                        if( j != 0 ) {
                            buf.append( "," ) ;
                        }
                        Object objs = lst.get( j ) ;
                        buf.append( " [" ).
                            append( keys[ i ] ).
                            append( ":" ) ;
                        if( objs == null || objs instanceof NullObjectHeader ) {
                            buf.append( "]" ) ;
                        }
                        else {
                            buf.append( objs ).append( "]" ) ;
                        }
                    }
                }
            }
            return buf.toString() ;
        }
        else {
            return "null" ;
        }
    }
}

/**
 * NULL用オブジェクト.
 */
class NullObjectHeader {
    
}

