package org.maachang.proxy.engine.net.server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.maachang.proxy.engine.conf.ProxyOption;
import org.maachang.proxy.engine.conf.ProxyOptions;
import org.maachang.proxy.engine.net.RequestInfo;

/**
 * プロキシ管理情報.
 * 
 * @version 2008/03/25
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class ProxyManager {
    
    /**
     * URL管理.
     */
    private List<ProxyInfo> man = null ;
    
    /**
     * コンストラクタ.
     */
    public ProxyManager() {
        man = Collections.synchronizedList( new ArrayList<ProxyInfo>() ) ;
    }
    
    /**
     * コンストラクタ.
     * @param options 対象のプロキシオプション群を設定します.
     * @exception Exception 例外.
     */
    public ProxyManager( ProxyOptions options )
        throws Exception {
        this() ;
        try {
            if( options == null || options.size() <= 0 ) {
                return ;
            }
            int len = options.size() ;
            for( int i = 0 ; i < len ; i ++ ) {
                ProxyOption opt = options.get( i ) ;
                this.add( opt ) ;
            }
        } catch( Exception e ) {
            man = null ;
            throw e ;
        }
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.clear() ;
        man = null ;
    }
    
    /**
     * 情報クリア.
     */
    public void clear() {
        if( man != null ) {
            int len = man.size() ;
            for( int i = 0 ; i < len ; i ++ ) {
                man.get( i ).destroy() ;
            }
            man.clear() ;
        }
    }
    
    /**
     * プロキシ情報を追加.
     * @param option 対象のProxyOption情報を設定します.
     * @exception Exception 例外.
     */
    public void add( ProxyOption option ) throws Exception {
        this.add( new ProxyInfo( option ) ) ;
    }
    
    /**
     * プロキシ情報を追加.
     * @param proxy 対象のProxy情報を設定します.
     * @exception Exception 例外.
     */
    public void add( ProxyInfo proxy ) throws Exception {
        if( proxy == null || proxy.isUse() == false ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( isURL( proxy.getUrl() ) == true ) {
            throw new IllegalArgumentException( "既に同じURL("+proxy.getName()+"が存在します" ) ;
        }
        if( isName( proxy.getName() ) == true ) {
            throw new IllegalArgumentException( "既に同じ名前("+proxy.getName()+"が存在します" ) ;
        }
        man.add( proxy ) ;
        proxy.startup() ;
    }
    
    /**
     * プロキシ情報を削除.
     * @param no 削除対象の項番を設定します.
     * @return ProxyInfo 削除されたProxyInfoが返されます.
     */
    public ProxyInfo remove( int no ) {
        if( no < 0 || no >= man.size() ) {
            return null ;
        }
        return man.remove( no ) ;
    }
    
    /**
     * プロキシ情報を削除.
     * @param name 削除対象の名前を設定します.
     * @return ProxyInfo 削除されたProxyInfoが返されます.
     */
    public ProxyInfo remove( String name ) {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            return null ;
        }
        int no = searchName( name ) ;
        if( no >= 0 ) {
            return man.remove( no ) ;
        }
        return null ;
    }
    
    /**
     * プロキシ情報を取得.
     * @param no 対象の項番を設定します.
     * @return ProxyInfo 対象のProxy情報が返されます.
     */
    public ProxyInfo get( int no ) {
        if( no < 0 || no >= man.size() ) {
            return null ;
        }
        return man.get( no ) ;
    }
    
    /**
     * 指定URLとマッチするプロキシ情報を取得.
     * @param request 対象のリクエスト情報を設定します.
     * @return ProxyInfo[] 対象のProxy情報が返されます.
     */
    public ProxyInfo get( RequestInfo request ) {
        if( request == null ) {
            return null ;
        }
        int no = searchURL( request.getHeader( "Host",0 ) ) ;
        if( no >= 0 ) {
            return man.get( no ) ;
        }
        return null ;
    }
    
    /**
     * 全体数を取得.
     * @return int 全体数が返されます.
     */
    public int size() {
        return man.size() ;
    }
    
    /**
     * オブジェクトが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public boolean isUse() {
        return man != null ;
    }
    
    /**
     * ProxyOptionsに変換.
     * @return ProxyOptions 変換されたProxyOptionsが返されます.
     */
    public ProxyOptions convertProxyOptions() {
        if( man == null || man.size() <= 0 ) {
            return new ProxyOptions() ;
        }
        int len = man.size() ;
        ProxyOptions ret = new ProxyOptions( len ) ;
        for( int i = 0 ; i < len ; i ++ ) {
            ret.set( man.get( i ).getOption(),i ) ;
        }
        return ret ;
    }
    
    /**
     * 指定名の内容が既に存在するかチェック.
     * @param name 対象の名前を設定します.
     * @return boolean [true]の場合は、指定名が存在します.
     */
    public boolean isName( String name ) {
        return ( searchName( name ) != -1 ) ;
    }
    
    /**
     * 指定名の内容が既に存在するかチェック.
     * @param name 対象の名前を設定します.
     * @return boolean [true]の場合は、指定名が存在します.
     */
    public boolean isURL( String name ) {
        return ( searchURL( name ) != -1 ) ;
    }
    
    /**
     * 指定名の内容のURLを取得.
     */
    private int searchName( String string ) {
        if( string == null || ( string = string.trim() ).length() <= 0 ) {
            return -1 ;
        }
        string = string.trim().toLowerCase() ;
        int len = man.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            ProxyInfo px = man.get( i ) ;
            if( string.equals( px.getName() ) ) {
                return i ;
            }
        }
        return -1 ;
    }
    
    /**
     * URLが存在するかチェック.
     */
    private int searchURL( String string ) {
        if( string == null || ( string = string.trim() ).length() <= 0 ) {
            return -1 ;
        }
        int len = man.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            ProxyInfo px = man.get( i ) ;
            if( string.equals( px.getUrl() ) ) {
                return i ;
            }
        }
        return -1 ;
    }
}
