// Created with MonoDevelop
//
//    Copyright (C) 2009 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.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

using Hiperscan.Common;
using Hiperscan.Common.RegularExpressions;
using Hiperscan.Unix;

namespace Hiperscan.Spectroscopy.IO
{
    public static partial class CSV
    {
        public static readonly string SampleFileNameRegex = GlobalRegex.SpectrumRegex.SpectrumId.xCompleteWithCsvExtension();
        public static readonly string CustomerFileNameRegex = GlobalRegex.SpectrumRegex.CustomerFilename.xCompleteWithCsvExtension();

        private static volatile bool ignore_correction = false;
        private static volatile uint open_id = 0;


        public static Spectrum Read(string fname, CSVParseOptions parse_option = CSVParseOptions.All)
        {
            using (FileStream stream = new FileStream(fname, FileMode.Open, FileAccess.Read))
            {
                Spectrum spectrum = CSV._Read(stream, parse_option);
                spectrum.FileInfo = new FileInfo(fname);
                spectrum.Id = string.Format("0_{0}_{1}", (CSV.open_id++).ToString("D9"), spectrum.FileInfo.FullName);

                if (string.IsNullOrEmpty(spectrum.Label))
                {
                    spectrum.Label = spectrum.FileInfo.Name;
                    spectrum.DefaultLabel = true;
                }

                return spectrum;
            }
        }

        public static Spectrum Read(Stream stream)
        {
            Spectrum spectrum = CSV._Read(stream, CSVParseOptions.All);
            spectrum.FileInfo = null;
            spectrum.Id = "0_stream_input";
            return spectrum;
        }

        public static IEnumerable<Spectrum> ReadParallel(IEnumerable<string> fnames, CSVParseOptions parse_option = CSVParseOptions.All)
        {
            Dictionary<string, string> ids = fnames.Distinct().ToDictionary(n => n, n => string.Format("0_{0}_{1}", (CSV.open_id++).ToString("D9"), n));

            ConcurrentBag<Spectrum> spectra = new ConcurrentBag<Spectrum>();
            Parallel.ForEach(fnames, fname => 
            {
                Spectrum spectrum = CSV.Read(fname, parse_option);
                spectrum.Id = ids[fname];
                spectra.Add(spectrum);
            });

            return spectra;
        }

        private static Spectrum _Read(Stream stream, CSVParseOptions parse_option)
        {
            string[] lines = null;
            using (var reader = new FileIO.FileStreamReader(stream, true))
            {
                lines = reader.ReadToEnd().Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
            }

            CultureInfo parse_culture = CSV.GetParserCulture(lines);

            List<double> raw_lambda = new List<double>();
            List<double> raw_intensity = new List<double>();
            List<double> raw_absorbance = new List<double>();

            Spectrum spectrum = new Spectrum(WavelengthLimits.DefaultWavelengthLimits);
            string label = string.Empty;
            Color color = Color.Empty;
            LineStyle line_style = LineStyle.Solid;
            float line_width = 0f;
            List<uint> add_data = new List<uint>();

            CultureInfo parser_culture = CSV.GetParserCulture(lines);
            string[] split_separators = new string[] { parser_culture.TextInfo.ListSeparator };

            if (parse_option != CSVParseOptions.All)
                spectrum.IsComplete = false;

            int line_number = 0;

            foreach (string line in lines) 
            {
                line_number++;

                if (string.IsNullOrEmpty(line.Trim()))
                    continue;

                string[] line_data = line.Split(split_separators, StringSplitOptions.RemoveEmptyEntries);
                if (line_data == null || line_data.Length == 0 || line_data[0].Length == 0)
                    continue;

                if (line_data[0][0] == CSV.Parser.HEADER_SIGN)
                {
                    line_data = line.Split(CSV.Parser.ASSIGN_SIGN);

                    if (line_data.Length < 2)
                        continue;

                    string parser_type = line_data[0].Remove(0, 1).Trim();
                    switch (parser_type)
                    {
                        case Parser.Culture:
                            break;

                        case Parser.SpectrumTime:
                            spectrum.Timestamp =
                                (DateTime)CSV.ParseLine(line, Parser.SpectrumTime, parser_culture);
                            break;

                        case Parser.DarkIntensity:
                            spectrum.DarkIntensity.SetIntensity((double)CSV.ParseLine(line, Parser.DarkIntensity, parser_culture));
                            break;

                        case Parser.DarkIntensitySigma:
                            spectrum.DarkIntensity.SetSigma((double)CSV.ParseLine(line, Parser.DarkIntensitySigma, parser_culture));
                            break;

                        case Parser.DarkIntensityTime:
                            spectrum.DarkIntensity.SetDateTime((DateTime)CSV.ParseLine(line, Parser.DarkIntensityTime, parser_culture));
                            break;

                        case Parser.WavelengthCorrection:
                            if (CSV.ignore_correction)
                                continue;
                            spectrum.WavelengthCorrection =
                                 (WavelengthCorrection)CSV.ParseLine(line, Parser.WavelengthCorrection, parser_culture);
                            break;

                        case Parser.WavelengthLimits:
                            if (CSV.ignore_correction)
                                continue;
                            spectrum.WavelengthLimits =
                                 (WavelengthLimits)CSV.ParseLine(line, Parser.WavelengthLimits, parser_culture);
                            break;

                        case Parser.AdcCorrection:
                            if (CSV.ignore_correction)
                                continue;
                            spectrum.AdcCorrection =
                                (DataSet)CSV.ParseLine(line, Parser.AdcCorrection, parser_culture);
                            break;

                        case Parser.WavelengthCorrectionTime:
                            if (spectrum.HasWavelengthCorrection)
                            {
                                spectrum.WavelengthCorrection.Timestamp =
                                     (DateTime)CSV.ParseLine(line, Parser.WavelengthCorrectionTime, parser_culture);
                            }
                            break;

                        case Parser.LinearDistortion:
                            if (CSV.ignore_correction)
                                continue;
                            spectrum.LinearDistortion =
                                 (LinearDistortion)CSV.ParseLine(line, Parser.LinearDistortion, parser_culture);
                            break;

                        case Parser.LinearDistortionTime:
                            if (spectrum.HasLinearDistortion)
                            {
                                spectrum.LinearDistortion.Timestamp =
                                    (DateTime)CSV.ParseLine(line, Parser.LinearDistortionTime, parser_culture);
                            }
                            break;

                        case Parser.LimitBandwidth:
                            spectrum.LimitBandwidth =
                                (bool)CSV.ParseLine(line, Parser.LimitBandwidth, parser_culture);
                            break;

                        case Parser._LimitWavelength:
                            spectrum.LimitBandwidth =
                                (bool)CSV.ParseLine(line, Parser._LimitWavelength, parser_culture);
                            break;

                        case Parser.SerialNumber:
                            spectrum.Serial =
                                (string)CSV.ParseLine(line, Parser.SerialNumber, parser_culture);
                            break;

                        case Parser.FirmwareVersion:
                            spectrum.FirmwareVersion =
                                (string)CSV.ParseLine(line, Parser.FirmwareVersion, parser_culture);
                            break;

                        case Parser.FirmwareRevision:
                            spectrum.FirmwareRevision =
                                (string)CSV.ParseLine(line, Parser.FirmwareRevision, parser_culture);
                            break;

                        case Parser.LutTimestamp:
                            spectrum.LutTimestamp =
                                (string)CSV.ParseLine(line, Parser.LutTimestamp, parser_culture);
                            break;

                        case Parser.HardwareVersionInfo:
                            spectrum.HardwareInfo =
                                (byte[])CSV.ParseLine(line, Parser.HardwareVersionInfo, parser_culture);
                            break;

                        case Parser.AverageCount:
                            spectrum.AverageCount =
                                (int)CSV.ParseLine(line, Parser.AverageCount, parser_culture);
                            break;

                        case Parser.SpectralResolution:
                            spectrum.SpectralResolution =
                                (double)CSV.ParseLine(line, Parser.SpectralResolution, parser_culture);
                            break;

                        case Parser.SpectrumType:
                            spectrum.SpectrumType =
                                (SpectrumType)CSV.ParseLine(line, Parser.SpectrumType, parser_culture);
                            break;

                        case Parser.ProbeType:
                            spectrum.ProbeType =
                                (ProbeType)CSV.ParseLine(line, Parser.ProbeType, parser_culture);
                            break;

                        case Parser.Creator:
                            spectrum.Creator =
                                (string)CSV.ParseLine(line, Parser.Creator, parser_culture);
                            break;

                        case Parser.Label:
                            label = (string)CSV.ParseLine(line, Parser.Label, parser_culture);
                            break;

                        case Parser.LineColor:
                            color = (Color)CSV.ParseLine(line, Parser.LineColor, parser_culture);
                            break;

                        case Parser.LineStyle:
                            line_style = (LineStyle)CSV.ParseLine(line, Parser.LineStyle, parser_culture);
                            break;

                        case Parser.LineWidth:
                            line_width = (float)CSV.ParseLine(line, Parser.LineWidth, parser_culture);
                            break;

                        case Parser.Comment:
                            if (String.IsNullOrEmpty(spectrum.Comment) == false)
                                spectrum.Comment += "\n";
                            spectrum.Comment +=
                                (string)CSV.ParseLine(line, CSV.Parser.Comment, parser_culture);
                            break;

                        case Parser.AddData:
                            add_data.Add((uint)CSV.ParseLine(line, CSV.Parser.AddData, parser_culture));
                            break;

                        default:
                            // skip unknown header entries
                            continue;

                    }
                }
                else
                {
                    try
                    {
                        if (parse_option == CSVParseOptions.OnlyHeaderInfos)
                            return spectrum;

                        raw_lambda.Add(double.Parse(line_data[0],
                                                    NumberStyles.AllowDecimalPoint |
                                                    NumberStyles.AllowExponent,
                                                    parser_culture));

                        raw_intensity.Add(double.Parse(line_data[1],
                                                       NumberStyles.AllowDecimalPoint |
                                                       NumberStyles.AllowExponent,
                                                       parser_culture));

                        // read absorbance if there is a third column
                        if (line_data.Length > 2)
                        {
                            raw_absorbance.Add(double.Parse(line_data[2],
                                                            NumberStyles.AllowDecimalPoint |
                                                            NumberStyles.AllowExponent |
                                                            NumberStyles.AllowLeadingSign,
                                                            parser_culture));
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(String.Format(Catalog.GetString("Cannot read CSV file in line {0}: Misformed data section: {1}"),
                                                                line_number,
                                                                ex.Message), ex);
                    }
                }
            }

            if (parse_option != CSVParseOptions.All)
                return spectrum;

            spectrum.RawIntensity = new DataSet(raw_lambda, raw_intensity);
            if (raw_absorbance.Count == raw_lambda.Count)
            {
                if (spectrum.HasDarkIntensity == false)
                    throw new Exception(Catalog.GetString("Absorbance spectra have to define dark intensity values."));

                double di = spectrum.DarkIntensity.Intensity;
                DataSet raw_absorbance_ds = new DataSet(raw_lambda, raw_absorbance);
                DataSet raw_intensity_di_ds = spectrum.RawIntensity - new DataSet.Operand(0.0, di);

                // negative intensity is not defined, zero intensity would be infinite absorbance
                for (int ix = 0; ix < raw_intensity_di_ds.Count; ++ix)
                {
                    if (raw_intensity_di_ds.Y[ix] < 1e-10)
                        raw_intensity_di_ds.Y[ix] = 1e-10;
                }

                DataSet raw_reference_di_ds = DataSet.InverseAbsorbance(raw_intensity_di_ds, raw_absorbance_ds);
                DataSet raw_reference_ds = raw_reference_di_ds + di;
                Spectrum reference = new Spectrum(raw_reference_ds, spectrum.WavelengthLimits)
                {
                    WavelengthCorrection = spectrum.WavelengthCorrection,
                    AdcCorrection = spectrum.AdcCorrection
                };
                spectrum.Reference = reference;

                foreach (double y in spectrum.Absorbance.Y)
                {
                    if (double.IsInfinity(y))
                        throw new Exception(Catalog.GetString("Absorbance contains infinite values."));
                    if (double.IsNaN(y))
                        throw new Exception(Catalog.GetString("Absorbance contains invalid values."));
                }
            }

            if (string.IsNullOrEmpty(label) == false)
                spectrum.Label = label;

            if (color != Color.Empty)
                spectrum.Color = color;

            if (line_style != LineStyle.Solid)
                spectrum.LineStyle = line_style;

            if (line_width > 0f)
                spectrum.LineWidth = line_width;

            if (add_data.Count > 0)
                spectrum.AddData = add_data.ToArray();

            return spectrum;
        }

        public static AddData ReadAdditionalData(FileInfo file)
        {
            string[] lines = FileIO.ReadFileToEndWithEncodingFromBOM(file.FullName)
                .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);

            CultureInfo parse_culture = CSV.GetParserCulture(lines);
            Regex regex = new Regex(parse_culture.TextInfo.ListSeparator);

            AddData add_data = new AddData();

            int skipped = 0;
            int line_number = 0;

            foreach(string line in lines)
            {
                ++line_number;
                string[] s = regex.Split(line);

                try
                {
                    double.Parse(s[0],
                                 NumberStyles.AllowDecimalPoint |
                                 NumberStyles.AllowExponent,
                                 parse_culture);

                    double.Parse(s[1],
                                 NumberStyles.AllowDecimalPoint |
                                 NumberStyles.AllowExponent,
                                 parse_culture);

                    // csv section has started -> no more header data available
                    break;
                }
                catch (Exception ex)
                {
                    ++skipped;

                    if (line_number > Parser.MaxHeaderLines)
                    {
                        throw new Exception(Catalog.GetString("Cannot read CSV file: The file header seems to be too long") +
                                                              " (" + ex.Message + ").", ex);
                    }

                    try
                    {
                        string new_culture = (string)CSV.ParseLine(line, CSV.Parser.Culture, parse_culture);
                        continue;
                    }
                    catch (InvalidDataException) { }
                    catch (Exception)
                    {
                        throw;
                    }

                    try
                    {
                        add_data.DateTime = (DateTime)CSV.ParseLine(line, CSV.Parser.SpectrumTime, parse_culture);
                        continue;
                    }
                    catch (InvalidDataException) { }
                    catch (Exception)
                    {
                        throw;
                    }

                    try
                    {
                        add_data.Serial = (string)CSV.ParseLine(line, CSV.Parser.SerialNumber, parse_culture);
                        continue;
                    }
                    catch (InvalidDataException) { }
                    catch (Exception)
                    {
                        throw;
                    }

                    try
                    {
                        add_data.Data.Add((uint)CSV.ParseLine(line, CSV.Parser.AddData, parse_culture));
                        continue;
                    }
                    catch (InvalidDataException) { }
                    catch (Exception)
                    {
                        throw;
                    }
                }
            }
            return add_data;
        }

        public static List<AddData> ReadAdditionalData(DirectoryInfo dir)
        {
            FileInfo[] files = dir.GetFiles("*.csv", SearchOption.AllDirectories);
            List<AddData> list = new List<AddData>();

            foreach (FileInfo file in files)
            {
                AddData add_data = ReadAdditionalData(file);
                list.Add(add_data);
            }

            return list;
        }

        private static CultureInfo GetParserCulture(string[] lines)
        {
            CultureInfo parser = new CultureInfo(Spectrum.STANDARD_CULTURE);

            foreach (string line in lines)
            {
                string[] split_line = line.Split('=');

                // skip custom or empty comments
                if (split_line.Length < 2)
                    continue;

                if (split_line[0].Remove(0, 1).Trim() == Parser.Culture)
                {
                    string new_culture = (string)CSV.ParseLine(line, Parser.Culture, CultureInfo.InvariantCulture);
                    parser = new CultureInfo(new_culture);
                    break;
                }
            }

            return parser;
        }







        public static void Write(Spectrum spectrum, string fname, byte[] salt = null)
        {
            if (Path.GetExtension(fname).ToLower() != ".csv")
                fname = Path.ChangeExtension(fname, ".csv");

            using (FileStream stream = new FileStream(fname, FileMode.Create, FileAccess.Write))
            {
                CSV.Write(spectrum, stream, salt);
            }

            spectrum.FileInfo = new FileInfo(fname);
        }

        public static void Write(Spectrum spectrum, Stream stream, byte[] salt = null)
        {
            byte[] checksum = null;

            if (salt != null && salt.Length != 0)
                checksum = CSV.CreateChecksum(spectrum, salt);

            using (StreamWriter writer = new StreamWriter(stream))
            {
                CSV._Write(spectrum, writer, checksum);
            }
        }

        public static void Write(Spectrum spectrum, StreamWriter writer)
        {
            CSV._Write(spectrum, writer, null);
        }

        private static void _Write(Spectrum spectrum, StreamWriter writer, byte[] checksum)
        {
            CultureInfo culture = spectrum.CultureInfo;

            writer.WriteLine("#");
            writer.WriteLine($"# {Parser.Culture}={culture.ToString()}");
            writer.WriteLine($"# {Parser.SpectrumTime}={spectrum.Timestamp.ToString(DateTimeFormat.ISO_8601_TIME_ZONE)}");

            if (checksum != null)
                writer.WriteLine($"# {Parser.Checksum}={CSV.GetStringFromHash(checksum)}");

            writer.WriteLine($"# {Parser.DarkIntensity}={spectrum.DarkIntensity.Intensity.ToString(culture)}");
            writer.WriteLine($"# {Parser.DarkIntensitySigma}={spectrum.DarkIntensity.Sigma.ToString(culture)}");
            writer.WriteLine($"# {Parser.DarkIntensityTime}={spectrum.Timestamp.ToString(DateTimeFormat.ISO_8601_TIME_ZONE)}");

            if (spectrum.HasLinearDistortion)
            {
                writer.Write($"# {Parser.LinearDistortion}=");
                writer.Write(spectrum.LinearDistortion.Offset.ToString(culture));
                writer.Write(culture.TextInfo.ListSeparator);
                writer.Write(spectrum.LinearDistortion.Factor.ToString(culture));
                writer.WriteLine();

                writer.WriteLine($"# {Parser.LinearDistortionTime}={spectrum.LinearDistortion.Timestamp.ToString(DateTimeFormat.ISO_8601_TIME_ZONE)}");
            }

            if (spectrum.HasWavelengthCorrection)
            {
                writer.WriteLine($"# {Parser.WavelengthCorrection}={spectrum.WavelengthCorrection.ToString(culture)}");
                writer.WriteLine($"# {Parser.WavelengthCorrectionTime}={spectrum.WavelengthCorrection.Timestamp.ToString(DateTimeFormat.ISO_8601_TIME_ZONE)}");

                string wlc_master_spectrum_id = $"# {Parser.WlcMasterSpectrumId}={spectrum.WlcMasterSpectrumId.ToString(culture)}";
                if (spectrum.WlcMasterSpectrumId == Spectrum.UNKNOWN_WLC_MASTER_SPECTRUM_ID)
                    wlc_master_spectrum_id += Parser.UnknownMasterSpectrumId;

                writer.WriteLine(wlc_master_spectrum_id);
            }

            if (spectrum.HasWavelengthLimits)
                writer.WriteLine($"# {Parser.WavelengthLimits}={spectrum.WavelengthLimits.ToString(culture)}");

            if (spectrum.HasAdcCorrection)
            {
                writer.Write($"# {Parser.AdcCorrection}=");
                for (int ix = 0; ix < spectrum.AdcCorrection.Count; ++ix)
                {
                    writer.Write(spectrum.AdcCorrection.X[ix].ToString(culture));
                    writer.Write(culture.TextInfo.ListSeparator);
                    writer.Write(spectrum.AdcCorrection.Y[ix].ToString(culture));
                    if (ix < spectrum.AdcCorrection.Count - 1)
                        writer.Write(culture.TextInfo.ListSeparator);
                    else
                        writer.WriteLine();
                }
            }

            writer.WriteLine($"# {Parser.LimitBandwidth}={spectrum.LimitBandwidth.ToString(culture)}");
            writer.WriteLine($"# {Parser.SerialNumber}={spectrum.Serial}");
            writer.WriteLine($"# {Parser.FirmwareVersion}={spectrum.FirmwareVersion}");
            writer.WriteLine($"# {Parser.FirmwareRevision}={spectrum.FirmwareRevision}");
            writer.WriteLine($"# {Parser.LutTimestamp}={spectrum.LutTimestamp}");

            if (spectrum.HardwareInfo != null && spectrum.HardwareInfo.Length >= 4)
            {
                writer.Write($"# {Parser.HardwareVersionInfo}=");
                for (int ix = 0; ix < spectrum.HardwareInfo.Length; ++ix)
                {
                    writer.Write(spectrum.HardwareInfo[ix].ToString(culture));
                    if (ix < spectrum.HardwareInfo.Length - 1)
                        writer.Write(culture.TextInfo.ListSeparator);
                    else
                        writer.WriteLine();
                }
            }

            writer.WriteLine($"# {Parser.SpectralResolution}={spectrum.SpectralResolution.ToString(culture)}");
            writer.WriteLine($"# {Parser.AverageCount}={spectrum.AverageCount.ToString(culture)}");

            if (spectrum.HasAbsorbance)
            {
                double max_absorbance = DataSet.AbsorbanceLimit(spectrum.Reference.Intensity, spectrum.DarkIntensity);
                writer.WriteLine($"# {Parser.MaxAbsorbance}={max_absorbance.ToString(culture)}");
            }

            if (spectrum.SpectrumType != SpectrumType.Unknown)
                writer.WriteLine($"# {Parser.SpectrumType}={spectrum.SpectrumType.ToString()}");

            if (spectrum.ProbeType != ProbeType.Unknown)
                writer.WriteLine($"# {Parser.ProbeType}={spectrum.ProbeType.ToString()}");

            if (string.IsNullOrEmpty(spectrum.Creator) == false)
                writer.WriteLine($"# {Parser.Creator}={spectrum.Creator}");

            if (string.IsNullOrEmpty(spectrum.Label) == false && spectrum.DefaultLabel == false)
                writer.WriteLine($"# {Parser.Label}={spectrum.Label}");

            if (spectrum.DefaultStyle == false)
            {
                writer.Write($"# {Parser.LineColor}=");
                writer.WriteLine("{1}{0}{2}{0}{3}{0}{4}", culture.TextInfo.ListSeparator,
                                 spectrum.Color.A, spectrum.Color.R,
                                 spectrum.Color.G, spectrum.Color.B);
                writer.WriteLine($"# {Parser.LineStyle}={spectrum.LineStyle.ToString()}");
                writer.WriteLine($"# {Parser.LineWidth}={spectrum.LineWidth.ToString(culture)}");
            }

            if (spectrum.Comment != string.Empty)
            {
                string[] lines = spectrum.Comment.Split('\n', '\r');

                foreach (string line in lines)
                {
                    if (string.IsNullOrEmpty(line.Trim()))
                        continue;

                    writer.WriteLine($"# {Parser.Comment}={line}");
                }
            }

            if (spectrum.AddData != null && spectrum.AddData.Length > 0)
            {
                foreach (uint data in spectrum.AddData)
                {
                    writer.WriteLine($"# {Parser.AddData}={data.ToString("d")}");
                }
            }

            writer.WriteLine("#");
            writer.WriteLine($"# Raw wavelength{culture.TextInfo.ListSeparator}Raw intensity{culture.TextInfo.ListSeparator}Raw absorbance");

            if (spectrum.HasAbsorbance)
            {
                bool sav_limit_bandwith = spectrum.LimitBandwidth;
                spectrum.LimitBandwidth = false;

                LinearDistortion sav_ld = spectrum.LinearDistortion;
                spectrum.LinearDistortion = null;

                DataSet sav_adc_correction = spectrum.AdcCorrection;
                spectrum.AdcCorrection = null;

                WavelengthLimits sav_limits = spectrum.WavelengthLimits;
                spectrum.WavelengthLimits = null;

                try
                {
                    for (int ix = 0; ix < spectrum.Count; ++ix)
                    {
                        writer.WriteLine(spectrum.RawIntensity.X[ix].ToString(culture) +
                                         culture.TextInfo.ListSeparator +
                                         spectrum.RawIntensity.Y[ix].ToString(culture) +
                                         culture.TextInfo.ListSeparator +
                                         spectrum.Absorbance.Y[ix].ToString(culture));
                    }
                }
                finally
                {
                    spectrum.LimitBandwidth = sav_limit_bandwith;
                    spectrum.LinearDistortion = sav_ld;
                    spectrum.AdcCorrection = sav_adc_correction;
                    spectrum.WavelengthLimits = sav_limits;
                }
            }
            else
            {
                for (int ix = 0; ix < spectrum.Count; ++ix)
                {
                    writer.WriteLine(spectrum.RawIntensity.X[ix].ToString(culture) +
                                     culture.TextInfo.ListSeparator +
                                     spectrum.RawIntensity.Y[ix].ToString(culture));
                }
            }
        }

        public static bool CheckIntegrity(string fname, byte[] salt)
        {
            using (FileStream reader = new FileStream(fname, FileMode.Open, FileAccess.Read))
            {
                return CSV.CheckIntegrity(reader, salt);
            }
        }

        public static bool CheckIntegrity(Stream stream, byte[] salt)
        {
            byte[] saved_checksum = null;

            using (MemoryStream ms = new MemoryStream())
            {
                stream.CopyTo(ms);
                stream.Position = 0;

                using (StreamReader reader = new StreamReader(stream))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {

                        if (line.StartsWith("#", StringComparison.InvariantCulture) == false)
                            break;

                        if (line.StartsWith("# " + Parser.Checksum, StringComparison.InvariantCulture) == false)
                            continue;

                        string[] s = line.Split('=');

                        if (s.Length != 2)
                            throw new FormatException(Catalog.GetString("Cannot parse header line: Invalid checksum data."));

                        saved_checksum = CSV.GetHashFromString(s[1]);
                        break;
                    }
                }

                if (saved_checksum == null)
                    throw new InvalidDataException(Catalog.GetString("Spectrum file has no checksum."));

                byte[] actual_checksum = CSV.CreateChecksum(ms, salt);
                return actual_checksum.SequenceEqual(saved_checksum);
            }
        }

        public static bool _IgnoreCorrection
        {
            get { return CSV.ignore_correction; }
            set { CSV.ignore_correction = value; }
        }
    }
}
