package org.maachang.comet.httpd.engine;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.OutputStream;

import org.maachang.comet.MaachangDef;
import org.maachang.comet.httpd.HttpdErrorDef;
import org.maachang.comet.httpd.HttpdExecutionManager;
import org.maachang.comet.httpd.HttpdRequest;
import org.maachang.comet.httpd.HttpdResponse;
import org.maachang.comet.httpd.HttpdStateException;
import org.maachang.comet.httpd.engine.script.ScriptDef;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * Httpdパブリックディレクトリ管理.
 * 
 * @version 2007/08/29
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
class HttpdPublicManager implements HttpdExecutionManager {
    
    /**
     * publicディレクトリ.
     */
    private String publicDirectory = null ;
    
    /**
     * コンストラクタ.
     */
    private HttpdPublicManager() {
        
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * カレントディレクトリを指定してオブジェクトを生成します.
     * <BR>
     * @param currentDirectory 対象のカレントディレクトリを設定します.
     * @exception Exception 例外.
     */
    public HttpdPublicManager( String currentDirectory )
        throws Exception {
        this.publicDirectory = new StringBuilder().
            append( ScriptDef.trimCurrentDirectory( currentDirectory ) ).
            append( MaachangDef.DIRECTORY_PUBLIC ).toString() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        publicDirectory = null ;
    }
    
    /**
     * 指定内容を取得.
     * <BR><BR>
     * 指定された内容を取得します.
     * <BR>
     * @param socket 対象のソケットチャネルを設定します.
     * @param request 対象のリクエストを設定します.
     * @return HttpdResponse 対象のレスポンスを設定します.
     * @exception Exception 例外.
     */
    public HttpdResponse get( HttpdRequest request )
        throws Exception {
        if( request == null ) {
            throw new HttpdStateException( HttpdErrorDef.HTTP11_400,
                "リクエストが不正です" ) ;
        }
        HttpdResponse ret = null ;
        try {
            String path = getUsePath( request.getUrlPath() ) ;
            pushUrl( request,path ) ;
            if( FileUtil.isFileExists( path ) == true ) {
                if( FileUtil.isRead( path ) == false ) {
                    throw new HttpdStateException( HttpdErrorDef.HTTP11_403,
                        "指定パス[" + path + "]は読み込み権限がありません" ) ;
                }
                ret = HttpdResponseInstance.createResponse( request,request.getUrlPath(),
                    request.getKeepAliveTimeout(),request.getKeepAliveCount() ) ;
                ret.setCookieSession( request ) ;
                ret.getHeader().addHeader( HttpdDef.VALUE_LAST_UPDATE,
                    HttpdTimestamp.getTimestamp( FileUtil.getLastTime( path ) ) ) ;
                ret.getHeader().addHeader( HttpdDef.VALUE_CONTENT_LENGTH,
                    String.valueOf( FileUtil.getLength( path ) ) ) ;
                ret.setHttpClose( false ) ;
                //ret.cacheOff() ;
                BufferedInputStream bi = null ;
                OutputStream out = null ;
                try {
                    bi = new BufferedInputStream( new FileInputStream( path ) ) ;
                    out = ret.getOutput() ;
                    //int cnt = 0 ;
                    //long tm = System.currentTimeMillis() ;
                    byte[] bin = new byte[ HttpdDef.BUFFER_SIZE ] ;
                    for( ;; ) {
                        int len = bi.read( bin ) ;
                        if( len <= 0 ) {
                            if( len <= -1 ) {
                                out.flush() ;
                                break ;
                            }
                        }
                        else {
                            //cnt += len ;
                            out.write( bin,0,len ) ;
                        }
                    }
                    //System.out.println( "# -url[" + request.getUrlPath() + "] 送信データ[" + cnt + "byte]" +
                    //    " time[" + ( System.currentTimeMillis()-tm ) + "msec]" ) ;
                    out.close() ;
                    out = null ;
                    bi.close() ;
                    bi = null ;
                } finally {
                    if( bi != null ) {
                        try {
                            bi.close() ;
                        } catch( Exception ee ) {
                        }
                    }
                    if( out != null ) {
                        try {
                            out.close() ;
                        } catch( Exception ee ) {
                        }
                    }
                }
            }
        } catch( Exception e ) {
            if( ret != null ) {
                ret.cancel() ;
            }
            throw e ;
        } catch( Error e ) {
            if( ret != null ) {
                ret.cancel() ;
            }
            throw e ;
        }
        return ret ;
    }
    
    /**
     * 指定パスのファイル日付を取得.
     * <BR><BR>
     * 指定パスのファイル日付を取得します.
     * <BR>
     * @parma path 対象のパスを設定します.
     * @return long ファイル日付が返されます.
     */
    public long getFileTime( String path ) {
        path = getUsePath( path ) ;
        if( FileUtil.isFileExists( path ) == true &&
            FileUtil.isRead( path ) == true ) {
            return FileUtil.getLastTime( path ) ;
        }
        return -1L ;
    }
    
    /**
     * 指定パスの内容が存在するかチェック.
     * <BR><BR>
     * 指定パスの内容が存在するかチェックします.
     * <BR>
     * @param path 対象のパスを設定します.
     * @return boolean [true]の場合、存在します.
     */
    public boolean isPath( String path ) {
        path = getUsePath( path ) ;
        if( FileUtil.isFileExists( path ) == true ) {
            return true ;
        }
        return false ;
    }
    
    /**
     * 指定パスの内容が読み込み可能かチェック.
     * <BR><BR>
     * 指定パスの内容が読み込み可能かチェックします.
     * <BR>
     * @param path 対象のパスを設定します.
     * @return boolean [true]の場合、読み込み可能です.
     */
    public boolean isRead( String path ) {
        path = getUsePath( path ) ;
        if( FileUtil.isFileExists( path ) == true ) {
            return FileUtil.isRead( path ) ;
        }
        return false ;
    }
    
    /**
     * 有効パスを取得.
     */
    private final String getUsePath( String path ) {
        if( HttpdDef.ADDRESS_BAR_ICON.endsWith( path ) == true ) {
            path = new StringBuilder().
                append( publicDirectory+MaachangDef.DIRECTORY_IMAGE ).
                append( HttpdDef.ADDRESS_BAR_ICON.substring( 1,HttpdDef.ADDRESS_BAR_ICON.length() ) ).
                toString() ;
            if( FileUtil.isFileExists( path ) == false ) {
                path = new StringBuilder().
                    append( publicDirectory ).
                    append( HttpdDef.ADDRESS_BAR_ICON.substring( 1,HttpdDef.ADDRESS_BAR_ICON.length() ) ).
                    toString() ;
            }
        }
        else {
            if( path.startsWith( "/" ) == true ) {
                path = path.substring( 1,path.length() ) ;
            }
            path = publicDirectory + path ;
            if( path.endsWith( "/" ) == true ) {
                int len = HttpdDef.INDEX_HTML.length ;
                for( int i = 0 ; i < len ; i ++ ) {
                    String ph = path + HttpdDef.INDEX_HTML[ i ] ;
                    if( FileUtil.isFileExists( ph ) == true ) {
                        path = ph ;
                        break ;
                    }
                }
            }
        }
        return path ;
    }
    
    /**
     * 対象パスを整形してURLとして設定.
     */
    private final void pushUrl( HttpdRequest request,String path )
        throws Exception {
        String str = FileUtil.getFullPath( MaachangDef.DIRECTORY_PUBLIC ) ;
        path = FileUtil.getFullPath( path ) ;
        path = path.substring( str.length(),path.length() ) ;
        path = StringUtil.changeString( path,"\\","/" ) ;
        if( path.startsWith( "/" ) == false ) {
            path = "/" + path ;
        }
        request.setUrlPath( path ) ;
    }
}

