package org.maachang.comet.net.nio ;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

import org.maachang.comet.net.ssl.SslElement;

/**
 * Nio受信データ要素.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
public class NioElement {
    
    /**
     * SelectionKey.
     */
    private SelectionKey key = null ;
    
    /**
     * SocketChannel.
     */
    private SocketChannel channel = null ;
    
    /**
     * 受信バッファ.
     */
    private ReceiveBuffer buffer = null ;
    
    /**
     * 送信バッファ.
     */
    private WriteDataQueue writeBuffer = null ;
    
    /**
     * 書き込み可能を示すバッファ.
     */
    private IsWriteQueue isWriteQueue = null ;
    
    /**
     * 最新時間.
     */
    private long time = -1L ;
    
    /**
     * 処理中フラグ.
     */
    private boolean executionFlag = false ;
    
    /**
     * クローズフラグ.
     */
    private boolean closeFlag = false ;
    
    /**
     * 付加オブジェクト.
     */
    private Object object = null ;
    
    /**
     * SSL要素.
     */
    private SslElement sslElement = null ;
    
    /**
     * コンストラクタ.
     */
    private NioElement() {
        
    }
    
    /**
     * コンストラクタ.
     * @param channel 対象のチャネル情報を設定します.
     * @exception Exception 例外.
     */
    public NioElement( SocketChannel channel ) throws Exception {
        if( channel == null || channel.isConnected() == false ) {
            throw new IOException( "ソケットチャネルは不正です" ) ;
        }
        this.channel = channel ;
        this.writeBuffer = new WriteDataQueue() ;
        this.buffer = new ReceiveBuffer() ;
        this.time = System.currentTimeMillis() ;
        this.executionFlag = false ;
        this.closeFlag = false ;
        this.object = null ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public synchronized void destroy() {
        if( sslElement != null ) {
            sslElement.destroy() ;
        }
        sslElement = null ;
        if( key != null ) {
            try {
                key.cancel() ;
            } catch( Exception e ) {
            }
        }
        key = null ;
        if( channel != null ) {
            try {
                channel.finishConnect() ;
            } catch( Exception e ) {
            }
            try {
                channel.close() ;
            } catch( Exception e ) {
            }
        }
        channel = null ;
        if( buffer != null ) {
            buffer.destroy() ;
        }
        if( writeBuffer != null ) {
            writeBuffer.destroy() ;
        }
        isWriteQueue = null ;
        buffer = null ;
        object = null ;
        writeBuffer = null ;
        executionFlag = false ;
        closeFlag = false ;
    }
    
    /**
     * 書き込み開始キューを設定.
     * @param isWrite 書き込み開始キューを設定します.
     */
    protected synchronized void setIsWriteQueue( IsWriteQueue isWriteQueue ) {
        this.isWriteQueue = isWriteQueue ;
    }
    
    /**
     * SSL要素を追加.
     * @param sslElement 対象のSSL要素を追加します.
     */
    protected synchronized void setSslElement( SslElement sslElement ) {
        this.sslElement = sslElement ;
    }
    
    /**
     * SSL要素を取得.
     * @return SslElement SSL要素が返されます.
     */
    public synchronized SslElement getSslElement() {
        return sslElement ;
    }
    
    /**
     * SelectionKeyを設定.
     * @param key SelectionKeyを設定します.
     */
    public synchronized void setKey( SelectionKey key ) {
        this.key = key ;
    }
    
    /**
     * SelectionKeyを取得.
     * @return SelectionKey SelectionKeyが返されます.
     */
    public synchronized SelectionKey getKey() {
        return key ;
    }
    
    /**
     * SocketChannelを取得.
     * @return SocketChannel SocketChannelが返されます.
     */
    public synchronized SocketChannel getChannel() {
        return channel ;
    }
    
    /**
     * 受信バッファを取得.
     * @return ArrayBinary 受信バッファを取得します.
     */
    public synchronized ReceiveBuffer getBuffer() {
        return buffer ;
    }
    
    /**
     * 書き込み開始を通知.
     */
    public synchronized void startWrite() {
        if( isWriteQueue != null ) {
            isWriteQueue.append( this ) ;
        }
    }
    
    /**
     * 書き込みバッファを取得.
     * @return WriteDataQueue 書き込みバッファを取得します.
     */
    public synchronized WriteDataQueue getWriteBuffer() {
        return writeBuffer ;
    }
    
    /**
     * クローズフラグをセット.
     * @param mode [true]の場合、クローズとなります.
     */
    public synchronized void setCloseFlag( boolean mode ) {
        closeFlag = mode ;
    }
    
    /**
     * クローズフラグを取得.
     * @return boolean [true]の場合、クローズです.
     */
    public synchronized boolean isCloseFlag() {
        return closeFlag ;
    }
    
    /**
     * 更新処理.
     */
    public synchronized void update() {
        time = System.currentTimeMillis() ;
    }
    
    /**
     * 最新時間を取得.
     * @return long 最新時間が返されます.
     */
    public synchronized long getTime() {
        return time ;
    }
    
    /**
     * 処理中フラグを設定.
     * @param flg [true]の場合、処理中フラグをONにします.
     */
    protected synchronized void setExecutionFlag( boolean flg ) {
        this.executionFlag = flg ;
        if( flg == false ) {
            update() ;
        }
    }
    
    /**
     * 処理中フラグを取得.
     * @return boolean [true]の場合、処理中です.
     */
    protected synchronized boolean isExecutionFlag() {
        return executionFlag ;
    }
    
    /**
     * 付加オブジェクトを設定.
     * @param object 付加オブジェクトを設定します.
     */
    public synchronized void setObject( Object object ) {
        this.object = object ;
    }
    
    /**
     * 付加オブジェクトを取得.
     * @return Object 付加オブジェクトが返されます.
     */
    public synchronized Object getObject() {
        return object ;
    }
    
    /**
     * オブジェクトが有効かチェック.
     */
    public synchronized boolean isUse() {
        return channel != null && channel.isConnected() && channel.isOpen() ;
    }
}
