package comedi;

import java.io.IOException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;


import comediJNI.ComediDef;

/** Storage of raw data read from data aquisition by {@link ComediAsyncReader}
 * Data storage wraps back to start if the buffer is too short so it is
 * a ring buffer for continuous reading.
 */
public class ComediRawReadBuffer extends ComediRawDataStore {

	/** Earliest scan number in data */
	public int firstScanNum = 0;
	
	/** Location in buffer of earliest valid scan for reading or earliest written scan for writing*/  
	public int firstScanLocation = 0;
	

	/** Create raw data storage for reading, allocating the buffer */
	public ComediRawReadBuffer(int nScans, int bitsPerSample, ComediDef.Range range[], int maxRaw[]) {
		this.bitsPerSample = bitsPerSample;
		this.nChans = range.length;
		this.buffLength = nScans;
		this.range = range;
		this.maxRaw = maxRaw;
		this.notify = null;
		
		
		int bufLenBytes;
		switch(bitsPerSample){
		case 1:
			bufLenBytes = buffLength;
			break;
		case 16:
			bufLenBytes = 2 * buffLength * nChans;
			break;
		case 32:
			bufLenBytes = 4 * buffLength * nChans;
			break;
		default:
			throw new RuntimeException("Expecting 1,16 or 32 bits samples");
		}
		
		
		buff = ByteBuffer.allocateDirect(bufLenBytes + 1); //the extra byte so we know when it's overflowed
		buff.order(ByteOrder.LITTLE_ENDIAN);		
	}


	protected int getScanLoc(int scan){
		if(scan < firstScanNum || scan >= nextScanNum)
			return -1; //out of range
		
		if(nOverflows <= 0){
			return scan;				
		}else{
			int entry = scan - (nOverflows * buffLength);
			if(entry < 0)
				entry += buffLength;
			return entry;
		}
	}

	int readMoreData(FileChannel fileChan) throws IOException{
				
		oneByteBuff.clear();
		//block here waiting for data, outside the sync lock for messing with the main buff
		int bytesRead = fileChan.read(oneByteBuff);
		if(bytesRead <= 0)
			return bytesRead;
		
		synchronized (this) {
			oneByteBuff.flip();
			buff.put(oneByteBuff);
			
			bytesRead += fileChan.read(buff);
			
			if(bytesRead <= 0)
				return bytesRead;
			
			int bytesPerScan = (bitsPerSample == 1) ? 1 : (nChans * bitsPerSample/8);
			int scansRead = (buff.position() / bytesPerScan) - nextScanLoc;
			nextScanNum += scansRead;
			nextScanLoc += scansRead;
			
			if(nOverflows > 0){ //are we overwriting old data?
				firstScanNum += scansRead;
				firstScanLocation = (int)(((float)buff.position() / bytesPerScan) + 0.5);
			}
			
			System.out.println("Read " + scansRead + " scan (" + bytesRead + " bytes). " +
					"firstScanNum = " + firstScanNum + 
					", nextScanNum = " + nextScanNum + 
					", nextScanLoc = " + nextScanLoc);
			
			if(!buff.hasRemaining()){
				nOverflows++;
				byte overflowByte = buff.get(buff.capacity() - 1);
				buff.clear();
				nextScanLoc = 0;
				buff.put(overflowByte);							
				System.out.println("Buffer overflowed. Now 1 byte in buffer.");		
			}

			if(notify != null)
				notify.rawStoreChanged();
		}
		
		return bytesRead;
		
	}
	
}
