package org.maachang.rawio.async ;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import org.maachang.rawio.Baseio;
import org.maachang.rawio.mapping.Mappingio;
import org.maachang.rawio.mapping.MappingioInstance;

/**
 * 非同期I/Oファクトリ.
 *  
 * @version 2008/06/12
 * @author  masahito suzuki
 * @since   Rawio 1.00
 */
public class ASyncFactory {
    
    /**
     * 生成モード : Auto.
     * <BR>
     * JNIで用意している条件と符号する場合は、DirectI/O用オブジェクトで
     * 生成し、そうでない場合は、EmurationI/O用オブジェクトで、ファイルI/Oオブジェクトを
     * 生成します.
     */
    public static final long MODE_AUTO = 0L ;
    
    /**
     * 生成モード : DirectI/O.
     * <BR>
     * JNIで用意している条件と符号しない場合は、例外が発生します.
     */
    public static final long MODE_DIRECT = 1L ;
    
    /**
     * 生成モード : EmulationI/O.
     * <BR>
     * EmurationI/O用オブジェクトで、ファイルI/Oオブジェクトを
     * 生成します.
     */
    public static final long MODE_EMULATOIN = 2L ;
    
    /**
     * デフォルト : 最大非同期書込みバッファ長.
     */
    private static final int DEF_ASYNC_BUFFER = 256 ;
    
    /**
     * 最小 : 最大非同期書込みバッファ長.
     */
    private static final int MIN_ASYNC_BUFFER = 32 ;
    
    /**
     * 最大 : 最大非同期書込みバッファ長.
     */
    private static final int MAX_ASYNC_BUFFER = 32767 ;
    
    /**
     * 最小 : 非同期書込みスレッド数.
     */
    private static final int MIN_ASYNC_THREAD = 1 ;
    
    /**
     * 最大 : 非同期書込みスレッド数.
     */
    private static final int MAX_ASYNC_THREAD = 32 ;
    
    /**
     * シングルトン.
     */
    private static final ASyncFactory SNGL = new ASyncFactory() ;
    
    /**
     * 生成モード.
     */
    private long mode = MODE_AUTO ;
    
    /**
     * キューオブジェクト.
     */
    private ASyncQueue queue = null ;
    
    /**
     * 書込みスレッド管理.
     */
    private ASyncWriteThread[] thread = null ;
    
    /**
     * 最大非同期書込みバッファ数.
     */
    private int maxAsyncLength = -1 ;
    
    /**
     * 停止オブジェクト処理.
     */
    private boolean stopFlag = true ;
    
    /**
     * 停止オブジェクト同期.
     */
    private final Object sync = new Object() ;
    
    /**
     * 生成ファイルオブジェクト.
     */
    private ArrayList<Baseio> imps = null ;
    
    /**
     * シャットダウン登録オブジェクト.
     */
    private ASyncioShutdown shutdown = null ;
    
    /**
     * コンストラクタ.
     */
    private ASyncFactory() {
    }
    
    /**
     * オブジェクトを取得.
     * @return ASyncFactory オブジェクトが返されます.
     */
    public static final ASyncFactory getInstance() {
        return SNGL ;
    }
    
    /**
     * 初期化処理.
     * @param threadLength 書込みスレッド数を設定します.
     * @exception Exception 例外.
     */
    public synchronized void init( int threadLength )
        throws Exception {
        this.init( MODE_AUTO,DEF_ASYNC_BUFFER,threadLength ) ;
    }
    
    /**
     * 初期化処理.
     * @param bufferLength 書込みバッファ数を設定します.
     * @param threadLength 書込みスレッド数を設定します.
     * @exception Exception 例外.
     */
    public synchronized void init( int bufferLength,int threadLength )
        throws Exception {
        this.init( MODE_AUTO,bufferLength,threadLength ) ;
    }
    
    /**
     * 初期化処理.
     * @param mode 生成モードを設定します.
     * @param bufferLength 書込みバッファ数を設定します.
     * @param threadLength 書込みスレッド数を設定します.
     * @exception Exception 例外.
     */
    public synchronized void init( long mode,int bufferLength,int threadLength )
        throws Exception {
        if( queue != null ) {
            throw new IOException( "オブジェクトは既に生成されています" ) ;
        }
        if( threadLength < MIN_ASYNC_THREAD || threadLength > MAX_ASYNC_THREAD ) {
            throw new IllegalArgumentException( "スレッド生成長は("+MIN_ASYNC_THREAD+
                "～"+MAX_ASYNC_THREAD+")の間で設定してください" ) ;
        }
        if( mode != MODE_AUTO && mode != MODE_DIRECT && mode != MODE_EMULATOIN ) {
            throw new IllegalArgumentException( "生成モードは不正です" ) ;
        }
        if( bufferLength <= MIN_ASYNC_BUFFER ) {
            bufferLength = MIN_ASYNC_BUFFER ;
        }
        else if( bufferLength >= MAX_ASYNC_BUFFER ) {
            bufferLength = MAX_ASYNC_BUFFER ;
        }
        ASyncQueue q = new ASyncQueue() ;
        ASyncWriteThread[] th = new ASyncWriteThread[ threadLength ] ;
        for( int i = 0 ; i < threadLength ; i ++ ) {
            th[ i ] = new ASyncWriteThread( q ) ;
        }
        this.mode = mode ;
        this.queue = q ;
        this.thread = th ;
        this.maxAsyncLength = bufferLength ;
        this.imps = new ArrayList<Baseio>() ;
        synchronized( sync ) {
            this.stopFlag = false ;
        }
    }
    
    /**
     * 終了化処理.
     */
    public synchronized void destroy() {
        if( queue != null ) {
            int len = imps.size() ;
            for( int i = 0 ; i < len ; i ++ ) {
                Baseio imp = imps.get( i ) ;
                if( imp != null ) {
                    try {
                        imp.flush() ;
                    } catch( Exception e ) {
                    }
                }
            }
            for( ;; ) {
                if( this.queue.elementSize() <= 0 ) {
                    break ;
                }
                //System.out.println( "wait" ) ;
                try{ Thread.sleep( 50L ) ; } catch( Exception e ) {}
            }
            synchronized( sync ) {
                this.stopFlag = true ;
            }
            len = thread.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( thread[ i ] != null ) {
                    thread[ i ].destroy() ;
                }
                thread[ i ] = null ;
            }
            len = imps.size() ;
            for( int i = 0 ; i < len ; i ++ ) {
                Baseio imp = imps.get( i ) ;
                if( imp != null ) {
                    ASyncio asyncio = null ;
                    if( imp.getType() == Baseio.IO_TYPE_MAPPING ) {
                        asyncio = ( ASyncio )( ( Mappingio )imp ).getBaseio() ;
                        if( asyncio == null ) {
                            continue ;
                        }
                    }
                    else {
                        asyncio = ( ASyncio )imp ;
                    }
                    asyncio.destroy() ;
                }
                imp = null ;
            }
            imps.clear() ;
        }
        this.queue = null ;
        this.thread = null ;
        this.maxAsyncLength = -1 ;
        this.imps = null ;
    }
    
    /**
     * シャットダウン登録.
     * <BR>
     * JavaVMが停止した後に終了化処理を呼び出す場合は、このメソッドを実行してください.
     */
    public synchronized void registShutdown() {
        if( queue != null ) {
            if( shutdown == null ) {
                shutdown = new ASyncioShutdown() ;
                Runtime.getRuntime().addShutdownHook( shutdown ) ;
            }
        }
    }
    
    /**
     * Factory停止条件を取得.
     * @return boolean [true]の場合停止しています.
     */
    protected boolean isStop() {
        boolean ret ;
        synchronized( sync ) {
            ret = this.stopFlag ;
        }
        return ret ;
    }
    
    /**
     * 非同期書込みオブジェクトを生成.
     * <BR>
     * 以前にオープンしたファイル名と同一の内容を指定した場合、
     * その内容が返されます.
     * @param name 対象のファイルパスを設定します.
     * @return Baseio 非同期書込みオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public synchronized Baseio open( String name ) throws Exception {
        return open( false,name ) ;
    }
    
    /**
     * 非同期書込みオブジェクトを生成.
     * <BR>
     * 以前にオープンしたファイル名と同一の内容を指定した場合、
     * その内容が返されます.
     * @param mode [true]の場合、Mappingioとして、オブジェクトを生成します.
     * @param name 対象のファイルパスを設定します.
     * @return Baseio 非同期書込みオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public synchronized Baseio open( boolean mode,String name ) throws Exception {
        if( queue == null ) {
            throw new IOException( "ASyncFactoryは生成されていないか、既に破棄されています" ) ;
        }
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        name = new File( name ).getCanonicalPath() ;
        int len = imps.size() ;
        for( int i = 0 ; i < len ; i ++ ) {
            Baseio impl = imps.get( i ) ;
            if( impl != null && name.equals( impl.getName() ) ) {
                return impl ;
            }
        }
        Baseio impl = new ASyncio( this.mode,this,queue,maxAsyncLength,name ) ;
        if( mode == true ) {
            impl = MappingioInstance.getInstance( impl ) ;
        }
        imps.add( impl ) ;
        return impl ;
    }
    
    /**
     * スレッド数を取得.
     * @return int スレッド数が返されます.
     */
    public synchronized int getThreadSize() {
        return ( thread != null ) ? thread.length : 0 ;
    }
    
    /**
     * 最大データ格納数を取得.
     */
    public synchronized int getMaxBuffer() {
        return maxAsyncLength ;
    }
    
    /**
     * Factoryが有効かチェック.
     * @return boolean [true]の場合、有効です.
     */
    public synchronized boolean isUse() {
        return queue != null ;
    }
}
