package org.maachang.proxy.engine.admin;

import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.proxy.engine.VersionDef;
import org.maachang.proxy.engine.error.ErrorDef;
import org.maachang.proxy.engine.net.ResponseInfo;
import org.maachang.proxy.engine.net.http.HttpdTimestamp;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * MaachangProxy管理基本ページ.
 * 
 * @version 2008/03/26
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public abstract class AbstractAdminPage {
    protected static final Log LOG = LogFactory.getLog( AbstractAdminPage.class ) ;
    private static final String PAGE_DIR = "PAGE_DIR" ;
    private static final String SESSION = "SESSION" ;
    private static final String SESSION_KEY = "SESSION_KEY" ;
    private static final String PLUS = ".view" ;
    protected String pageInfo = null ;
    protected String pageDir = null ;
    
    /** コンストラクタ */
    private AbstractAdminPage() {
        
    }
    
    /**
     * コンストラクタ.
     * @param name 対象名を設定します.
     * @exception Exception 例外.
     */
    public AbstractAdminPage( String name ) throws Exception {
        this( name,null ) ;
    }
    
    /**
     * コンストラクタ.
     * @param name 対象名を設定します.
     * @param pageDir このオブジェクトのページ名を設定します.
     * @exception Exception 例外.
     */
    protected AbstractAdminPage( String name,String pageDir ) throws Exception {
        super() ;
        if( pageDir == null || pageDir.startsWith( AdminManager.ADMIN_URL ) == false ) {
            if( pageDir == null ) {
                pageDir = AdminManager.ADMIN_URL ;
            }
            else {
                if( pageDir.startsWith( "/" ) ) {
                    pageDir = pageDir.substring( 1 ) ;
                }
                pageDir = AdminManager.ADMIN_URL + pageDir ;
            }
        }
        if( pageDir.startsWith( "/" ) == false ) {
            pageDir = "/" + pageDir ;
        }
        if( pageDir.endsWith( "/" ) == false ) {
            pageDir = pageDir + "/" ;
        }
        
        String packageName = StringUtil.changeString( name,".","/" ) ;
        try {
            this.pageInfo = FileUtil.getScriptByResource( packageName+PLUS,"UTF8" ) ;
        } catch( Exception e ) {
            this.pageInfo = null ;
        }
        this.pageDir = pageDir ;
    }
    
    /**
     * 実行処理.
     * @param conn 対象のコネクション情報を設定します.
     * @exception Exception 例外.
     */
    public abstract void execution( AdminConnect conn )
        throws Exception ;
    
    /**
     * レスポンス送信情報を設定.
     * @param response 対象のレスポンスを設定します.
     * @param pageName 対象のページ名を設定します.
     * @exception AdminException 例外.
     */
    public void flushResponse( ResponseInfo response,int code )
        throws AdminException {
        if( response.getVersion() == null ) {
            try {
                response.setState( String.valueOf( code ) ) ;
                response.setStateMessage( ErrorDef.convertErrorMessage( code ) ) ;
                response.setVersion( "1.1" ) ;
                response.putHeader( "Server",VersionDef.getPrintServerName() ) ;
                response.putHeader( "Date",HttpdTimestamp.getNowTimestamp() ) ;
                response.putHeader( "Expires",HttpdTimestamp.getTimestamp( 0L ) );
                response.putHeader( "Pragma","no-cache" ) ;
                response.putHeader( "Last-Modified",HttpdTimestamp.getNowTimestamp() ) ;
                response.putHeader( "Cache-Control","private" ) ;
                response.addHeader( "Cache-Control","no-cache" ) ;
                response.addHeader( "Cache-Control","no-store" ) ;
                response.addHeader( "Cache-Control","max-age=0" ) ;
                response.putHeader( "Connection","close" ) ;
            } catch( Exception e ) {
                throw new AdminException( "管理画面処理中にエラーが発生しました:" + e.getMessage() ) ;
            }
        }
    }
    
    /**
     * ページディレクトリ名を取得..
     * @return String ページディレクトリ名が返されます.
     */
    public String getPageDir() {
        return pageDir ;
    }
    
    /**
     * 表示内容を生成.
     * @param conn 対象のコネクション情報を設定します.
     * @exception AdminException 例外.
     */
    public void viewController( AdminConnect conn )
        throws AdminException {
        if( pageInfo == null || pageInfo.length() <= 0 ) {
            return ;
        }
        HashMap<String,String> map = conn.getViewParams() ;
        ResponseInfo response = conn.getResponse() ;
        try {
            if( map == null ) {
                map = new HashMap<String,String>() ;
                conn.setViewParams( map ) ;
            }
            if( map.get( "errorMessage" ) == null ) {
                map.put( "errorMessage","" ) ;
            }
            StringBuilder buf = new StringBuilder() ;
            int p = 0 ;
            int b = 0 ;
            for( ;; ) {
                p = pageInfo.indexOf( "[%",b ) ;
                if( p <= -1 ) {
                    buf.append( pageInfo.substring( b ) ) ;
                    break ;
                }
                int x = pageInfo.indexOf( "%]",p+1 ) ;
                if( x <= -1 ) {
                    buf.append( pageInfo.substring( b,p+2 ) ) ;
                    b = p + 2 ;
                    continue ;
                }
                String key = pageInfo.substring( p+2,x ) ;
                buf.append( pageInfo.substring( b,p ) ) ;
                if( key != null && ( key = key.trim() ).length() > 0 ) {
                    String val = map.get( key ) ;
                    if( val != null && val.length() > 0 ) {
                        buf.append( val ) ;
                    }
                    else if( PAGE_DIR.equals( key ) ) {
                        buf.append( pageDir ) ;
                    }
                    else if( SESSION.equals( key ) ) {
                        buf.append( map.get( AdminManager.SESSION_ID ) ) ;
                    }
                    else if( SESSION_KEY.equals( key ) ) {
                        buf.append( AdminManager.SESSION_ID ) ;
                    }
                }
                b = x + 2 ;
            }
            String body = buf.toString() ;
            buf = null ;
            byte[] bin = body.getBytes( "UTF8" ) ;
            body = null ;
            response.setBody( bin ) ;
            response.putHeader( "Content-Type","text/html; charset=utf-8" ) ;
            response.putHeader( "Content-Length",String.valueOf( bin.length ) ) ;
        } catch( Exception e ) {
            throw new AdminException( "ページ[" + pageDir +
                "]の生成中にエラーが発生しました:" + e.getMessage() ) ;
        }
    }
    
    /**
     * フォワード処理.
     * @param conn 対象のコネクション情報を設定します.
     * @exception Exception 例外.
     */
    public void forward( AdminConnect conn )
        throws Exception {
        forward( false,conn,null,null ) ;
    }
    
    /**
     * フォワード処理.
     * @param conn 対象のコネクション情報を設定します.
     * @param path 対象のパスを設定します.
     * @exception Exception 例外.
     */
    public void forward( AdminConnect conn,String path )
        throws Exception {
        forward( false,conn,path,null ) ;
    }
    
    /**
     * フォワード処理.
     * @param boolean [true]の場合、実行処理も実施します.
     * @param conn 対象のコネクション情報を設定します.
     * @param path 対象のパスを設定します.
     * @exception Exception 例外.
     */
    public void forward( boolean mode,AdminConnect conn,String path )
        throws Exception {
        forward( mode,conn,path,null ) ;
    }
    
    /**
     * フォワード処理.
     * @param boolean [true]の場合、実行処理も実施します.
     * @param conn 対象のコネクション情報を設定します.
     * @param path 対象のパスを設定します.
     * @param key 対象のセッションキーを設定します.
     * @exception Exception 例外.
     */
    public void forward( boolean mode,AdminConnect conn,String path,String key )
        throws Exception {
        if( mode == true || conn.isForwardFlag() == false ) {
            if( key != null && ( key = key.trim() ).length() > 0 ) {
                conn.getViewParams().put( AdminManager.SESSION_ID,key ) ;
                conn.getParams().add( AdminManager.SESSION_ID,key ) ;
            }
            if( path == null || ( path = path.trim() ).length() <= 0 ) {
                viewController( conn ) ;
                conn.setForwardFlag( true ) ;
            }
            else {
                AbstractAdminPage page = conn.getManager().getPage( conn.getConf(),path ) ;
                if( mode == true ) {
                    conn.setForwardFlag( false ) ;
                    page.execution( conn ) ;
                    if( conn.isForwardFlag() == false ) {
                        page.viewController( conn ) ;
                        conn.setForwardFlag( true ) ;
                    }
                }
                else {
                    page.viewController( conn ) ;
                    conn.setForwardFlag( true ) ;
                }
            }
        }
    }
    
    /**
     * Adminページ内にリダイレクト.
     * @param conn 対象のコネクション情報を設定します.
     * @param url 対象のURLを設定します.
     */
    public void redirectAdmin( AdminConnect conn,String url )
        throws Exception {
        redirectAdmin( conn,url,null ) ;
    }
    
    /**
     * Adminページ内にリダイレクト.
     * @param conn 対象のコネクション情報を設定します.
     * @param url 対象のURLを設定します.
     * @param sessionKey 対象のセッションキーを設定します.
     */
    public void redirectAdmin( AdminConnect conn,String url,String sessionKey )
        throws Exception {
        if( url.startsWith( AdminManager.ADMIN_URL ) == false ) {
            if( url.startsWith("/") ) {
                url = url.substring( 1 ) ;
            }
            url = AdminManager.ADMIN_URL + url ;
        }
        redirect( conn,url,sessionKey ) ;
    }
    
    /**
     * リダイレクト.
     * @param conn 対象のコネクション情報を設定します.
     * @param url 対象のURLを設定します.
     */
    public void redirect( AdminConnect conn,String url )
        throws Exception {
        redirect( conn,url,null ) ;
    }
    
    /**
     * リダイレクト.
     * @param conn 対象のコネクション情報を設定します.
     * @param url 対象のURLを設定します.
     * @param sessionKey 対象のセッションキーを設定します.
     */
    public void redirect( AdminConnect conn,String url,String sessionKey )
        throws Exception {
        if( sessionKey == null ) {
            sessionKey = ( String )conn.getParams().get( AdminManager.SESSION_ID ) ;
        }
        if( sessionKey != null && conn.getSession().getSession( sessionKey ) != null ) {
            if( url.indexOf( "?" ) >= 0 ) {
                url += "&"+AdminManager.SESSION_ID+"="+sessionKey ;
            }
            else {
                url += "?"+AdminManager.SESSION_ID+"="+sessionKey ;
            }
        }
        flushResponse( conn.getResponse(),ErrorDef.HTTP11_303 ) ;
        conn.getResponse().putHeader( "Location",url ) ;
    }
    
    /**
     * セッションIDを取得.
     * @param conn 対象のコネクション情報を設定します.
     * @return String セッションIDが返されます.
     */
    public String getSessionId( AdminConnect conn ) {
        return ( String )conn.getParams().get( AdminManager.SESSION_ID ) ;
    }
    
    /**
     * セッションIDをGETパラメータ形式に変換.
     * @param conn 対象のコネクション情報を設定します.
     * @return String セッションIDが返されます.
     */
    public String getSessionIdByGetParam( AdminConnect conn ) {
        return AdminManager.SESSION_ID+"="+conn.getParams().get( AdminManager.SESSION_ID ) ;
    }
    
    /**
     * エラーメッセージを追加.
     * @param conn 対象のコネクションを設定します.
     * @param message 対象のエラーメッセージを設定します.
     */
    public void addErrorMessage( AdminConnect conn,String message ) {
        if( message == null || ( message = message.trim() ).length() <= 0 ) {
            return ;
        }
        HashMap<String,String> p = conn.getViewParams() ;
        String bef = p.get( "errorMessage" ) ;
        if( bef != null ) {
            bef += "<BR>"+message ;
        }
        else {
            bef = message ;
        }
        p.put( "errorMessage",bef ) ;
    }
    
    /**
     * ログインされていない場合に、ログイン画面に戻るかチェック.
     * @param conn 対象のコネクション情報を設定します.
     * @return boolean [true]の場合、ログイン画面に戻ります.
     * @exception Exception 例外.
     */
    public boolean isLogin( AdminConnect conn )
        throws Exception {
        if( conn.getConf().isAuthAdmin() == true ) {
            String key = ( String )conn.getParams().get( AdminManager.SESSION_ID ) ;
            if( conn.getSession().getSession( key ) == null ) {
                redirectAdmin( conn,"/" ) ;
                conn.setForwardFlag( true ) ;
                return true ;
            }
        }
        return false ;
    }
    
    /**
     * selectオプションを出力.
     * @param conn 対象のコネクション情報を設定します.
     * @param name 対象のselectオプション名を設定します.
     * @param options 対象のオプション群を設定します.
     * @exception Exception 例外.
     */
    public void outputOptions( AdminConnect conn,String name,Object... options )
        throws Exception {
        outputOptions( conn,name,null,options ) ;
    }
    
    /**
     * selectオプションを出力.
     * @param conn 対象のコネクション情報を設定します.
     * @param name 対象のselectオプション名を設定します.
     * @param value 対象のValue情報を設定します.
     * @param options 対象のオプション群を設定します.
     * @exception Exception 例外.
     */
    public void outputOptions( AdminConnect conn,String name,String value,Object... options )
        throws Exception {
        if( options == null || options.length <= 0 ) {
            return ;
        }
        StringBuilder buf = new StringBuilder() ;
        String data = ( String )conn.getParams().get( name ) ;
        if( data == null || data.length() <= 0 ) {
            data = value ;
        }
        int len = options.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            String[] vals = ( String[] )options[ i ] ;
            buf.append( "<option value='" ).append( vals[ 0 ] ).append( "'" ) ;
            if( vals[ 0 ].equals( data ) ) {
                buf.append( " selected" ) ;
            }
            buf.append( ">" ).append( vals[ 1 ] ) ;
        }
        String s = buf.toString() ;
        buf = null ;
        conn.getViewParams().put( name,s ) ;
    }
}

