using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Description;
using System.Text;
using System.Xml.Schema;
using Gmc.Fido.WebServices.Api.SurveyDataService;
using NUnit.Framework;
using ServiceDescription = System.Web.Services.Description.ServiceDescription;

namespace Gmc.Fido.WebServices.Api.Test.SurveyDataService
{
	[TestFixture]
	public class SurveyDataServiceTest
	{
		[Test]
		public void ExportsValidWsdl()
		{
			var metadataSet = GetMetadataOfService(typeof(ISurveyDataService));
			var filteredSet = new MetadataSetFilter().GetUsedMetadata(metadataSet);

			var contracts = ImportContractsFromMetadata(filteredSet);

			GenerateCodeFromContracts(contracts);
		}

		static MetadataSet GetMetadataOfService(Type contractType)
		{
			var exporter = new WsdlExporter();
			exporter.ExportContract(ContractDescription.GetContract(contractType));
			AssertNoConversionErrors(exporter.Errors, "Exporting metadata from contract");
			return exporter.GetGeneratedMetadata();
		}

		static IEnumerable<ContractDescription> ImportContractsFromMetadata(MetadataSet filteredSet)
		{
			var importer = new WsdlImporter(filteredSet);
			var contracts = importer.ImportAllContracts();
			AssertNoConversionErrors(importer.Errors, "Importing contracts from metadata");
			return contracts;
		}

		static string GenerateCodeFromContracts(IEnumerable<ContractDescription> contracts)
		{
			var generator = new ServiceContractGenerator();
			foreach (ContractDescription contract in contracts)
			{
				generator.GenerateServiceContractType(contract);
			}
			AssertNoConversionErrors(generator.Errors, "Generating code from contracts");

			var options = new System.CodeDom.Compiler.CodeGeneratorOptions();
			options.BracingStyle = "C";
			var codeDomProvider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("C#");
			var builder = new StringBuilder();
			var textWriter = new System.CodeDom.Compiler.IndentedTextWriter(new System.IO.StringWriter(builder));
			codeDomProvider.GenerateCodeFromCompileUnit(generator.TargetCompileUnit, textWriter, options);
			textWriter.Close();

			return builder.ToString();
		}

		class MetadataSetFilter
		{
			MetadataSet _original;
			MetadataSet _newMetadata;

			public MetadataSet GetUsedMetadata(MetadataSet original)
			{
				_original = original;
				_newMetadata = new MetadataSet();
				var mainWsdlSection = FindMainWsdlSection();
				AddMetadataSection(mainWsdlSection);
				return _newMetadata;
			}

			MetadataSection FindMainWsdlSection()
			{
				return _original.MetadataSections.First(s => s.Metadata is ServiceDescription);
			}

			void AddMetadataSection(MetadataSection section)
			{
				_newMetadata.MetadataSections.Add(section);
				if (section.Metadata is ServiceDescription)
					AddMetadata((ServiceDescription)section.Metadata);
				else if (section.Metadata is XmlSchema)
					AddMetadata((XmlSchema)section.Metadata);
			}

			void AddMetadata(ServiceDescription wsdl)
			{
				foreach (XmlSchema schema in wsdl.Types.Schemas)
				{
					AddMetadata(schema);
				}
			}

			void AddMetadata(XmlSchema schema)
			{
				foreach (XmlSchemaImport import in schema.Includes)
				{
					var section = FindSection(import.Namespace);
					AddMetadataSection(section);
				}
			}

			MetadataSection FindSection(string xsdNamespace)
			{
				return _original.MetadataSections.First(s =>
					{
						var xsd = s.Metadata as XmlSchema;
						return xsd != null && xsd.TargetNamespace == xsdNamespace;
					});
			}
		}
		
		static void AssertNoConversionErrors(IEnumerable<MetadataConversionError> errors, string phase)
		{
			Assert.That(errors.Select(e=>e.Message).ToArray(), Is.Empty, phase);
		}
	}
}