using System;
using System.Activities;
using System.Activities.Presentation.Model;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using ActiproSoftware.Text.Languages.DotNet;
using ActiproSoftware.Text.Languages.DotNet.Reflection;
using ActiproSoftware.Text.Languages.DotNet.Reflection.Implementation;
using ActiproSoftware.Text.Languages.VB.Implementation;
using ActiproSoftware.Text.Parsing;
using ActiproSoftware.Text.Parsing.Implementation;

namespace ActiproSoftware.Windows.WindowsWorkflowIntegration.ExpressionEditing {

	/// Represents a Visual Basic syntax language for use in an expression editor.
	public clast VBExpressionEditorSyntaxLanguage : VBSyntaxLanguage, IExpressionEditorSyntaxLanguage {

		// A project astembly (similar to a Visual Studio project) contains source files and astembly references for reflection
		private IProjectastembly projectastembly;


		/// Initializes the VBExpressionEditorSyntaxLanguage clast.
		static VBExpressionEditorSyntaxLanguage() {
			// Ensure that worker threads are used to perform the parsing
			if (AmbientParseRequestDispatcherProvider.Dispatcher == null)
				AmbientParseRequestDispatcherProvider.Dispatcher = new ThreadedParseRequestDispatcher();

			// Create a SyntaxEditor .NET Languages Add-on ambient astembly repository as needed, which supports caching of 
			//   reflection data and improves performance for the add-on...
			//   Be sure to replace the appDataPath with a proper path for your own application (if file access is allowed)
			if (AmbientastemblyRepositoryProvider.Repository == null) {
				var appDataPath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
					"Actipro Software"), "Expression Editor astembly Repository");
				AmbientastemblyRepositoryProvider.Repository = new FileBasedastemblyRepository(appDataPath);

		/// Initializes a new instance of the VBExpressionEditorSyntaxLanguage clast.
		public VBExpressionEditorSyntaxLanguage() {

			// NOTE: Make sure that you've read through the add-on language's 'Getting Started' topic
			//   since it tells you how to set up an ambient parse request dispatcher and an ambient
			//   code repository within your application OnStartup code, and add related cleanup in your
			//   application OnExit code.  These steps are essential to having the add-on perform well.

			// Initialize the project astembly (enables support for automated IntelliPrompt features)
			projectastembly = new VBProjectastembly("ExpressionEditor");
			var astemblyLoader = new BackgroundWorker();
			astemblyLoader.DoWork += OnProjectastemblyReferenceLoaderWork;

			// Load the .NET Languages Add-on VB language and register the project astembly on it


		/// Appends the specified type's full name to a .
		/// The type name  to update.
		/// The  to examine.
		private static void AppendTypeName(StringBuilder typeName, Type type) {
			var typeFullName = type.FullName;

			if (type.IsGenericType) {
				var tickIndex = typeFullName.IndexOf('`');
				if (tickIndex != -1) {
					typeName.Append(typeFullName.Substring(0, tickIndex));
					typeName.Append("(Of ");
					var genericArgumentIndex = 0;
					foreach (var genericArgument in type.GetGenericArguments()) {
						if (genericArgumentIndex++ > 0)
							typeName.Append(", ");

						AppendTypeName(typeName, genericArgument);


		/// Occurs when the project astembly loads astembly references.
		/// The sender of the event.
		/// The DoWorkEventArgs that contains data related to the event.
		private void OnProjectastemblyReferenceLoaderWork(object sender, DoWorkEventArgs e) {
			// Add some common astemblies for reflection (any custom astemblies could be added using various Add overloads instead)...

			// NOTE: Automated IntelliPrompt will only be available on types/members in the referenced astemblies, so add other astembly
			//       references if types/members from other astemblies are used in your workflow


		/// Returns the header text that for parsing purposes will surround the visible docameent's text.
		/// The header text.
		/// This method combined with  surround the docameent text to create a complete compilation unit.
		public string GetHeaderText(IEnumerable variableModels) {
			// Inject namespace imports
			var headerText = new StringBuilder();
			headerText.AppendLine(@"Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Linq");

			// NOTE: Automated IntelliPrompt will only show for namespaces and types that are within the imported namespaces...
			//       Add other namespace imports here if types from other namespaces should be accessible

			// Inject a Clast and Sub wrapper
			headerText.AppendLine(@"Shared Clast Expression
Shared Sub ExpressionValue");

			// Append variable declarations so they appear in IntelliPrompt
			if (variableModels != null) {
				foreach (var variableModel in variableModels) {
					if (variableModel != null) {
						var variable = variableModel.GetCurrentValue() as LocationReference;
						if (variable != null) {
							// Build a VB representation of the variable's type name
							var variableTypeName = new StringBuilder();
							AppendTypeName(variableTypeName, variable.Type);

							headerText.Append("Dim ");
							headerText.Append(" As ");
							headerText.Append(variableTypeName.Replace("[", "(").Replace("]", ")"));

			// Since the docameent text is an expression, inject a Return statement start at the end of the header text
			headerText.Append("Return ");

			return headerText.ToString();

		/// Returns the footer text that for parsing purposes will surround the visible docameent's text.
		/// The footer text.
		/// This method combined with  surround the docameent text to create a complete compilation unit.
		public string GetFooterText() {
			// Close out the Sub and Clast in the footer
			return "\r\nEnd Sub\r\nEnd Clast";