/* ************************************************************************
#
#  DivConq
#
#  http://divconq.com/
#
#  Copyright:
#    Copyright 2012 eTimeline, LLC. All rights reserved.
#
#  License:
#    See the license.txt file in the project's top-level directory for details.
#
#  Authors:
#    * Andy White
#
************************************************************************ */
package divconq.lang;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;

import divconq.util.ArrayUtil;

/**
 * Internal class, @see Memory.
 * 
 * @author Andy
 *
 */
public class MemoryChunk {
    private byte[] buffer = null;		// the capacity is buffer.Length
    private int length = 0;    			// effective length of content
    private int position = 0;

    public MemoryChunk(byte[] buffer) {
        if (buffer == null) 
        	buffer = new byte[0];
        
        this.buffer = buffer;
        this.length = buffer.length;
    }

    public MemoryChunk(byte[] buffer, int length) {
        if (buffer == null) {
        	buffer = new byte[0];
        	length = 0;
        }
        
        if (length > buffer.length) 
        	length = buffer.length;
        
        this.buffer = buffer;
        this.length = buffer.length;
    }

    public MemoryChunk(int capacity) {
        if (capacity < 0) 
        	capacity = 0;
        
        this.buffer = new byte[capacity];
    }

    public int getCapacity() {
        return this.buffer.length;
    }

    public int getLength() {
    	return this.length;
    }

    public int getPosition() {
    	return this.position;
    }
    
    public void setPosition(int value) {
        if (value < 0) 
        	value = 0;
        
        if (value >= this.buffer.length) 
        	value = this.buffer.length - 1;
        
        this.position = value;
    }

    public byte[] getBuffer() {
        return this.buffer;
    }

    public int read(byte[] buffer) {
    	return this.read(buffer, 0, buffer.length);
    }
    
    public int read(byte[] buffer, int offset, int count) {
    	if (count <= 0)
    		return 0;
    	
        if ((this.position + count) > this.length) 
        	count = this.length - this.position;
        
        if (ArrayUtil.blockCopy(this.buffer, this.position, buffer, offset, count)) {
        	this.position += count;
            return count;
        }

        return 0;
    }

    public int readByte() {
        if (this.position >= this.length) 
        	return -1;

        return (int)0xFF & this.buffer[this.position++];
    }

    public void setLength(int value) {
        if (value > this.buffer.length)
        	value = this.buffer.length;
        
        if (value < 0) 
        	value = 0;

        length = value;
    }

    public byte[] toArray() {
        return Arrays.copyOf(this.buffer, this.length);
    }

    public int write(byte[] buffer) {
        return this.write(buffer, 0, buffer.length);
    }

    public int write(byte[] buffer, int offset, int count) {
    	if (count <= 0)
    		return 0;
    	
        if ((this.position + count) > this.buffer.length) 
        	count = this.buffer.length - this.position;
        
        if (ArrayUtil.blockCopy(buffer, offset, this.buffer, this.position, count)) {
            this.position += count;

            if (this.position >= this.length) 
            	this.length = this.position;
            
            return count;
        }
    	
        return 0;
    }

    public int write(ByteBuffer buffer) {
    	if (buffer == null)
    		return 0;
    	
        int count = buffer.limit() - buffer.position();
        int offset = buffer.position();
        
        if ((this.position + count) > this.buffer.length) 
        	count = this.buffer.length - this.position;
        
        if (ArrayUtil.blockCopy(buffer, offset, this.buffer, this.position, count)) {
            this.position += count;
            buffer.position(offset + count);

            if (this.position >= this.length) 
            	this.length = this.position;
            
            return count;
        }
    	
        return 0;
    }

    public boolean writeByte(byte value) {
        if (this.position >= this.buffer.length) 
        	return false;

        this.buffer[this.position] = value;
        
        this.position++;
        
        if (this.position >= this.length) 
        	this.length = this.position;
        
        return true;
    }

    public long copyToStream(OutputStream stream, long count) {
    	if (count <= 0)
    		return 0;
    	
        if ((this.position + count) > this.buffer.length) 
        	count = this.buffer.length - this.position;
        
        try {
			stream.write(this.buffer, this.position, (int)count);
	        
	        this.position += count;

	        if (this.position >= this.length) 
	        	this.length = this.position;
	        
	        return count;
		} 
        catch (IOException e) {
        	// TODO logging
		}
        
        return 0;
    }
    

    /*
    public virtual void WriteFrom(Stream stream)
    {
        CheckIfClosedThrowDisposed();

        if (stream == null) throw new ArgumentNullException("stream");

        stream.Read(m_buffer, m_position, m_buffer.Length - m_position);
    }

    public virtual void ReadInto(Stream stream)
    {
        CheckIfClosedThrowDisposed();

        if (stream == null) throw new ArgumentNullException("stream");

        stream.Write(m_buffer, 0, m_length);
    }
    */
}

