using SEI2.TestConsole.SEI2ReportProxy;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace SEI2.TestConsole
{
    public static class FactoryContract
    {
        public static object CreateContractOrException(string filePath)
        {
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true;
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                document.Load(stream);
            }

            var result = DeserializeXmlNode(document.DocumentElement);

            if (result == null)
            {
                throw new NullReferenceException($"No contract created for file: {filePath}");
            }

            return result;
        }

        private static object DeserializeXmlNode(XmlNode contractNode)
        {
            object result = null;
            if (UserMgmt.TryDeserialize(contractNode, ref result))
            {
                return result;
            }

            switch (contractNode.LocalName)
            {
                case "SEI2SchemaBedPlacesContract":
                    return DeserializeBody<SEI2SchemaBedPlacesContract>(contractNode);

                case "SEI2SchemaChildrensDatabaseContract":
                    return DeserializeBody<SEI2SchemaChildrensDatabaseContract>(contractNode);

                case "SEI2SchemaCoercion1Contract":
                    return DeserializeBody<SEI2SchemaCoercion1Contract>(contractNode);

                case "SEI2SchemaCoercion2Contract":
                    return DeserializeBody<SEI2SchemaCoercion2Contract>(contractNode);

                case "SEI2SchemaCoercion3Contract":
                    return DeserializeBody<SEI2SchemaCoercion3Contract>(contractNode);

                case "SEI2SchemaCoercion4Contract":
                    return DeserializeBody<SEI2SchemaCoercion4Contract>(contractNode);

                case "SEI2SchemaCoercion5Contract":
                    return DeserializeBody<SEI2SchemaCoercion5Contract>(contractNode);

                case "SEI2SchemaAbortionContract":
                    return DeserializeBody<SEI2SchemaAbortionContract>(contractNode);

                case "SEI2SchemaRitualCircumcisionContract":
                    return DeserializeBody<SEI2SchemaRitualCircumcisionContract>(contractNode);

                case "SEI2SchemaInjHeroinContract":
                    return DeserializeBody<SEI2SchemaInjHeroinContract>(contractNode);

                case "SEI2SchemaReleaseFollowupContract":
                    return DeserializeBody<SEI2SchemaReleaseFollowupContract>(contractNode);

                case "SEI2SchemaMortality1Contract":
                    return DeserializeBody<SEI2SchemaMortality1Contract>(contractNode);

                case "SEI2SchemaMortality2Contract":
                    return DeserializeBody<SEI2SchemaMortality2Contract>(contractNode);

                case "SEI2SchemaCancerContract":
                    return DeserializeBody<SEI2SchemaCancerContract>(contractNode);

                case "SEI2SchemaIVFContract":
                    return DeserializeBody<SEI2SchemaIVFContract>(contractNode);

                case "SEI2SchemaMRSAContract":
                    return DeserializeBody<SEI2SchemaMRSAContract>(contractNode);

                case "SEI2SchemaRehabilitationContract":
                    return DeserializeBody<SEI2SchemaRehabilitationContract>(contractNode);

                case "SEI2SchemaWaitInfoContract":
                    return DeserializeBody<SEI2SchemaWaitInfoContract>(contractNode);

                case "SEI2SchemaCoercionIncapacitatedSomatikContract":
                    return DeserializeBody<SEI2SchemaCoercionIncapacitatedSomatikContract>(contractNode);

                case "SEI2SchemaSSIContract":
                    return DeserializeBody<SEI2SchemaSSIContract>(contractNode);

                case "SEI2SchemaNABContract":
                    return DeserializeBody<SEI2SchemaNABContract>(contractNode);

                case "SEI2SchemaNAB2Contract":
                    return DeserializeBody<SEI2SchemaNAB2Contract>(contractNode);

                case "SEI2SchemaGeneralPracticeContract":
                    return DeserializeBody<SEI2SchemaGeneralPracticeContract>(contractNode);

                case "SEI2SchemaSpecialistPracticeContract":
                    return DeserializeBody<SEI2SchemaSpecialistPracticeContract>(contractNode);

                //Cancel
                case "SEI2SchemaCancelContract":
                    return DeserializeBody<SEI2SchemaCancelContract>(contractNode);
                //UserGetDetails
                case "SEI2UserDetailsRequestContract":
                    return DeserializeBody<SEI2UserDetailsRequestContract>(contractNode);
                //UserCreate
                case "SEI2UserCreateRequestContract":
                    return DeserializeBody<SEI2UserCreateRequestContract>(contractNode);

                //SchemaReportFhirBundle
                case "SEI2BundleRequestContract":
                    return DeserializeBody<SEI2BundleRequestContract>(contractNode);

                default:
                    throw new ApplicationException(contractNode.LocalName + " is not supported by this client.");
            }
        }

        private static T DeserializeBody<T>(XmlNode bodyNode) where T : class
        {
            T contract;
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            using (XmlNodeReader reader = new System.Xml.XmlNodeReader(bodyNode))
            {
                contract = serializer.ReadObject(reader) as T;
            }
            if (contract == null)
                Console.WriteLine("Could not deserialize as " + typeof(T).ToString());
            return contract;
        }

        private static T String2Object<T>(string value) where T : class
        {
            if (value == null)
                return default(T);
            using (MemoryStream stream = new MemoryStream())
            using (StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
            {
                writer.Write(value);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                return serializer.ReadObject(stream) as T;
            }
        }
    }
}
