import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;

import otherSupport.SettingsManager;
import comedi.ComediException;
import comedi.ComediOptions;
import comediJNI.Comedi;
import comediJNI.ComediDef;
import comediJNI.ComediJNIException;

/*
 * Asynchronous Digital Output Example

 * Part of Comedilib
 *
 * Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
 * Copyright (c) 2007 Frank Mori Hess <fmhess@users.sourceforge.net>
 *
 * This file may be freely modified, distributed, and combined with
 * other software, as long as proper attribution is given in the
 * source code.
 * 
 * Ported to javaJNI by oliford <codes<at>oliford.co.uk>
 */

/*
 * Requirements: NI M-Series board.
 *
 * This demo uses the digital io subdevice with an
 * asynchronous command to generate waveforms.  The
 * waveforms in this example are square waves with
 * varying periods depending on the digital output
 * channel.  Channel N outputs a square wave with
 * period 2*(N+1) clocks. The command line argument chooses the
 * clock signal for updating the outputs.  As a suggestion,
 * you might use the output of one of the general
 * purpose counters for a clock (the default clock
 * source is the output of general purpose counter 0),
 * and run the gpct_pulse_generator demo to start the counter
 * generating pulses on its output.
 *
 * Note, you must configure at least one of the digital channels as
 * an output (for example by running the dio demo program)
 * before running this program. You must also pass the dio
 * subdevice file using the -f option, since the default write
 * subdevice for the m-series boards is the analog output.  For
 * example, "dio_waveform -f /dev/comedi0_sub2".
 */

public class do_waveform {
	static { new SettingsManager("minerva", true); 	}
	
	private static final int BUF_LEN = 0x1000;
	
	private static class TestWaveformGenerator	{
		
		public TestWaveformGenerator(int num_channels) {
			
			this.num_channels = num_channels;
			toggle_countdowns = new int[num_channels];		
			for(int i=0; i < num_channels; i++)
				toggle_countdowns[i] = i + 1;
			last_output = 0;
		}
		
		int toggle_countdowns[];
		int num_channels;
		int /*lsampl_t*/ last_output;
	};
	
	private static void build_waveforms(TestWaveformGenerator generator, ByteBuffer bBuf) {
		IntBuffer iBuf = bBuf.asIntBuffer();
		iBuf.clear();
		
		while(iBuf.hasRemaining()) {
			int val = generator.last_output;
			
			for(int j=0; j < generator.num_channels; j++) {
				
				if(--generator.toggle_countdowns[j] == 0) {
					generator.toggle_countdowns[j] = j + 1;
					val ^= 1 << j;
				}
			}
			iBuf.put(val);
			generator.last_output = val;
		}

		bBuf.rewind();
		bBuf.limit(bBuf.capacity());
	}
	
	public static void main(String[] args) {
		
		ComediDef.Cmd cmd;
		long dev;
		
		int total=0;
		int chanlist[];
		int ret;
	
		//init_parsed_options(&options);

		ComediOptions options = new ComediOptions();
		options.fileName = "/dev/comedi0_subd2";
		options.subdevice = 2;
		
		// These don't work here, all 8 channels get driven anyway 
		options.n_chan = 8;
		options.channel = 0;
		
		//options.range = 0;
		//options.aref = ComediDef.AREF_GROUND;
		//options.n_scan = 1000;
		//options.freq = 1000.0;
		//options.physical = true;

		options.value = ComediDef.NI_CDIO_SCAN_BEGIN_SRC_G0_OUT;
		
		//parse_options(&options, argc, argv);
	
		dev = Comedi.open(options.fileName);
		if(dev == 0)
			throw new ComediJNIException("comedi_open");
		
		if(options.subdevice < 0)
			options.subdevice = Comedi.find_subdevice_by_type(dev, ComediDef.COMEDI_SUBD_DIO, 0);
	
		cmd = new ComediDef.Cmd();
		cmd.subdev = options.subdevice;
		cmd.flags = ComediDef.CMDF_WRITE;
		cmd.start_src = ComediDef.TRIG_INT;
		cmd.start_arg = 0;
		cmd.scan_begin_src = ComediDef.TRIG_EXT;
		cmd.scan_begin_arg = (int)options.value;
		cmd.convert_src = ComediDef.TRIG_NOW;
		cmd.convert_arg = 0;
		cmd.scan_end_src = ComediDef.TRIG_COUNT;
		cmd.scan_end_arg = options.n_chan;
		cmd.stop_src = ComediDef.TRIG_NONE;
		cmd.stop_arg = 0;
	
		chanlist = new int[options.n_chan];
		
		cmd.chanlist = chanlist;
		cmd.chanlist_len = options.n_chan;
		for(int i=0; i < cmd.chanlist_len; i++)
			cmd.chanlist[i] = options.channel + i;
		
		cmd.dump();
	
		ret = Comedi.command_test(dev, cmd);
		if(ret < 0)
			throw new ComediJNIException("comedi_command_test(#1) = "+ret);
	
		ret = Comedi.command_test(dev, cmd);
		if(ret < 0)
			throw new ComediJNIException("comedi_command_test(#2) = "+ret);
	
		ret = Comedi.command_test(dev, cmd);
		if(ret != 0)
			throw new ComediJNIException("comedi_command_test(#3) = "+ret);
		
		ret = Comedi.command(dev, cmd);
		if(ret < 0)
			throw new ComediJNIException("comedi_command");
	
		TestWaveformGenerator generator = new TestWaveformGenerator(8);
		
		ByteBuffer bBuf = ByteBuffer.allocate(BUF_LEN * 8);
		bBuf.order(ByteOrder.LITTLE_ENDIAN);
		build_waveforms(generator, bBuf);
		
		try{
			
			FileDescriptor fd = Comedi.getFileDesc(dev);
			FileOutputStream fin = new FileOutputStream(fd);
			FileChannel chan = fin.getChannel();
			
			int bytes_written = chan.write(bBuf);
			
			if(bytes_written < 0) {
				throw new RuntimeException("Write error");
				
			}else if(bytes_written < bBuf.capacity()) {
				throw new RuntimeException("Failed to preload output buffer with " + bBuf.capacity() + " bytes, is it too small? "
												+ "See the --write-buffer option of comedi_config");
			}
			System.out.println("wrote  " + bytes_written + " bytes of preload");
		
			ret = Comedi.internal_trigger(dev, options.subdevice, 0);
			if(ret < 0)
				throw new ComediJNIException("comedi_internal_trigger");

			System.out.println("Start triggered");
			
		
			while(true) {
				System.out.println("Preparing next buffer sequence");
				build_waveforms(generator, bBuf);
				int n = BUF_LEN * 8;
				while(bBuf.hasRemaining()) {
					bytes_written = chan.write(bBuf);				
					if(bytes_written < 0)
						throw new RuntimeException("Write error");
					if(bytes_written == 0){
						System.out.println("Nothing written, sleeping");
						try{
						Thread.sleep(500);
						}catch(InterruptedException err){}
						
					}else{
						System.out.println("wrote  " + bytes_written + " bytes, now " + bBuf.remaining() + " remaining");
					}
				
				}
				total += BUF_LEN;
			}
			
		}catch(IOException err){
			throw new RuntimeException(err);
		}
	}
}
