/*
可遵循 aardio 用户协议与 aardio 开源许可证在 aardio 程序中自由使用本组件以及本组件源码,
禁止在非 aardio 开发的程序中引用本组件的任何部份(包含但不限于本组件源码、使用此源码生成的 DLL )
*/
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Web.Services.Description;
using Microsoft.CSharp;
using System.Net;

namespace Aardio.InteropServices
{
	[ClassInterface(ClassInterfaceType.AutoDispatch), ComVisible(true)]
	public class CCodeCompiler
	{
		private StringBuilder lastErrorText;

		private CompilerParameters parameters;

		private CodeDomProvider provider;

		private object source;

		public string LastError
		{
			get
			{
				return this.lastErrorText.ToString();
			}
		}

		public CompilerParameters Parameters
		{
			get
			{
				return this.parameters;
			}
			set
			{
				this.parameters = value;
			}
		}

		public CodeDomProvider Provider
		{
			get
			{
				return this.provider;
			}
			set
			{
				this.provider = value;
			}
		}

		public object Source
		{
			get
			{
				return this.source;
			}
			set
			{
				this.source = value;
			}
		} 

        public CCodeCompiler(CodeDomProvider p)
		{
			this.provider = p;
			this.parameters = new CompilerParameters();
			this.parameters.ReferencedAssemblies.Add("System.dll");
			this.parameters.GenerateExecutable = false;
			this.parameters.GenerateInMemory = true;
			this.lastErrorText = new StringBuilder();
			this.source = null;
		}

		public Assembly Compile()
		{
			this.lastErrorText = new StringBuilder();

			string[] source;
			if (this.Source is string) source = new string[] { this.Source as string };
			else source = this.Source as string[];
			 
			if (source == null)
			{
				this.lastErrorText.Append("Property 'Source' cannot be null!");
				return null;
			}

            CompilerResults compilerResults = this.provider.CompileAssemblyFromSource(this.parameters, source);
			if (!compilerResults.Errors.HasErrors)
			{
				return compilerResults.CompiledAssembly;
			}
			foreach (CompilerError compilerError in compilerResults.Errors)
			{
				this.lastErrorText.Append(string.Format("ErrorNumber:{0} Line:{1} {2}", compilerError.ErrorNumber, compilerError.Line, compilerError.ErrorText));
				this.lastErrorText.Append("\r\n");
			}
			return null;
		}

		public Assembly CompileWebService(string url, string ns, string protocolName)
		{
			this.lastErrorText = new StringBuilder();
			if (url == null)
			{
				this.lastErrorText.Append("未指定服务地址!");
				return null;
			}

			WebClient http = new WebClient();
			Stream stream = http.OpenRead(url + "?WSDL");
			ServiceDescription serviceDesc = ServiceDescription.Read(stream);
			ServiceDescriptionImporter sdImporter = new ServiceDescriptionImporter();
			sdImporter.ProtocolName = protocolName;
			sdImporter.AddServiceDescription(serviceDesc, "", "");

			foreach (Import schemaImport in serviceDesc.Imports)
			{
				Uri baseUri = new Uri(url + "?WSDL");
				string schemaLocation = schemaImport.Location;
				if (schemaLocation == null)
					continue;
				Uri schemaUri = new Uri(baseUri, schemaLocation);

				using (Stream schemaStream = http.OpenRead(schemaUri))
				{
					try
					{
						ServiceDescription sdImport = ServiceDescription.Read(schemaStream, true);
						sdImport.Namespaces.Add("wsdl", schemaImport.Namespace);
						sdImporter.AddServiceDescription(sdImport, null, null);
					}
					catch { }
				}
			}
		
			foreach (System.Xml.Schema.XmlSchema wsdlSchema in serviceDesc.Types.Schemas)
			{
				foreach (System.Xml.Schema.XmlSchemaObject externalSchema in wsdlSchema.Includes)
				{
					if (externalSchema is System.Xml.Schema.XmlSchemaImport)
					{
						Uri baseUri = new Uri(url + "?WSDL");
						string exSchemaLocation = ((System.Xml.Schema.XmlSchemaExternal)externalSchema).SchemaLocation;
						if (string.IsNullOrEmpty(exSchemaLocation))
							continue;

						Uri schemaUri = new Uri(baseUri, exSchemaLocation);

						using (Stream schemaStream = http.OpenRead(schemaUri))
						{
							try
							{
								System.Xml.Schema.XmlSchema schema = System.Xml.Schema.XmlSchema.Read(schemaStream, null);
								sdImporter.Schemas.Add(schema);
							}
							catch { }                                                      
						}
					}
				}
			}


			System.CodeDom.CodeNamespace codeNamespace = new System.CodeDom.CodeNamespace(ns);
			System.CodeDom.CodeCompileUnit codeCompileUnit = new System.CodeDom.CodeCompileUnit();
			codeCompileUnit.Namespaces.Add(codeNamespace);
			sdImporter.Import(codeNamespace, codeCompileUnit);

			this.parameters.ReferencedAssemblies.Add("System.XML.dll");
			this.parameters.ReferencedAssemblies.Add("System.Web.Services.dll");
			this.parameters.ReferencedAssemblies.Add("System.Data.dll");
			CompilerResults compilerResults = this.provider.CompileAssemblyFromDom(this.parameters, codeCompileUnit);

			if (true == compilerResults.Errors.HasErrors)
			{
				foreach (CompilerError compilerError in compilerResults.Errors)
				{
					this.lastErrorText.Append(string.Format("ErrorNumber:{0} Line:{1} {2}", compilerError.ErrorNumber, compilerError.Line, compilerError.ErrorText));
					this.lastErrorText.Append("\r\n");
				}
				return null;
			}

			return compilerResults.CompiledAssembly; 
				
		}

		public void Reference(string assemblyName)
		{
			this.parameters.ReferencedAssemblies.Add(assemblyName);
		}
	}
}
