package org.maachang.dao ;

import org.maachang.dao.dbms.Record;
import org.maachang.dao.dbms.RecordFactory;
import org.maachang.dao.dbms.RecordSequenceId;
import org.maachang.util.FileUtil;

/**
 * DaoSessionFactory.
 *
 * @version 2007/11/03
 * @author  masahito suzuki
 * @since MaachangDao 1.00
 */
public class DaoSessionFactory {
    
    /**
     * Javaシーケンス出力先ディレクトリ.
     */
    private static final String OUTPUT_SEQ_DIR = "./conf/jseq/" ;
    
    /**
     * Javaシーケンスファイル名.
     */
    private static final String SEQ_FILE = "jseq.seq" ;
    
    /**
     * Javaシーケンスメタファイル名.
     */
    private static final String SEQ_META_FILE = "jseq.meta" ;
    
    /**
     * シングルトン.
     */
    private static final DaoSessionFactory SNGL = new DaoSessionFactory() ;
    
    /**
     * レコードファクトリ.
     */
    private RecordFactory recordFactory = null ;
    
    /**
     * セッション管理情報.
     */
    private DaoSession[] daoSessions = null ;
    
    /**
     * Javaシーケンス.
     */
    private RecordSequenceId javaSeq = null ;
    
    /**
     * Javaシーケンス同期.
     */
    private final Object javaSeqSync = new Object() ;
    
    /**
     * コンストラクタ.
     */
    private DaoSessionFactory() {
    }
    
    /**
     * オブジェクトを取得.
     * <BR><BR>
     * オブジェクトを取得します.
     * <BR>
     * @return DaoSessionFactory オブジェクト情報が返されます.
     */
    public static final DaoSessionFactory getInstance() {
        return SNGL ;
    }
    
    /**
     * レコードファクトリを設定.
     * <BR><BR>
     * レコードファクトリを設定します.
     * <BR>
     * @param recordFactory 対象のレコードファクトリを設定します.
     */
    protected synchronized void setRecordFactory( RecordFactory recordFactory ) {
        this.recordFactory = recordFactory ;
        int len = recordFactory.getConnectionManager().getMax() ;
        this.daoSessions = new DaoSession[ len ] ;
        for( int i = 0 ; i < len ; i ++ ) {
            this.daoSessions[ i ] = new DaoSession() ;
        }
    }
    
    /**
     * レコードファクトリを取得.
     * @return RecordFactory レコードファクトリが返されます.
     */
    public synchronized RecordFactory getRecordFactory() {
        return recordFactory ;
    }
    
    /**
     * 新しいDaoSessionを取得.
     * <BR><BR>
     * 新しいDaoSessionを取得します.
     * <BR>
     * @return DaoSession 新しいDaoSessionが返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public DaoSession getDaoSession()
        throws MaachangDaoException {
        if( recordFactory != null ) {
            Record record = null ;
            DaoSession ret = null ;
            try {
                record = recordFactory.getRecord() ;
                ret = poolDaoSession( record ) ;
            } catch( Exception e ) {
                if( record != null ) {
                    try {
                        record.close() ;
                    } catch( Exception ee ) {
                    }
                }
                throw new MaachangDaoException( e ) ;
            }
            return ret ;
        }
        throw new MaachangDaoException(
            "初期化されていないか、条件が不正です" ) ;
    }
    
    /**
     * 新しいDaoSessionを取得.
     * <BR><BR>
     * 新しいDaoSessionを取得します.
     * <BR>
     * @param record 対象のレコードを設定します.
     * @return DaoSession 新しいDaoSessionが返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public DaoSession getDaoSession( Record record )
        throws MaachangDaoException {
        DaoSession s = poolDaoSession( record ) ;
        return s ;
    }
    
    /**
     * 新しいレコードを取得.
     * <BR><BR>
     * 新しいレコードを取得します.
     * <BR>
     * @return Record 新しいレコード情報が返されます.
     * @exception MaachangDaoException MaachangDao例外.
     */
    public Record getRecord()
        throws MaachangDaoException {
        if( recordFactory != null ) {
            Record ret = null ;
            try {
                ret = recordFactory.getRecord() ;
            } catch( Exception e ) {
                if( ret != null ) {
                    try {
                        ret.close() ;
                    } catch( Exception ee ) {
                    }
                }
                throw new MaachangDaoException( e ) ;
            }
            return ret ;
        }
        throw new MaachangDaoException(
            "初期化されていないか、条件が不正です" ) ;
    }
    
    /**
     * Java側でシーケンス発行を行う場合の処理.
     * @exception Exception 例外.
     */
    public void createJavaSequence() throws Exception {
        synchronized( javaSeqSync ) {
            if( javaSeq != null && javaSeq.isUse() ) {
                return ;
            }
            if( FileUtil.isDirExists( OUTPUT_SEQ_DIR ) == false ) {
                FileUtil.mkdirs( OUTPUT_SEQ_DIR ) ;
            }
            RecordSequenceId rs = new RecordSequenceId( OUTPUT_SEQ_DIR+SEQ_FILE,OUTPUT_SEQ_DIR+SEQ_META_FILE ) ;
            this.javaSeq = rs ;
        }
    }
    
    /**
     * シーケンス発行オブジェクトを破棄.
     */
    public void clearJavaSequence() {
        synchronized( javaSeqSync ) {
            if( javaSeq != null ) {
                javaSeq.destroy() ;
            }
            javaSeq = null ;
        }
    }
    
    /**
     * シーケンス発行オブジェクト用ファイルを削除.
     */
    public void deleteJavaSequence() {
        synchronized( javaSeqSync ) {
            if( javaSeq != null ) {
            	try {
	                javaSeq.allClear() ;
            	} catch( Exception e ) {
            	}
                javaSeq.destroy() ;
            }
            javaSeq = null ;
            try {
                FileUtil.removeFile( OUTPUT_SEQ_DIR+SEQ_META_FILE ) ;
            } catch( Exception e ) {
            }
        }
    }
    
    /**
     * シーケンス発行オブジェクトを取得.
     * @return RecordSequenceId シーケンス発行オブジェクトが返されます.
     */
    public RecordSequenceId getJavaSequence() {
        RecordSequenceId ret ;
        synchronized( javaSeqSync ) {
            ret = javaSeq ;
        }
        return ret ;
    }
    
    /**
     * シーケンス発行オブジェクトが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public boolean isJavaSequence() {
        boolean ret ;
        synchronized( javaSeqSync ) {
            ret = ( javaSeq != null ) ;
        }
        return ret ;
    }
    
    /**
     * １つのDaoSessionオブジェクトを取得.
     */
    private synchronized DaoSession poolDaoSession( Record record ) {
        int len = daoSessions.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            DaoSession s = daoSessions[ i ] ;
            if( s.isUsedObject() == false ) {
                s.create( record ) ;
                return s ;
            }
        }
        return new DaoSession() ;
    }
}
