package comedi;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import comediJNI.ComediDef;

/** Write buffer for Comedi writing.
 * 
 * This is a bit simpler than {@link ComediRawReadBuffer} because, while
 * it just get written continously like a ring buffer, the sense of which scan
 * number related to which entry is simple 0 to buffLen-1.
 * 
 * @author cisadmin
 *
 */
public class ComediRawWriteBuffer extends ComediRawDataStore {

	/** Maximum number of repeats for writing before stopping filling the buffer */
	public int maxCycles = -1;
	
	protected int getScanLoc(int scan){ 
		return (scan >= 0 && scan < buffLength) ? scan : -1; 
	}
	
	/** Create raw data storage for writing, basing buffer on given data */
	public ComediRawWriteBuffer(ByteBuffer data, int bitsPerSample, ComediDef.Range range[], int maxRaw[], int maxCycles) {
		this.bitsPerSample = bitsPerSample;
		this.nChans = range.length;
		this.range = range;
		this.maxRaw = maxRaw;
		this.notify = null;
		this.maxCycles = maxCycles;
		
		
		switch(bitsPerSample){
		case 1:
			this.buffLength = data.capacity() / 4;
			break;
		case 16:			
			this.buffLength = data.capacity() / nChans / 2 ;
			break;
		case 32:
			this.buffLength = data.capacity() / nChans / 4;
			break;
		default:
			throw new RuntimeException("Expecting 1,16 or 32 bits samples");
		}
				
		this.buff = data;		
	}
	
		
	int writeMoreData(FileChannel fileChan) throws IOException{
		if(!buff.hasRemaining())
			return 0; //nothing todo
			
		synchronized (this) {
			//get the next single byte to be written, but don't advance the position yet
			oneByteBuff.clear();
			byte b = buff.get(buff.position());
			oneByteBuff.put(b);
			oneByteBuff.flip();
		}
		
		//block here waiting for data write, outside the sync locks for messing with the main buff
		int bytesWritten = fileChan.write(oneByteBuff);
		if(bytesWritten <= 0)
			return bytesWritten;
		
		synchronized (this) {
			//now we can advance the buffer position
			buff.get();
			
			bytesWritten += fileChan.write(buff);
			
			if(bytesWritten <= 0)
				return bytesWritten;
			
			int bytesPerScan = (bitsPerSample == 1) ? 4 : (nChans * bitsPerSample/8);
			int scansWritten = (buff.position() / bytesPerScan) - nextScanLoc;
			nextScanNum += scansWritten;
			nextScanLoc += scansWritten;
			
			System.out.println("Wrote " + scansWritten + " scan (" + bytesWritten + " bytes). " +
					", nextScanNum = " + nextScanNum + 
					", nextScanLoc = " + nextScanLoc);
			
			if(!buff.hasRemaining()){
				nOverflows++;		
				if(nOverflows >= maxCycles){
					System.out.println("Reached max overflows, not wrapping");
					return bytesWritten; //return with nothing left in buffer
				}
				buff.rewind();
				nextScanLoc = 0;
				System.out.println("End of write buffer, restarting.");
			}

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

	/** Returns a byte buffer filled with the last scan of data, for running out the 
	 * comedi kernel buffer with 
	 * @return
	 */
	public ByteBuffer getLastScan() {
		int bytesPerScan = (bitsPerSample == 1) ? 4 : (nChans * bitsPerSample/8);
		ByteBuffer bBuf = ByteBuffer.allocate(bytesPerScan);
		
		for(int i=0; i < bytesPerScan; i++){
			byte b = buff.get((buffLength - 1) * bytesPerScan + i);
			bBuf.put(b);
		}
		return bBuf;
	}
}
