

#region Used Namespaces ---------------------------------------------------------------------------

using System;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections.Generic;
using System.Xml;
using System.Text;

#endregion Used Namespaces ------------------------------------------------------------------------

namespace syngo.Services.PM.MRSimulatorGUI
{
    #region Public Types --------------------------------------------------------------------------

    /// <summary>
    /// Serialization format types.
    /// </summary>
    public enum SerializedFormats
    {
        /// <summary>
        /// Binary serialization format.
        /// </summary>
        Binary,

        /// <summary>
        /// Document serialization format.
        /// </summary>
        Document
    }

    #endregion Public Types -----------------------------------------------------------------------

    #region Internal Types ------------------------------------------------------------------------
    #endregion Internal Types ---------------------------------------------------------------------

    /// <summary>
    /// Facade to XML serialization and deserialization of strongly typed objects to/from an XML file. 
    /// Specify that T must be a class.
    /// </summary>
    public static class ObjectXMLSerializer<T> where T : class
    {
        #region Public Constructors ---------------------------------------------------------------
        #endregion Public Constructors ------------------------------------------------------------

        #region Destructor ------------------------------------------------------------------------
        #endregion Destructor ---------------------------------------------------------------------

        #region Public section --------------------------------------------------------------------

        #region Load methods

        /// <summary>
        /// Loads an object from an XML file in Document format.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, @"C:\XMLObjects.xml");
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="path">Path of the file to load the object from.</param>
        /// <returns>Object loaded from an XML file in Document format.</returns>
        public static T Load(T serializableObject, string path)
        {
            serializableObject = LoadFromDocumentFormat(serializableObject, null, path, null);
            return serializableObject;
        }

        /// <summary>
        /// Loads an object from an XML file using a specified serialized format.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, @"C:\XMLObjects.xml", SerializedFormats.Binary);
        /// </code>
        /// </example>		
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="path">Path of the file to load the object from.</param>
        /// <param name="serializedFormat">XML serialized format used to load the object.</param>
        /// <returns>Object loaded from an XML file using the specified serialized format.</returns>
        public static T Load(T serializableObject, string path, SerializedFormats serializedFormat)
        {
            switch (serializedFormat)
            {
                case SerializedFormats.Binary:
                    serializableObject = LoadFromBinaryFormat(serializableObject, path, null);
                    break;

                case SerializedFormats.Document:
                default:
                    serializableObject = LoadFromDocumentFormat(serializableObject, null, path, null);
                    break;
            }

            return serializableObject;
        }

        /// <summary>
        /// Loads an object from an XML file in Document format, supplying extra data types to enable deserialization of custom types within the object.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, @"C:\XMLObjects.xml", new Type[] { typeof(MyCustomType) });
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="path">Path of the file to load the object from.</param>
        /// <param name="extraTypes">Extra data types to enable deserialization of custom types within the object.</param>
        /// <returns>Object loaded from an XML file in Document format.</returns>
        public static T Load(T serializableObject, string path, System.Type[] extraTypes)
        {
            serializableObject = LoadFromDocumentFormat(serializableObject, extraTypes, path, null);
            return serializableObject;
        }

        /// <summary>
        /// Loads an object from an XML file in Document format, located in a specified isolated storage area.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly());
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to load the object from.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to load the object from.</param>
        /// <returns>Object loaded from an XML file in Document format located in a specified isolated storage area.</returns>
        public static T Load(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory)
        {
            serializableObject = LoadFromDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
            return serializableObject;
        }

        /// <summary>
        /// Loads an object from an XML file located in a specified isolated storage area, using a specified serialized format.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly(), SerializedFormats.Binary);
        /// </code>
        /// </example>		
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to load the object from.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to load the object from.</param>
        /// <param name="serializedFormat">XML serialized format used to load the object.</param>        
        /// <returns>Object loaded from an XML file located in a specified isolated storage area, using a specified serialized format.</returns>
        public static T Load(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory, SerializedFormats serializedFormat)
        {
            switch (serializedFormat)
            {
                case SerializedFormats.Binary:
                    serializableObject = LoadFromBinaryFormat(serializableObject, fileName, isolatedStorageDirectory);
                    break;

                case SerializedFormats.Document:
                default:
                    serializableObject = LoadFromDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
                    break;
            }

            return serializableObject;
        }

        /// <summary>
        /// Loads an object from an XML file in Document format, located in a specified isolated storage area, and supplying extra data types to enable deserialization of custom types within the object.
        /// </summary>
        /// <example>
        /// <code>
        /// // Always create a new object prior to passing to ObjectXMLSerializer.Load method.
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// serializableObject = ObjectXMLSerializer&lt;SerializableObject&gt;.Load(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly(), new Type[] { typeof(MyCustomType) });
        /// </code>
        /// </example>		
        /// <param name="serializableObject">Serializable object to be loaded from file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to load the object from.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to load the object from.</param>
        /// <param name="extraTypes">Extra data types to enable deserialization of custom types within the object.</param>
        /// <returns>Object loaded from an XML file located in a specified isolated storage area, using a specified serialized format.</returns>
        public static T Load(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory, System.Type[] extraTypes)
        {
            serializableObject = LoadFromDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
            return serializableObject;
        }

        #endregion

        #region Save methods

        /// <summary>
        /// Saves an object to an XML file in Document format.
        /// </summary>
        /// <example>
        /// <code>        
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, @"C:\XMLObjects.xml");
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="path">Path of the file to save the object to.</param>
        public static void Save(T serializableObject, string path)
        {
            SaveToDocumentFormat(serializableObject, null, path, null);
        }

        /// <summary>
        /// Saves an object to an XML file using a specified serialized format.
        /// </summary>
        /// <example>
        /// <code>
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, @"C:\XMLObjects.xml", SerializedFormats.Binary);
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="path">Path of the file to save the object to.</param>
        /// <param name="serializedFormat">XML serialized format used to save the object.</param>
        public static void Save(T serializableObject, string path, SerializedFormats serializedFormat)
        {
            switch (serializedFormat)
            {
                case SerializedFormats.Binary:
                    SaveToBinaryFormat(serializableObject, path, null);
                    break;

                case SerializedFormats.Document:
                default:
                    SaveToDocumentFormat(serializableObject, null, path, null);
                    break;
            }
        }

        /// <summary>
        /// Saves an object to an XML file in Document format, supplying extra data types to enable serialization of custom types within the object.
        /// </summary>
        /// <example>
        /// <code>        
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, @"C:\XMLObjects.xml", new Type[] { typeof(MyCustomType) });
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="path">Path of the file to save the object to.</param>
        /// <param name="extraTypes">Extra data types to enable serialization of custom types within the object.</param>
        public static void Save(T serializableObject, string path, System.Type[] extraTypes)
        {
            SaveToDocumentFormat(serializableObject, extraTypes, path, null);
        }

        /// <summary>
        /// Saves an object to an XML file in Document format, located in a specified isolated storage area.
        /// </summary>
        /// <example>
        /// <code>        
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly());
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to save the object to.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to save the object to.</param>
        public static void Save(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory)
        {
            SaveToDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
        }

        /// <summary>
        /// Saves an object to an XML file located in a specified isolated storage area, using a specified serialized format.
        /// </summary>
        /// <example>
        /// <code>        
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly(), SerializedFormats.Binary);
        /// </code>
        /// </example>
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to save the object to.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to save the object to.</param>
        /// <param name="serializedFormat">XML serialized format used to save the object.</param>        
        public static void Save(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory, SerializedFormats serializedFormat)
        {
            switch (serializedFormat)
            {
                case SerializedFormats.Binary:
                    SaveToBinaryFormat(serializableObject, fileName, isolatedStorageDirectory);
                    break;

                case SerializedFormats.Document:
                default:
                    SaveToDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
                    break;
            }
        }

        /// <summary>
        /// Saves an object to an XML file in Document format, located in a specified isolated storage area, and supplying extra data types to enable serialization of custom types within the object.
        /// </summary>
        /// <example>
        /// <code>
        /// SerializableObject serializableObject = new SerializableObject();
        /// 
        /// ObjectXMLSerializer&lt;SerializableObject&gt;.Save(serializableObject, "XMLObjects.xml", IsolatedStorageFile.GetUserStoreForAssembly(), new Type[] { typeof(MyCustomType) });
        /// </code>
        /// </example>		
        /// <param name="serializableObject">Serializable object to be saved to file.</param>
        /// <param name="fileName">Name of the file in the isolated storage area to save the object to.</param>
        /// <param name="isolatedStorageDirectory">Isolated storage area directory containing the XML file to save the object to.</param>
        /// <param name="extraTypes">Extra data types to enable serialization of custom types within the object.</param>
        public static void Save(T serializableObject, string fileName, IsolatedStorageFile isolatedStorageDirectory, System.Type[] extraTypes)
        {
            SaveToDocumentFormat(serializableObject, null, fileName, isolatedStorageDirectory);
        }

        //save it to xml string
        public static String Save(T serializableObject)
        {
            String XmlizedString = null;
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xs = CreateXmlSerializer(serializableObject, null);

            XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
            xs.Serialize(xmlTextWriter, serializableObject);
            memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
            XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
            return XmlizedString;
        }

        #endregion

        #endregion Public section -----------------------------------------------------------------

        #region Protected section -----------------------------------------------------------------
        #endregion Protected section --------------------------------------------------------------

        #region Private section -------------------------------------------------------------------

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isolatedStorageFolder"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        private static FileStream CreateFileStream(IsolatedStorageFile isolatedStorageFolder, string path)
        {
            FileStream fileStream = null;

            if (isolatedStorageFolder == null)
                fileStream = new FileStream(path, FileMode.OpenOrCreate);
            else
                fileStream = new IsolatedStorageFileStream(path, FileMode.OpenOrCreate, isolatedStorageFolder);

            return fileStream;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializableObject"></param>
        /// <param name="path"></param>
        /// <param name="isolatedStorageFolder"></param>
        /// <returns></returns>
        private static T LoadFromBinaryFormat(T serializableObject, string path, IsolatedStorageFile isolatedStorageFolder)
        {
            using (FileStream fileStream = CreateFileStream(isolatedStorageFolder, path))
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                serializableObject = binaryFormatter.Deserialize(fileStream) as T;
            }

            return serializableObject;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializableObject"></param>
        /// <param name="extraTypes"></param>
        /// <param name="path"></param>
        /// <param name="isolatedStorageFolder"></param>
        /// <returns></returns>
        private static T LoadFromDocumentFormat(T serializableObject, System.Type[] extraTypes, string path, IsolatedStorageFile isolatedStorageFolder)
        {
            using (TextReader textReader = CreateTextReader(isolatedStorageFolder, path))
            {
                XmlSerializer xmlSerializer = CreateXmlSerializer(serializableObject, extraTypes);
                serializableObject = xmlSerializer.Deserialize(textReader) as T;

            }

            return serializableObject;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isolatedStorageFolder"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        private static TextReader CreateTextReader(IsolatedStorageFile isolatedStorageFolder, string path)
        {
            TextReader textReader = null;

            if (isolatedStorageFolder == null)
                textReader = new StreamReader(path);
            else
                textReader = new StreamReader(new IsolatedStorageFileStream(path, FileMode.Open, isolatedStorageFolder));

            return textReader;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isolatedStorageFolder"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        private static TextWriter CreateTextWriter(IsolatedStorageFile isolatedStorageFolder, string path)
        {
            TextWriter textWriter = null;

            if (isolatedStorageFolder == null)
                textWriter = new StreamWriter(path);
            else
                textWriter = new StreamWriter(new IsolatedStorageFileStream(path, FileMode.OpenOrCreate, isolatedStorageFolder));

            return textWriter;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializableObject"></param>
        /// <param name="extraTypes"></param>
        /// <returns></returns>
        private static XmlSerializer CreateXmlSerializer(T serializableObject, System.Type[] extraTypes)
        {
            Type ObjectType = serializableObject.GetType();

            XmlSerializer xmlSerializer = null;

            if (extraTypes != null)
                xmlSerializer = new XmlSerializer(ObjectType, extraTypes);
            else
                xmlSerializer = new XmlSerializer(ObjectType);

            return xmlSerializer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializableObject"></param>
        /// <param name="extraTypes"></param>
        /// <param name="path"></param>
        /// <param name="isolatedStorageFolder"></param>
        private static void SaveToDocumentFormat(T serializableObject, System.Type[] extraTypes, string path, IsolatedStorageFile isolatedStorageFolder)
        {
            using (TextWriter textWriter = CreateTextWriter(isolatedStorageFolder, path))
            {
                XmlSerializer xmlSerializer = CreateXmlSerializer(serializableObject, extraTypes);
                xmlSerializer.Serialize(textWriter, serializableObject);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serializableObject"></param>
        /// <param name="path"></param>
        /// <param name="isolatedStorageFolder"></param>
        private static void SaveToBinaryFormat(T serializableObject, string path, IsolatedStorageFile isolatedStorageFolder)
        {
            using (FileStream fileStream = CreateFileStream(isolatedStorageFolder, path))
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(fileStream, serializableObject);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="characters"></param>
        /// <returns></returns>
        private static String UTF8ByteArrayToString(Byte[] characters)
        {

            UTF8Encoding encoding = new UTF8Encoding();
            String constructedString = encoding.GetString(characters);
            return (constructedString);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pXmlString"></param>
        /// <returns></returns>
        private static Byte[] StringToUTF8ByteArray(String pXmlString)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            Byte[] byteArray = encoding.GetBytes(pXmlString);
            return byteArray;
        }

        #endregion Private section ----------------------------------------------------------------
    }
}