/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.io;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import net.ranides.assira.events.Event;
import net.ranides.assira.events.Events;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public interface IOEvent extends Event {
    
    static Flush flush(OutputStream source) {
        return new Flush(source);
    }
    
    static Reset reset(InputStream source) {
        return new Reset(source);
    }
    
    static Read read(InputStream source, int data) {
        if(data == -1) {
            return new Read(source);
        } else {
            return new Read(source, new byte[]{(byte)data}, 0, 1);
        }
    }
    
    static Read read(InputStream source, byte[] data, int offset, int length) {
        return new Read(source, data, offset, length);
    }
    
    static Read read(InputStream source, byte[] data) {
        return new Read(source, data, 0, data.length);
    }
    
    static Write write(OutputStream source, int data) {
        return new Write(source, new byte[]{(byte)data}, 0, 1);
    }
    
    static Write write(OutputStream source, byte[] data, int offset, int length) {
        return new Write(source, data, offset, length);
    }
    
    static Write write(OutputStream source, byte[] data) {
        return new Write(source, data, 0, data.length);
    }
    
    static Close close(Closeable source) {
        return new Close(source);
    }
    
    static Skip skip(InputStream source, long count) {
        return new Skip(source, count);
    }
    
    static Move move(InputStream source, long position) {
        return new Move(source, position);
    }
    
    static Failure failure(IOException cause) {
        return new Failure(cause);
    }
    
    class Reset implements IOEvent {
        
        private final InputStream source;

        protected Reset(InputStream source) {
            this.source = source;
        }

        public InputStream source() {
            return source;
        }
    }
    
    class Flush implements IOEvent {
        
        private final OutputStream source;

        protected Flush(OutputStream source) {
            this.source = source;
        }

        public OutputStream source() {
            return source;
        }
    }
    
    class Read implements IOEvent {
        
        private static final ByteBuffer NONE = ByteBuffer.wrap(new byte[0]);
        
        private final InputStream source;
        private final ByteBuffer data;
        
        protected Read(InputStream source) {
            this.source = source;
            this.data = NONE;
        }
        
        protected Read(InputStream source, byte[] data, int offset, int length) {
            this.source = source;
            this.data = ByteBuffer.wrap(data, offset, length);
        }
        
        public InputStream source() {
            return source;
        }
        
        public ByteBuffer data() {
            return data;
        }
        
    }
    
    class Write implements IOEvent {
        
        private final OutputStream source;
        private final ByteBuffer data;
        
        protected Write(OutputStream source, byte[] data, int offset, int length) {
            this.source = source;
            this.data = ByteBuffer.wrap(data, offset, length);
        }
        
        public OutputStream source() {
            return source;
        }
        
        public ByteBuffer data() {
            return data;
        }
        
    }

    class Close implements IOEvent {
        
        private final Closeable source;

        protected Close(Closeable source) {
            this.source = source;
        }

        public Closeable source() {
            return source;
        }
        
    }
    
    class Skip implements IOEvent {
        
        private final InputStream source;
        private final long count;

        protected Skip(InputStream source, long count) {
            this.source = source;
            this.count = count;
        }

        public InputStream source() {
            return source;
        }

        public long count() {
            return count;
        }
        
    }
    
    class Move implements IOEvent {
        
        private final InputStream source;
        private final long position;

        protected Move(InputStream source, long position) {
            this.source = source;
            this.position = position;
        }

        public InputStream source() {
            return source;
        }

        public long position() {
            return position;
        }
        
    }
    
    class Failure extends Events.Failure implements IOEvent {
		
        protected Failure(Throwable cause) {
            super(cause);
        }
        
        @Override
        public IOException cause() {
            return (IOException)super.cause();
        }
        
    }
}
