// Created with MonoDevelop
//
//    Copyright (C) 2012 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using System.Collections.Generic;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.SGS
{
	internal class Device0_255 : Device0_20
	{
		protected new class Protocol : Device0_20.Protocol
		{
		}

		public Device0_255(DeviceInfo info, VersionInfo version_info) : base (info, version_info)
		{
        }

        protected override void ReadSpectrum(out uint[] add_data, out double[] lambdas, out double[] intensities, IdleTaskHandler idle_task)
        {
            this.usb.Purge();

            // start acquisition
            this.PrintCommandInfo(Protocol.AcquireSpectrum.Query, true);
            uint result = this.Write(Protocol.AcquireSpectrum.Query);
            if (result != Protocol.AcquireSpectrum.Query.Length)
                throw new SGSException(Catalog.GetString("Transmission interrupted."), this);

            // wait for response
            byte[] header = this.ReadBlocking((uint)Protocol.HeaderLength + 1, this.CurrentConfig.Timeout, idle_task);
            this.PrintCommandInfo(header, false);
            if (header[0] != Protocol.AcquireSpectrum.Query[0] ||
                header[1] != Protocol.AcquireSpectrum.Query[1])
            {
                Console.WriteLine("Wrong device response (command ID)."); 
                throw new SGSException(Catalog.GetString("Wrong device response (command ID)."), this);
            }

            int count          = header[2] + (header[3] << 8);
            int add_data_count = header[4] * sizeof(uint);

            // read spectrum data
            byte[] rx_buf = this.ReadBlocking((uint)(count-1), this.CurrentConfig.Timeout);

            int cycles = rx_buf[0] + (rx_buf[1] << 8) + (rx_buf[2] << 16) + (rx_buf[3] << 24);
            int temp   = rx_buf[4] + (rx_buf[5] << 8);

            double lambda_start = (double)(rx_buf[6] + (rx_buf[7] << 8)) / 10f;
            double lambda_step  = (double)(rx_buf[8] + (rx_buf[9] << 8)) / 10f;
            int dark_intensity  = rx_buf[10] + (rx_buf[11] << 8);

            double mirror_freq  = (double)this.clock_frequency / (double)cycles / 2.0;
            int    sample_count = (count-12-add_data_count)/2;
            double sample_rate  = (double)sample_count * mirror_freq * 2f;
            int    offset;

            lock (this)
            {
                this.acquisition_info = new AcquisitionInfo(mirror_freq, sample_count, sample_rate);

                if (!double.IsInfinity(mirror_freq) && !double.IsNaN(mirror_freq))
                {
                    TimeSpan span = DateTime.Now - this.creation_time;
                    this.frequency.Add((double)span.TotalSeconds, mirror_freq);
                }
            }

            if (sample_count > 1500 && sample_count < 4000 && sample_count % 2 == 0)
            {
                lambdas = new double[sample_count/2];
                for (int ix=0; ix < sample_count/2; ++ix)
                {
                    lambdas[ix] = (double)(rx_buf[12+2*ix] + (rx_buf[13+2*ix] << 8)) / 10.0;
                }

                intensities = new double[sample_count/2];
                offset = sample_count/2;
                for (int ix=offset; ix < sample_count; ++ix)
                {
                    intensities[ix-offset] = (double)(rx_buf[12+2*ix] + (rx_buf[13+2*ix] << 8)) / 10000.0;
                }
            }
            else
            {
                lambdas = new double[sample_count];
                for (int ix=0; ix < sample_count; ++ix)
                {
                    lambdas[ix] = lambda_start + (double)ix * lambda_step;
                }

                intensities = new double[sample_count];
                for (int ix=0; ix < sample_count; ++ix)
                {
                    intensities[ix] = (double)(rx_buf[12+2*ix] + (rx_buf[13+2*ix] << 8)) / 10000.0;
                }
            }

            if (this.CheckIntensity(lambdas, intensities) == false)
                throw new InvalidLUTException(Catalog.GetString("Device returned invalid spectrum. This can be caused by an invalid configuration. Please restart spectrometer."), this);
            
            add_data = new uint[3 + add_data_count/sizeof(uint)];
            add_data[0] = (uint)cycles;
            add_data[1] = (uint)temp;
            add_data[2] = (uint)dark_intensity;
            offset  = 12 + 2 * sample_count; 
            for (int ix=0; ix < add_data_count/sizeof(uint); ++ix)
            {
                add_data[3+ix] = (uint)(((uint)rx_buf[offset + ix*4]        ) +
                                        ((uint)rx_buf[offset + ix*4+1] << 8 ) +
                                        ((uint)rx_buf[offset + ix*4+2] << 16) +
                                        ((uint)rx_buf[offset + ix*4+3] << 24) 
                                       );

            }
        }
	}
}
