package org.maachang.comet.net.nio ;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;

/**
 * Nio用InputStream.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
class NioInputStream extends InputStream {
    
    private static final long TIMEOUT = 5000L ;
    private static final long MIN_TIMEOUT = 500L ;
    
    private NioElement element = null ;
    private byte[] buffer = null ;
    private int bufferToPos = 0 ;
    private int count = 0 ;
    private long timeout = -1L ;
    
    private NioInputStream() {
        
    }
    
    public NioInputStream( NioElement element ) {
        this( element,TIMEOUT ) ;
    }
    
    public NioInputStream( NioElement element,long timeout ) {
        this.element = element ;
        this.count = 0 ;
        this.timeout = ( timeout <= MIN_TIMEOUT ) ? MIN_TIMEOUT : timeout ;
        this.buffer = null ;
        this.bufferToPos = 0 ;
    }
    
    public void close() {
        synchronized( this ) {
            if( element != null && element.getBuffer() != null ) {
                element.getBuffer().reset() ;
            }
            element = null ;
            count = 0 ;
            buffer = null ;
            bufferToPos = 0 ;
        }
    }
    
    public int available() throws IOException {
        if( isUse() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        int ret ;
        synchronized( this ) {
            ret = element.getBuffer().length() - count ;
        }
        return ret ;
    }
    
    public int read() throws IOException {
        if( isUse() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        ReceiveBuffer bm ;
        byte[] b = null ;
        int bPos = 0 ;
        synchronized( this ) {
            bm = element.getBuffer() ;
            b = buffer ;
            bPos = bufferToPos ;
        }
        if( b == null ) {
            for( ;; ) {
                long time = System.currentTimeMillis() ;
                if( ( b = bm.get() ) != null ) {
                    synchronized( this ) {
                        buffer = b ;
                        bufferToPos = 0 ;
                        bPos = 0 ;
                    }
                    break ;
                }
                if( time + timeout <= System.currentTimeMillis() ) {
                    throw new SocketTimeoutException( "SocketTimeout" ) ;
                }
                try { Thread.sleep( 5L ) ; } catch( Exception e ) {}
            }
        }
        int ret = ( ( int )b[ bPos ] & 0x000000ff ) ;
        if( bPos + 1 >= b.length ) {
            synchronized( this ) {
                buffer = null ;
                bufferToPos = 0 ;
                count ++ ;
            }
        }
        else {
            synchronized( this ) {
                bufferToPos ++ ;
                count ++ ;
            }
        }
        return ret ;
    }
    
    public boolean markSupported() {
        return false ;
    }
    
    private boolean isUse() {
        boolean ret ;
        synchronized( this ) {
            ret = ( element == null || element.isUse() == false ) ;
        }
        return ret ;
    }
}
