package org.maachang.comet.conf;

import java.sql.ResultSet;
import java.util.HashMap;

import org.maachang.comet.httpd.engine.auth.HttpdAuthUsers;
import org.maachang.dao.dbms.DbUtil;
import org.maachang.dao.dbms.Record;
import org.maachang.dao.dbms.RecordFactory;

/**
 * データベース管理によるHttp認証用オブジェクト.
 * 
 * @version 2007/08/29
 * @author masahito suzuki
 * @since MaachangComet 1.00
 */
public class AuthUserByDatabase implements HttpdAuthUsers {
    
    /**
     * 再読み込み時間 : 最小値.
     */
    private static final long MIN_RELOAD_TIME = 15000L ;
    
    /**
     * 再読み込み時間 : 最大値.
     */
    private static final long MAX_RELOAD_TIME = 300000L ;
    
    /**
     * 再読み込み時間 : デフォルト値.
     */
    public static final long DEFAULT_RELOAD_TIME = 30000L ;
    
    /**
     * ユーザテーブル.
     */
    private String userTable = null ;
    
    /**
     * ユーザカラム名.
     */
    private String userColumn = null ;
    
    /**
     * パスワードカラム名.
     */
    private String passwdColumn = null ;
    
    /**
     * コネクション管理.
     */
    private RecordFactory recordFactory = null ;
    
    /**
     * 再読み込み間隔.
     */
    private long reloadTime = -1L ;
    
    /**
     * 前回読み込み時間.
     */
    private long beforeAccessTime = -1L ;
    
    /**
     * データ管理テーブル.
     */
    private HashMap<String,Object> temporaryUserTable = null ;
    
    /**
     * コンストラクタ.
     */
    private AuthUserByDatabase() {}
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * ユーザテーブルを設定してオブジェクトを生成します.
     * <BR>
     * @param recordFactory コネクション管理オブジェクトを設定します.
     * @param userTable 対象のユーザテーブルを設定します.
     * @param user 対象のユーザカラム名を設定します.
     * @param passwd 対象のパスワードカラム名を設定します.
     * @exception Exception 例外.
     */
    public AuthUserByDatabase( RecordFactory recordFactory,String userTable,String user,String passwd )
        throws Exception {
        this( recordFactory,userTable,user,passwd,DEFAULT_RELOAD_TIME ) ;
    }
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * ユーザテーブルを設定してオブジェクトを生成します.
     * <BR>
     * @param recordFactory コネクション管理オブジェクトを設定します.
     * @param userTable 対象のユーザテーブルを設定します.
     * @param user 対象のユーザカラム名を設定します.
     * @param passwd 対象のパスワードカラム名を設定します.
     * @param reloadTime 対象の再読み込み時間を設定します.
     * @exception Exception 例外.
     */
    public AuthUserByDatabase( RecordFactory recordFactory,String userTable,String user,String passwd,long reloadTime )
        throws Exception {
        if( recordFactory == null || userTable == null ||
            ( userTable = userTable.trim() ).length() <= 0 ||
            user == null || ( user = user.trim() ).length() <= 0 ||
            passwd == null || ( passwd = passwd.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( reloadTime <= MIN_RELOAD_TIME ) {
            reloadTime = MIN_RELOAD_TIME ;
        }
        else if( reloadTime >= MAX_RELOAD_TIME ) {
            reloadTime = MAX_RELOAD_TIME ;
        }
        this.userTable = userTable ;
        this.userColumn = user ;
        this.passwdColumn = passwd ;
        this.recordFactory = recordFactory ;
        this.reloadTime = reloadTime ;
    }
    
    /**
     * ユーザ名に対するパスワードを取得.
     * <BR><BR>
     * ユーザ名に対するパスワードを取得します.
     * <BR>
     * @param user ユーザ名を設定します.
     * @return String[] パスワードが返されます.<BR>
     *                  [null]が返された場合、対象ユーザは存在しません.
     * @exception Exception 例外.
     */
    public synchronized String[] getPassword( String user )
        throws Exception {
        if( user == null || ( user = user.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "ユーザ名が不正です" ) ;
        }
        this.reload() ;
        Object o = this.temporaryUserTable.get( user ) ;
        if( o == null ) {
            return null ;
        }
        if( o instanceof String ) {
            return new String[]{ ( String )o } ;
        }
        return ( String[] )o ;
    }
    
    /**
     * 指定ユーザ名が存在するかチェック.
     * <BR><BR>
     * 指定ユーザ名が存在するかチェックします.
     * <BR>
     * @param user 対象のユーザ名を設定します.
     * @return boolean [true]が返された場合、存在します.
     * @exception Exception 例外.
     */
    public boolean isUser( String user )
        throws Exception {
        if( user == null || ( user = user.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "ユーザ名が不正です" ) ;
        }
        this.reload() ;
        return ( this.temporaryUserTable.get( user ) == null ) ? false : true ;
    }
    
    /**
     * テーブルリロード.
     */
    private void reload() {
        if( beforeAccessTime+reloadTime <= System.currentTimeMillis() ) {
            Record record = null ;
            ResultSet result = null ;
            try {
                String sql = new StringBuilder().append( "select " ).
                    append( userColumn ).append( "," ).
                    append( passwdColumn ).append( " from " ).
                    append( DbUtil.convertJavaNameByDBName( userTable ) ).
                    append( " ;" ).toString() ;
                record = recordFactory.getRecord() ;
                result = record.executeQuery( sql ) ;
                
                if( result != null ) {
                    if( result.next() == false ) {
                        return ;
                    }
                    HashMap<String,Object> t = new HashMap<String,Object>() ;
                    for( ;; ) {
                        String user = result.getString( userColumn ) ;
                        String passwd = result.getString( passwdColumn ) ;
                        if( passwd == null || ( passwd = passwd.trim() ).length() <= 0 ) {
                            passwd = "" ;
                        }
                        Object o = null ;
                        if( ( o = t.get( user ) ) == null ) {
                            t.put( user,passwd ) ;
                        }
                        else {
                            if( o instanceof String ) {
                                String[] lst = new String[ 2 ] ;
                                lst[ 0 ] = ( String )o ;
                                lst[ 1 ] = passwd ;
                                t.put( user,lst ) ;
                            }
                            else {
                                String[] lst = ( String[] )o ;
                                int len = lst.length ;
                                String[] tmp = new String[ len+1 ] ;
                                System.arraycopy( lst,0,tmp,0,len ) ;
                                tmp[ len ] = passwd ;
                                t.put( user,tmp ) ;
                            }
                        }
                        if( result.next() == false ) {
                            break ;
                        }
                    }
                    this.beforeAccessTime = System.currentTimeMillis() ;
                    this.temporaryUserTable = t ;
                }
            } catch( Exception e ) {
                e.printStackTrace() ;
            } finally {
                if( result != null ) {
                    try {
                        result.close() ;
                    } catch( Exception e ) {
                    }
                }
                if( record != null ) {
                    record.close() ;
                }
            }
        }
    }
}
