package org.maachang.proxy.engine.admin;

import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;

import org.maachang.proxy.engine.conf.AccessManager;
import org.maachang.proxy.engine.conf.MaachangProxyConfig;
import org.maachang.proxy.engine.conf.RefusalAddressManager;
import org.maachang.proxy.engine.error.ErrorDef;
import org.maachang.proxy.engine.net.RequestInfo;
import org.maachang.proxy.engine.net.ResponseInfo;
import org.maachang.proxy.engine.net.http.ConnectionInfo;
import org.maachang.proxy.engine.net.http.HttpParams;
import org.maachang.proxy.engine.net.http.MultiPartForm;
import org.maachang.proxy.engine.net.server.ProxyManager;
import org.maachang.util.AnalysisUtil;

/**
 * MaachangProxy管理情報管理.
 * 
 * @version 2008/03/26
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class AdminManager {
    
    /**
     * 管理者アクセス用URL.
     */
    public static final String ADMIN_URL = "/@admin/" ;
    
    /**
     * セッションID.
     */
    public static final String SESSION_ID = "_admin_session" ;
    
    /**
     * ページ管理マネージャ.
     */
    private HashMap<String,AbstractAdminPage> manager = null ;
    
    /**
     * セッション管理マネージャ.
     */
    private AdminSession session = null ;
    
    /**
     * コンストラクタ.
     */
    public AdminManager() throws Exception {
        this.manager = new HashMap<String,AbstractAdminPage>() ;
        this.session = new AdminSession() ;
    }
    
    /**
     * 管理画面を処理して表示.
     * @param conf 対象のMaachangConfig情報を設定します.
     * @param proxyManager プロキシマネージャが設定されます.
     * @param refusal 対象の禁止アドレス管理を設定します.
     * @param accessManager 対象のアクセスマネージャを設定します.
     * @param conn コネクションオブジェクトを設定します.
     * @param seqId シーケンスIDを設定します.
     * @param request 対象のリクエスト情報を設定します.
     * @exception AdminException 管理例外.
     */
    public synchronized void execution( MaachangProxyConfig conf,
        ProxyManager proxyManager,RefusalAddressManager refusal,AccessManager accessManager,
        ConnectionInfo conn,int seqId,RequestInfo request ) throws AdminException {
        try {
            AbstractAdminPage page = getPage( conf,request.getUrl() ) ;
            ResponseInfo res = new ResponseInfo() ;
            HashMap<String,String> viewParams = new HashMap<String,String>() ;
            HttpParams httpParams = MultiPartForm.convertMultiPartForm( request,"UTF8" ) ;
            AnalysisUtil.convertAnalysisQuery( httpParams,request,"UTF8" ) ;
            String sessionKey = ( String )httpParams.get( SESSION_ID ) ;
            if( session.updateSession( sessionKey ) == false ) {
                sessionKey = "" ;
            }
            viewParams.put( SESSION_ID,sessionKey ) ;
            try {
                AdminConnect acn = new AdminConnect(
                    res,conf,proxyManager,refusal,
                    accessManager,this,session,request,
                    httpParams,seqId,viewParams ) ;
                page.execution( acn ) ;
                if( acn.isForwardFlag() == false ) {
                    page.forward( acn ) ;
                }
            } catch( AdminException ae ) {
                throw ae ;
            } catch( Exception e ) {
                throw new AdminException( this.getClass().getName()+
                    "の処理中にエラーが発生しました:" + e.getMessage() ) ;
            }
            page.flushResponse( res,ErrorDef.HTTP11_200 ) ;
            OutputStream outputStream = conn.getOutputStream() ;
            outputStream.write( res.responseByBinary() ) ;
            outputStream.flush() ;
            conn.setCloseFlag( true ) ;
        } catch( AdminException ae ) {
            throw ae ;
        } catch( Exception e ) {
            throw new AdminException( "指定ページ名["+request.getUrl()+
                "]の読み込みに失敗しました:" + e.getMessage() ) ;
        }
    }
    
    /**
     * ページオブジェクトを取得.
     */
    public synchronized AbstractAdminPage getPage( MaachangProxyConfig conf,String url )
        throws AdminException {
        try {
            if( url == null ) {
                url = "" ;
            }
            if( url.startsWith( ADMIN_URL ) == false ) {
                if( url.startsWith( "/" ) == true ) {
                    url = url.substring( 1 ) ;
                }
                url = ADMIN_URL + url ;
            }
            AbstractAdminPage ret = null ;
            String packageName = conf.getAdminUrl( url ) ;
            ret = manager.get( packageName ) ;
            if( ret == null ) {
                Class<?> clazz = Class.forName( packageName ) ;
                Class<? extends AbstractAdminPage>adminClazz = clazz.asSubclass(AbstractAdminPage.class);
                Constructor<? extends AbstractAdminPage> cst = adminClazz.getConstructor( String.class ) ;
                ret = cst.newInstance( packageName ) ;
                manager.put( packageName,ret ) ;
            }
            return ret ;
        } catch( Exception e ) {
            throw new AdminException( "指定ページ名["+url+
                "]の読み込みに失敗しました:"+e.getMessage() ) ;
        }
    }
}

