package highspeedacquisitionsample;

import com.oceanoptics.omnidriver.features.boardtemperature.BoardTemperature;
import com.oceanoptics.highrestiming.HighResTimeStamp;
import com.oceanoptics.omnidriver.api.wrapper.Wrapper;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.io.IOException;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

import java.io.FileWriter;
// import java.io.PrintWriter;
import java.io.BufferedWriter;
/**
 * This sample demonstrates how to use external trigger modes and the
 * high speed acquisition feature of the OmniDriver "Wrapper" API.
 * 
 * We will wait for a hardware trigger signal (level-triggered) to occur.
 * Then we will configure the spectrometer to acquire 160 spectra in a one second
 * interval.  Then we initiate the high speed acquisition, which will acquire
 * all 160 spectra.  Finally, we extract these spectra out of the internal
 * buffer area so as to be able to manipulate them in our application.
 * 
 * <p><b>Important note reg                // the oneSpectrum array now contains all the raw CCD pixel values for one spectrum
                // You might want to call wrapper.highSpdAcq_IsSaturated(spectrumNumber) at this point.
                // For example...
//                 boolean weHaveAProblem = wrapper.highSpdAcq_IsSaturated(spectrumNumber);arding the duration of the hardware trigger signal</b>
 * <br>The duration of the trigger signal MUST be slightly longer than the time it takes
 * to acquire the initial minimum-integration-time spectrum.  This is because
 * you can only change the trigger mode of a spectrometer during the phase when
 * the spectrometer is actively acquiring a spectrum.  Thus, the trigger signal
 * must be long enough to cause TWO spectra to be acquired.  The first spectrum
 * acquisition causes our initial getSpectrum() method to return control to
 * the application.  Then, while the spectrometer is acquiring a second spectrum,
 * our application calls the setExternalTriggerMode() method to set the mode
 * back to "normal" (aka free-running).
 * <p>As a consequence of this requirement, it means that there will be a small
 * delay (equal to two times the minimum integration time for the spectrometer)
 * following the trigger signal before we begin collecting the desired  spectra.
 */



public class HighSpeedAcquisitionSample {


    
    Wrapper wrapper;
    
  
    public static void main(String[] args) {

        HighSpeedAcquisitionSample    myself;
        //args = [Serialnumber; integration time [us]]
        myself= new HighSpeedAcquisitionSample();

        myself.run(args);
    }
    
    public void run(String[] args) {
        
        double  activeSampleIntervalDuration; // units: seconds; this is how long we want to actively acquire spectra
        int integrationTime; // units: microseconds
        int loopCount;
        int loopLimit;
        int minimumIntegrationTime; // units: microseconds
        int numberOfSpectraRequested;
        int numberOfSpectrometers;
        int spectrometerIndex;
        int spectrumNumber;
	int n_readout_spectra = 1000;
	String serialNumber; 
	BoardTemperature boardTemperature;
        double           temperatureCelsius;
	int BackgroundIntegTime;
// 	DoubleArray background;
	double[] background;
	double[] wavelength;
	double[] readOutPatterns;

	int numberOfPixels; // number of CCD elements/pixels provided by the spectrometer
	
	
	
        wrapper = new Wrapper();

	numberOfSpectrometers = wrapper.openAllSpectrometers();

	
	try{
	    BufferedWriter prepared = new BufferedWriter(new FileWriter("./prepared"));
	    prepared.write(" ");	
	    prepared.close();
	}
	catch (IOException e) {
		System.out.println( e.getMessage() );		    
	}
	
	
	// choose reguested spectrometer
        spectrometerIndex = -1;
        for (int i = 0;i<numberOfSpectrometers;++i){
	    serialNumber = wrapper.getSerialNumber(i);
	    if (serialNumber.equals(args[0]))	    {
		spectrometerIndex = i;
	    }
	}
	
	
	if (numberOfSpectrometers == -1) {
            System.out.println(wrapper.getLastException());
            System.out.println(wrapper.getLastExceptionStackTrace());
            System.out.println("Error occured while attempting to access spectrometers.  Exiting the application.");
            return;
        } else if (numberOfSpectrometers == 0) {
            System.out.println("No spectrometers found.  Exiting the application.");
            return;
        }
        if (spectrometerIndex == -1) {
	    System.out.println("Spectrometers with serial number "+ args[0]+ " was not found.  Exiting the application.");
            return;	    
	}
	
        serialNumber = wrapper.getSerialNumber(0); 
	System.out.println("spectrometer type: " + wrapper.getName(spectrometerIndex) +
	" s/n: " + wrapper.getSerialNumber(spectrometerIndex) +
	" number of channels: " + wrapper.getWrapperExtensions().getNumberOfEnabledChannels(spectrometerIndex) +
	" firmware: " + wrapper.getFirmwareVersion(spectrometerIndex));
        System.out.flush();
	boardTemperature = wrapper.getFeatureControllerBoardTemperature(spectrometerIndex);
	wavelength = wrapper.getWavelengths(spectrometerIndex);
        activeSampleIntervalDuration = Integer.parseInt(args[1]); //[ms] 
// 	maximum is limited by memory accesible by java virtual maschine to about 3000 spectra
        minimumIntegrationTime = wrapper.getMinimumIntegrationTime(spectrometerIndex);
	
	
	integrationTime = Integer.parseInt(args[2]); //[us]
	if (integrationTime< minimumIntegrationTime){
	    integrationTime = minimumIntegrationTime;
	    System.out.println("To short integration time, set to "+integrationTime/1000+ "ms");	    
	}
	numberOfSpectraRequested = (int)((double)activeSampleIntervalDuration/integrationTime); // this is how may spectra we want to collect during our "active sample interval",
	
	System.out.println("Integration time "+integrationTime/1000.0+ " [ms], number of spectra "+numberOfSpectraRequested+" Sampling time "+activeSampleIntervalDuration);
	BackgroundIntegTime = 1000000; 
        numberOfPixels = wrapper.getNumberOfPixels(spectrometerIndex);
	System.out.println("Number of pixels: " + numberOfPixels);
	SimpleDateFormat date = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
	
	
	
// // 	readout noise patterns
	System.out.println("acquiring the readoutnoise patterns");

	wrapper.highSpdAcq_AllocateBuffer(spectrometerIndex,1000);
	wrapper.setIntegrationTime(spectrometerIndex,integrationTime);
	wrapper.highSpdAcq_StartAcquisition(spectrometerIndex); 

	readOutPatterns = wrapper.highSpdAcq_GetSpectrum(spectrometerIndex);
// 	for (spectrumNumber=0; spectrumNumber<wrapper.highSpdAcq_GetNumberOfSpectraAcquired(); ++spectrumNumber) {	    
// 	     // this will contain the sum of the raw pixel 
// 	    double[] oneSpectrum; // this will contain the raw pixel values comprising one spectrum
// 	    oneSpectrum = wrapper.highSpdAcq_GetSpectrum(spectrumNumber);
// 	    for (int line=0; line<numberOfPixels; ++line) {
// 		readOutPatterns[line] = readOutPatterns[line]+oneSpectrum[line];
// 	    }		    
// 	} 
// 	try {
// 	    BufferedWriter readoutpatternsOut = new BufferedWriter(new FileWriter("./data/readoutpatterns.txt"));
// 	    readoutpatternsOut.write("Readout noise patterns of the spectrometer - "+wrapper.getName(spectrometerIndex) +"\n");
// 	    for (int line=0; line<numberOfPixels; ++line) {
// 		readoutpatternsOut.write((readOutPatterns[line]/1001.0)+"\n");
// 	    }
// 	    readoutpatternsOut.close();
// 
// 	}
// 	catch (IOException e) {
// 	    System.out.println( e.getMessage() );
// 		
// 	}
// 	
// 	
// 	System.out.println("The readoutnoise patterns acquired");
// 
// 	
	
	
	
	//prepare for high speed acqusition
	

	
	
	

	loopCount = 0;
	while (loopCount == 0) {
	    ++loopCount;
 
            try { Thread.sleep(1000); } catch (Exception ee) {}
            
	    wrapper.highSpdAcq_AllocateBuffer(spectrometerIndex,numberOfSpectraRequested);

            
            // Prepare to acquire auxiliary spectra
            wrapper.setIntegrationTime(spectrometerIndex,minimumIntegrationTime); // minimize latency when next hardware trigger occurs
            wrapper.setExternalTriggerMode(spectrometerIndex,0); // hardware trigger mode - level triggered
            System.out.println("Loop " + (loopCount) + " of " + "infinite");
// 	    System.out.println("Background acquiring");
// 	    System.out.println("Background acquring");

	    wrapper.getSpectrum(spectrometerIndex);
	    
// 	    background = background.getDoubleValues();
// 	    System.out.println("Background acquired");
            // Prepare to acquire spectra
            wrapper.setExternalTriggerMode(spectrometerIndex,1); // external hardware trigger mode
	    wrapper.setIntegrationTime(spectrometerIndex,integrationTime);

	    
	    // Now wait for the hardware trigger signal
	    System.out.println("Waiting on trigger");
// 	    wrapper.getSpectrum(spectrometerIndex);
//             System.out.println();
	     try { Thread.sleep(1000); } catch (Exception ee) {}
/*	    
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
            System.out.println("Begin acquisition: " + sdf.format(new Date()));
            long startTime = new Date().getTime(); // a count of milliseconds from some reference point*/

	    // ACQUIRE THE SPECTRA
            wrapper.highSpdAcq_StartAcquisition(spectrometerIndex); 

	    if(wrapper.highSpdAcq_GetNumberOfSpectraAcquired() == 0)
	    {
		continue;
	    }
	    
	    
	    
	    	System.out.println("Spectra acquired");
	
	    // 	dark noise and background
	    wrapper.setIntegrationTime(spectrometerIndex,BackgroundIntegTime); // minimize latency when next hardware trigger occurs
	    wrapper.setExternalTriggerMode(spectrometerIndex,0); // hardware trigger mode - level triggered	    
	    background = wrapper.getSpectrum(spectrometerIndex);
	    System.out.println("Background acquired");
		
            System.out.println("Spectra acquired");
	


            // Extract the newly acquired spectra from the internal buffer area
	    try {
		BufferedWriter outd = new BufferedWriter(new FileWriter("./data/spectra.txt"));
// 		head of the file
		temperatureCelsius = boardTemperature.getBoardTemperatureCelsius();
		System.out.println("board temperature: " + temperatureCelsius);
		outd.write("Data file from Ocean Optics Spectrometer - "+wrapper.getName(spectrometerIndex) +"\n");
		outd.write("Spectrometer S/N: "+wrapper.getSerialNumber(spectrometerIndex) +"\n");
		outd.write("Date and time:\t"+date.format(new Date()) +"\n");
		outd.write("Number of spectra: \t"+wrapper.highSpdAcq_GetNumberOfSpectraAcquired()+"\n");
		outd.write("Number pixels in spectra: \t"+numberOfPixels+"\n");
		outd.write("Integration time [us]: \t"+integrationTime+"\n");
		outd.write("Board temperature [C]: \t"+temperatureCelsius+"\n");
		
		outd.write("Exact time stamps:"+"\n");
		double[] dT = new double [numberOfSpectraRequested];

		for (spectrumNumber=0; spectrumNumber<wrapper.highSpdAcq_GetNumberOfSpectraAcquired(); ++spectrumNumber) {
		    HighResTimeStamp hrts = wrapper.highSpdAcq_GetTimeStamp(spectrumNumber); // this is optional
		    outd.write(hrts.toString()+"\n");	

		    if (spectrumNumber !=0) {
			HighResTimeStamp hrtsMinus1 = wrapper.highSpdAcq_GetTimeStamp(spectrumNumber-1); 
			dT[spectrumNumber] = hrts.getMilliTimeDeltaSince(hrtsMinus1);
			//double dT2 = startTimes[spectrumNumber] - startTimes[spectrumNumber-1];
			//System.out.printf(", dt2[ms]= %12.6f , ", dT2);
		    } else {
			dT[0]=0.;
		    }
		    System.out.printf("dt[ms]= %8.3f \n", dT[spectrumNumber]);
		}
	    

		outd.write("Wavelengths:"+"\n");
		
		double lambda;
		for (int line=0; line<numberOfPixels; ++line) {
		    lambda = (double)((int)(wavelength[line]*1000))/1000;
		    outd.write(lambda+"\n");
		}
		

		
		
		for (spectrumNumber=0; spectrumNumber<wrapper.highSpdAcq_GetNumberOfSpectraAcquired(); ++spectrumNumber) {		    
		    double[] oneSpectrum; // this will contain the raw pixel values comprising one spectrum
		    oneSpectrum = wrapper.highSpdAcq_GetSpectrum(spectrumNumber);
		    for (int line=0; line<numberOfPixels; ++line) {
			outd.write((int)oneSpectrum[line]+"\n");
		    }			    
		} 		
		outd.close();
		
		BufferedWriter background_out = new BufferedWriter(new FileWriter("./data/background.txt"));
		background_out.write("Background file from Ocean Optics Spectrometer - "+wrapper.getName(spectrometerIndex) +"\n");
		background_out.write("Spectrometer S/N: "+wrapper.getSerialNumber(spectrometerIndex) +"\n");
		background_out.write("Date and time:\t"+date.format(new Date()) +"\n");
		background_out.write("Number pixels in spectra: \t"+numberOfPixels+"\n");
		background_out.write("Integration time [us]: \t"+BackgroundIntegTime+"\n");
		for (int line=0; line<numberOfPixels; ++line) {
		    background_out.write((int)background[line]+"\n");
		}		
		background_out.close();		
		
		
		// 	readout noise patterns
		wrapper.highSpdAcq_AllocateBuffer(spectrometerIndex,n_readout_spectra);
		wrapper.setIntegrationTime(spectrometerIndex,integrationTime);
		wrapper.highSpdAcq_StartAcquisition(spectrometerIndex); 
		System.out.println("Acquring readoutnoise patterns");

		readOutPatterns = wrapper.highSpdAcq_GetSpectrum(0);
		double[] oneSpectrum;
		for (spectrumNumber=0; spectrumNumber<n_readout_spectra; ++spectrumNumber) {	    
		    // this will contain the sum of the raw pixel 
		     // this will contain the raw pixel values comprising one spectrum
		    oneSpectrum = wrapper.highSpdAcq_GetSpectrum(spectrumNumber);
		    for (int line=0; line<numberOfPixels; ++line) {
			readOutPatterns[line] = readOutPatterns[line]+oneSpectrum[line];
		    }		    
		} 
		double norm = 0;
		double std_sq = 0;
		//BufferedWriter readoutpatternsFullOut = new BufferedWriter(new FileWriter("./data/readoutpatternsFull.txt"));
		double[] oneSpectrum2;

		for (spectrumNumber=1; spectrumNumber<n_readout_spectra; ++spectrumNumber) {	    
// 		    double[] oneSpectrum; 
		    oneSpectrum2 = wrapper.highSpdAcq_GetSpectrum(spectrumNumber);
		    for (int line=0; line<numberOfPixels; ++line) {
			//readoutpatternsFullOut.write( oneSpectrum2[line] +"\n");

			norm = oneSpectrum2[line]-readOutPatterns[line]/(n_readout_spectra+1.0) ;
			std_sq = std_sq + norm*norm;
		    }		    
		} 
		//readoutpatternsFullOut.close();

		std_sq = std_sq/(( n_readout_spectra-1)* numberOfPixels-1);
	
		
		
		System.out.println("The readoutnoise patterns acquired  "+ std_sq);
    // save readout noise
		BufferedWriter readoutpatternsOut = new BufferedWriter(new FileWriter("./data/readoutpatterns.txt"));
		readoutpatternsOut.write("Readout patterns file from Ocean Optics Spectrometer - "+wrapper.getName(spectrometerIndex) +"\n");
		readoutpatternsOut.write("Spectrometer S/N: "+wrapper.getSerialNumber(spectrometerIndex) +"\n");
		readoutpatternsOut.write("Date and time:\t"+date.format(new Date()) +"\n");
		readoutpatternsOut.write("Number pixels in spectra: \t"+numberOfPixels+"\n");
		readoutpatternsOut.write("Integration time [us]: \t"+integrationTime+"\n");
		readoutpatternsOut.write("Number of averaged spectra: \t"+n_readout_spectra+"\n");
		readoutpatternsOut.write("RMS^2 of signal: \t"+std_sq+"\n");
		for (int line=0; line<numberOfPixels; ++line) {
		    readoutpatternsOut.write((readOutPatterns[line]/(n_readout_spectra+1.0))+"\n");
		}
		readoutpatternsOut.close();
	
		
		// save loopnumber
		BufferedWriter ready = new BufferedWriter(new FileWriter("./ready"));
		ready.write(" ");	
		ready.close();
		    
		}        
		
	    catch (IOException e) {
		System.out.println( e.getMessage() );
		    
	    }
	 
	    System.out.println("end");
// 	    System.runFinalization();
// 	    System.gc();
// 	    try { Thread.sleep(10000); } catch (Exception ee) {}

	    
        }
        wrapper.closeAllSpectrometers();

//         return ;
    }
    
}


  
