/// <summary>
        /// Writes the data of the plot into the specified <paramref name="stream"/>.
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        internal static void WriteTo(this Formplot plot, Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            using (var zipOutput = new ZipArchive(stream, ZipArchiveMode.Create))
            {
                var versionInfoEntry = zipOutput.CreateEntry("fileversion.txt", CompressionLevel.NoCompression);
                versionInfoEntry.LastWriteTime = new DateTime(1980, 1, 1);

                using (var textWriter = new StreamWriter(versionInfoEntry.Open(), Encoding.UTF8))
                {
                    textWriter.Write(FormplotHelpers.GetFileFormatVersion(plot.FormplotType).ToString(2));
                }

                using (var metadataStream = new MemoryStream())
                {
                    var settings = new XmlWriterSettings
                    {
                        Indent      = true,
                        Encoding    = Encoding.UTF8,
                        CloseOutput = false
                    };

                    using (var writer = XmlWriter.Create(metadataStream, settings))
                    {
                        Stream pointDataStream;

                        if (plot.FormplotType != FormplotTypes.None)
                        {
                            var plotPointsEntry = zipOutput.CreateEntry("plotpoints.dat", CompressionLevel.Fastest);
                            plotPointsEntry.LastWriteTime = new DateTime(1980, 1, 1);
                            pointDataStream = plotPointsEntry.Open();
                        }
                        else
                        {
                            pointDataStream = null;
                        }

                        writer.WriteStartDocument(true);
                        writer.WriteStartElement(plot.FormplotType == FormplotTypes.None ? FormplotHelpers.PropertiesRootTag : FormplotHelpers.FormplotRootTag);

                        plot.Serialize(writer, pointDataStream);

                        writer.WriteEndElement();
                        writer.WriteEndDocument();

                        pointDataStream?.Close();
                    }

                    var metaDataEntry = zipOutput.CreateEntry("header.xml", CompressionLevel.Optimal);
                    metaDataEntry.LastWriteTime = new DateTime(1980, 1, 1);

                    using (var metaDataZipStream = metaDataEntry.Open())
                        metadataStream.WriteTo(metaDataZipStream);
                }
            }
        }
        /// <summary>
        /// Writes the points into the specified <see cref="Stream" /> and writes their metadata with the specified <see cref="XmlWriter" />.
        /// </summary>
        /// <param name="plot">The plot.</param>
        /// <param name="writer">The xml writer.</param>
        /// <param name="pointdataStream">The point data stream.</param>
        private static void WritePoints(this Formplot plot, XmlWriter writer, Stream pointdataStream)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            if (pointdataStream == null)
            {
                throw new ArgumentNullException(nameof(pointdataStream));
            }

            writer.WriteStartElement("Count");
            writer.WriteValue(XmlConvert.ToString(plot.Points.Count()));
            writer.WriteEndElement();

            var propertyLists  = new Dictionary <Property, RangeList>();
            var statelists     = new Dictionary <PointState, RangeList>();
            var segmentlists   = new Dictionary <Segment, RangeList>();
            var tolerancelists = new Dictionary <Tolerance, RangeList>();

            var lastPoint = default(Point);
            var index     = 0;

            foreach (var point in plot.Points)
            {
                CollectMetaData(point, propertyLists, index, lastPoint);
                CollectStates(point, statelists, index, lastPoint);
                CollectSegments(point, segmentlists, index, lastPoint);
                CollectTolerances(point, tolerancelists, index, lastPoint);
                point.WriteToStream(pointdataStream);

                lastPoint = point;
                index++;
            }

            WriteMetaData(writer, propertyLists);
            WriteStates(writer, statelists);
            WriteSegments(writer, segmentlists);
            WriteTolerances(writer, tolerancelists);
        }
        /// <summary>
        /// Serializes the formplot. The header information is written to the <paramref name="metaDataWriter" />, whereas the points are stored as a
        /// blob in the <paramref name="pointDataStream" /></summary>
        /// <param name="plot">The plot.</param>
        /// <param name="metaDataWriter">An XML writer to store the header data, such as tolerances and segments.</param>
        /// <param name="pointDataStream">A stream to store the binary point data.</param>
        private static void Serialize(this Formplot plot, XmlWriter metaDataWriter, Stream pointDataStream)
        {
            if (metaDataWriter == null)
            {
                throw new ArgumentNullException(nameof(metaDataWriter));
            }

            if (plot.FormplotType != FormplotTypes.None)
            {
                metaDataWriter.WriteAttributeString("Type", plot.FormplotType.ToString());
            }

            metaDataWriter.WriteStartElement("CreatorSoftware");
            metaDataWriter.WriteValue(plot.CreatorSoftware);
            metaDataWriter.WriteEndElement();

            metaDataWriter.WriteStartElement("CreatorSoftwareVersion");
            metaDataWriter.WriteValue(plot.CreatorSoftwareVersion.ToString());
            metaDataWriter.WriteEndElement();

            if (plot.Properties.Count > 0)
            {
                foreach (var property in plot.Properties)
                {
                    metaDataWriter.WriteStartElement("Property");
                    property.Serialize(metaDataWriter);
                    metaDataWriter.WriteEndElement();
                }
            }

            if (plot.FormplotType == FormplotTypes.None)
            {
                return;
            }

            if (!plot.Tolerance.IsEmpty)
            {
                metaDataWriter.WriteStartElement("Tolerance");
                plot.Tolerance.Serialize(metaDataWriter);
                metaDataWriter.WriteEndElement();
            }

            if (plot.DefaultErrorScaling.HasValue)
            {
                metaDataWriter.WriteStartElement("ErrorScaling");
                metaDataWriter.WriteValue(XmlConvert.ToString(plot.DefaultErrorScaling.Value));
                metaDataWriter.WriteEndElement();
            }

            if (plot.FormplotType == FormplotTypes.Straightness && plot.ProjectionAxis != ProjectionAxis.None)
            {
                metaDataWriter.WriteStartElement("ProjectionAxis");
                metaDataWriter.WriteValue(plot.ProjectionAxis.ToString());
                metaDataWriter.WriteEndElement();
            }

            if (plot.GeometryType != GeometryTypes.None)
            {
                metaDataWriter.WriteStartElement("Geometry");

                {
                    metaDataWriter.WriteAttributeString("Type", plot.GeometryType.ToString());

                    metaDataWriter.WriteStartElement("Nominal");
                    plot.Nominal.Serialize(metaDataWriter);
                    metaDataWriter.WriteEndElement();

                    metaDataWriter.WriteStartElement("Actual");
                    plot.Actual.Serialize(metaDataWriter);
                    metaDataWriter.WriteEndElement();
                }

                metaDataWriter.WriteEndElement();
            }

            metaDataWriter.WriteStartElement("Points");
            plot.WritePoints(metaDataWriter, pointDataStream);
            metaDataWriter.WriteEndElement();
        }