// 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.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Xml.Serialization;


namespace Hiperscan.Spectroscopy.IO
{

    public static partial class CSV
    {

        public static void WriteContainer(List<Spectrum> spectra, string fname, CultureInfo culture, byte[] salt = null)
        {
            CSV.WriteContainer(spectra, new List<MetaData>(), fname, culture, salt);
        }

        public static void WriteContainer(List<Spectrum> spectra, List<MetaData> meta_data, string fname, CultureInfo culture, byte[] salt = null)
        {
            if (Path.GetExtension(fname).ToLower() != ".csvc")
                fname = Path.ChangeExtension(fname, ".csvc");

            using (Stream stream = File.Open(fname, FileMode.Create))
            {
                CSV.WriteContainer(spectra, meta_data, stream, culture, salt);
            }
        }

        public static void WriteContainer(List<Spectrum> spectra, Stream stream, CultureInfo culture, byte[] salt = null)
        {
            CSV.WriteContainer(spectra, new List<MetaData>(), stream, culture, salt);
        }

        public static void WriteContainer(List<Spectrum> spectra, List<MetaData> meta_data, Stream stream, CultureInfo culture, byte[] salt = null)
        {
            List<byte[]> content = new List<byte[]>();

            foreach (Spectrum spectrum in spectra)
            {
                using (MemoryStream mstream = new MemoryStream())
                using (StreamWriter writer  = new StreamWriter(mstream))
                {
                    writer.Write(spectrum.Id);
                    writer.Flush();
                    content.Add(mstream.ToArray());
                }
                using (MemoryStream mstream = new MemoryStream())
                {
                    spectrum.DefaultLabel = false;
                    CSV.Write(spectrum, mstream, culture, salt);
                    content.Add(mstream.ToArray());
                }
            }

            foreach (MetaData md in meta_data)
            {
                using (MemoryStream mstream = new MemoryStream())
                using (StreamWriter writer  = new StreamWriter(mstream))
                {
                    writer.Write(Parser.Metadata + md.Type);
                    writer.Flush();
                    content.Add(mstream.ToArray());
                }
                content.Add(md.Data);
            }

            using (MemoryStream mstream = new MemoryStream())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(List<byte[]>));
                serializer.Serialize(mstream, content);

                mstream.Position = 0;

                using (GZipStream zstream = new GZipStream(stream, CompressionMode.Compress))
                {
                    CSV.StreamCopy(mstream, zstream);
                }
            }
        }

        public static Spectrum[] ReadContainer(string fname)
        {
            return CSV.ReadContainer(fname, out List<MetaData> meta_data);
        }

        public static Spectrum[] ReadContainer(Stream stream)
        {
            return CSV.ReadContainer(stream, out List<MetaData> meta_data);
        }

        public static Spectrum[] ReadContainer(string fname, out List<MetaData> meta_data)
        {
            using (Stream stream = File.OpenRead(fname))
            {
                return CSV.ReadContainer(stream, out meta_data);
            }
        }

        public static Spectrum[] ReadContainer(Stream stream, out List<MetaData> meta_data)
        {
            meta_data = new List<MetaData>();

            using (GZipStream zstream = new GZipStream(stream, CompressionMode.Decompress))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(List<byte[]>));
                List<Spectrum> spectra = new List<Spectrum>();
                List<byte[]> content = (List<byte[]>)serializer.Deserialize(zstream);

                for (int ix = 0; ix < content.Count; ix += 2)
                {
                    string id;
                    using (MemoryStream mstream = new MemoryStream(content[ix]))
                    {
                        using (StreamReader reader = new StreamReader(mstream))
                        {
                            id = reader.ReadToEnd();

                            if (id.StartsWith(Parser.Metadata, StringComparison.InvariantCulture))
                            {
                                string type = id.Remove(0, Parser.Metadata.Length);
                                meta_data.Add(new MetaData(type, content[ix + 1]));
                                continue;
                            }
                        }
                    }

                    using (MemoryStream mstream = new MemoryStream(content[ix + 1]))
                    {
                        Spectrum spectrum = CSV.Read(mstream);
                        spectrum.Id = id;

                        if (string.IsNullOrEmpty(spectrum.Label))
                            spectrum.Label = id.Remove(0, 2);

                        spectra.Add(spectrum);
                    }
                }

                return spectra.ToArray();
            }
        }

        public static bool CheckContainerIntegrety(string fname, byte[] salt)
        {
            using (Stream stream = File.OpenRead(fname))
            using (GZipStream zstream = new GZipStream(stream, CompressionMode.Decompress))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(List<byte[]>));
                List<Spectrum> spectra = new List<Spectrum>();
                List<byte[]> content = (List<byte[]>)serializer.Deserialize(zstream);

                for (int ix = 0; ix < content.Count; ix += 2)
                {
                    string id;
                    using (MemoryStream mstream = new MemoryStream(content[ix]))
                    using (StreamReader reader  = new StreamReader(mstream))
                    {
                        id = reader.ReadToEnd();

                        if (id.StartsWith(Parser.Metadata, StringComparison.InvariantCulture))
                            continue;
                    }

                    using (MemoryStream mstream = new MemoryStream(content[ix + 1]))
                    {
                        if (CSV.CheckIntegrity(mstream, salt) == false)
                            return false;
                    }
                }

                return true;
            }
        }

        private static void StreamCopy(Stream src, Stream dest)
        {
            byte[] buffer = new byte[4 * 1024];
            int n = 1;
            while (n > 0)
            {
                n = src.Read(buffer, 0, buffer.Length);
                dest.Write(buffer, 0, n);
            }
        }
    }
}