package binaryMatrixFile;

import java.io.*;

public class BinaryMatrixReader {

	String fileName;
	FileInputStream file;
	DataInputStream ds;
	int cols,nRows;
	int currentRow;

	public BinaryMatrixReader(String fileName) {
		this.fileName = fileName;
				
		try{
			file = new FileInputStream(fileName);
			ds = new DataInputStream(file);
					
			nRows = ds.readInt();
			cols = ds.readInt();
			
			if(nRows < 0){
				int nBytes = ds.available();
				nRows = (nBytes / (cols * 8));
				System.err.println("WARNING: Invalid/unknown row count in "+fileName+", using "+nRows+" based on file size.");				
			}
		}catch(IOException e){ throw new RuntimeException(e);	}
		
		currentRow = 0;
		
	}
	
	public final double[] mustReadRow(){
		try{
			return readRow();
		}catch(IOException e){ throw new RuntimeException(e); }		
	}
	
	public final void skipRows(long nRows){
		long bytesToSkip = (long)nRows * cols * 8;
		try {
			long bytesSkipped = file.skip(bytesToSkip);
			//apparently, that sometime doesn't work
			if(bytesSkipped < bytesToSkip){
				bytesToSkip -= bytesSkipped;
				int blockSize = 1024*1024;
				byte junkBuff[] = new byte[blockSize];
				do{
					if(bytesToSkip < blockSize)
						junkBuff = new byte[(int)bytesToSkip];
					int bytesRead = file.read(junkBuff);
					bytesToSkip -= bytesRead;
				}while(bytesToSkip > 0);
			}
			
		} catch (IOException e) { throw new RuntimeException(e); }
	}
	
	public final double[] readRow() throws IOException{		
		double row[] = new double[cols];
		for(int j=0;j<cols;j++)
			row[j] = ds.readDouble();			
		currentRow++;
		return row;
		
	}
		
	/** Writes int, int arrays, doubles or double arrays all in a line, throwing and exception if it does not matched defined number of columns */
	public final void readRow(Object... rowDatas){
		readRowInternal(false, rowDatas);
	}
	
	/** Writes int, int arrays, doubles or double arrays all in a line, filling with fillVal if it does not matched defined number of columns */
	public final void readRowShort(Object... rowDatas){
		readRowInternal(true, rowDatas);
	}
	
	/** Writes int, int arrays, doubles or double arrays all in a line */
	private final void readRowInternal(boolean allowShort, Object... rowDatas){
		try{
			int col = 0;
			for(int i=0; i < rowDatas.length; i++){
				if( rowDatas[i] instanceof double[] ){
					double section[] = (double[])rowDatas[i];						
					for(int j=0;j<section.length;j++)
						section[j] = ds.readDouble();
					col += section.length;
					
				}else if( rowDatas[i] instanceof int[] ){
					int section[] = (int[])rowDatas[i];
					for(int j=0;j<section.length;j++)
						section[j] = (int)ds.readDouble();
					col += section.length;
				}
			}
				
			/*if(col > cols){
				throw new RuntimeException("Too many cols: Data for row had total " + col +" columns but writer was inited with only" + cols );
			
			}else if(!allowShort && (col != cols))
				throw new RuntimeException("Data for row had total " + col +" columns but writer was inited with " + cols );
			*/
			currentRow++;
			
			
		}catch(IOException e){ throw new RuntimeException(e); }
			
	}
	
	public final void close(){
		try{
			file.close();
		
		}catch(IOException e){ throw new RuntimeException(e); }
		
	}
	
	@Override
	protected void finalize() throws Throwable {
		close();
		super.finalize();
	}
	
	public int nCols(){
		return cols;
	}
	
	public int nRows(){
		return nRows;
	}
	
}
