//
// GuiBuilderWindow.cs
//
// Author:
//   Lluis Sanchez Gual
//
// Copyright (C) 2006 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//


using System;
using System.Collections;
using System.CodeDom;

using MonoDevelop.Core;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Projects;
using MonoDevelop.Projects.Text;
using MonoDevelop.GtkCore.Dialogs;
using MonoDevelop.Ide;
using ICSharpCode.NRefactory.TypeSystem;

namespace MonoDevelop.GtkCore.GuiBuilder
{
	public class GuiBuilderWindow: IDisposable
	{
		Stetic.WidgetInfo rootWidget;
		GuiBuilderProject fproject;
		Stetic.Project gproject;
		string name;
		
		public event WindowEventHandler Changed;
		
		internal GuiBuilderWindow (GuiBuilderProject fproject, Stetic.Project gproject, Stetic.WidgetInfo rootWidget)
		{
			this.fproject = fproject;
			this.rootWidget = rootWidget;
			this.gproject = gproject;
			name = rootWidget.Name;
			gproject.ProjectReloaded += OnProjectReloaded;
			rootWidget.Changed += OnChanged;
		}
		
		public Stetic.WidgetInfo RootWidget {
			get { return rootWidget; }
		}
		
		public GuiBuilderProject Project {
			get { return fproject; }
		}
		
		public string Name {
			get { return rootWidget.Name; }
		}
		
		public FilePath SourceCodeFile {
			get { return fproject.GetSourceCodeFile (rootWidget); }
		}
		
		public void Dispose ()
		{
			gproject.ProjectReloaded -= OnProjectReloaded;
			rootWidget.Changed -= OnChanged;
		}
		
		void OnProjectReloaded (object s, EventArgs args)
		{
			rootWidget.Changed -= OnChanged;
			rootWidget = gproject.GetWidget (name);
			if (rootWidget != null)
				rootWidget.Changed += OnChanged;
		}
		
		void OnChanged (object o, EventArgs args)
		{
			// Update the name, it may have changed
			name = rootWidget.Name;
			
			if (Changed != null)
				Changed (this, new WindowEventArgs (this));
		}
		
		public bool BindToClass ()
		{
			if (SourceCodeFile != FilePath.Null)
				return true;
			
			// Find the classes that could be bound to this design
			var ctx = fproject.GetParserContext ();
			ArrayList list = new ArrayList ();
			foreach (var cls in ctx.MainAssembly.GetAllTypeDefinitions ()) {
				if (IsValidClass (cls))
					list.Add (cls.FullName);
			}
		
			// Ask what to do

			try {
				using (BindDesignDialog dialog = new BindDesignDialog (Name, list, Project.Project.BaseDirectory)) {
					if (!dialog.Run ())
						return false;
					
					if (dialog.CreateNew)
						CreateClass (dialog.ClassName, dialog.Namespace, dialog.Folder);

					string fullName = dialog.Namespace.Length > 0 ? dialog.Namespace + "." + dialog.ClassName : dialog.ClassName;
					rootWidget.Name = fullName;
					fproject.SaveWindow (true, fullName);
				}
				return true;
			} catch (Exception ex) {
				MessageService.ShowException (ex);
				return false;
			}
		}
		
		void CreateClass (string name, string namspace, string folder)
		{
			// TODO: Type system conversion.
			
//			string fullName = namspace.Length > 0 ? namspace + "." + name : name;
//			
//			var gen = new CodeRefactorer (fproject.Project.ParentSolution);
//			bool partialSupport = fproject.Project.UsePartialTypes;
//			Stetic.WidgetComponent component = (Stetic.WidgetComponent) rootWidget.Component;
//			
//			CodeTypeDeclaration type = new CodeTypeDeclaration ();
//			type.Name = name;
//			type.IsClass = true;
//			type.IsPartial = partialSupport;
//			type.BaseTypes.Add (new CodeTypeReference (component.Type.ClassName));
//			
//			// Generate the constructor. It contains the call that builds the widget.
//			
//			CodeConstructor ctor = new CodeConstructor ();
//			ctor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
//			
//			foreach (object val in component.Type.InitializationValues) {
//				if (val is Enum) {
//					ctor.BaseConstructorArgs.Add (
//						new CodeFieldReferenceExpression (
//							new CodeTypeReferenceExpression (val.GetType ()),
//							val.ToString ()
//						)
//					);
//				}
//				else
//					ctor.BaseConstructorArgs.Add (new CodePrimitiveExpression (val));
//			}
//			
//			if (partialSupport) {
//				CodeMethodInvokeExpression call = new CodeMethodInvokeExpression (
//					new CodeMethodReferenceExpression (
//						new CodeThisReferenceExpression (),
//						"Build"
//					)
//				);
//				ctor.Statements.Add (call);
//			} else {
//				CodeMethodInvokeExpression call = new CodeMethodInvokeExpression (
//					new CodeMethodReferenceExpression (
//						new CodeTypeReferenceExpression ("Stetic.Gui"),
//						"Build"
//					),
//					new CodeThisReferenceExpression (),
//					new CodeTypeOfExpression (fullName)
//				);
//				ctor.Statements.Add (call);
//			}
//			type.Members.Add (ctor);
//			
//			// Add signal handlers
//			
//			AddSignalsRec (type, component);
//			foreach (Stetic.Component ag in component.GetActionGroups ())
//				AddSignalsRec (type, ag);
//			
//			// Create the class
//			IType cls = gen.CreateClass (Project.Project, ((DotNetProject)Project.Project).LanguageName, folder, namspace, type);
//			if (cls == null)
//				throw new UserException ("Could not create class " + fullName);
//			
//			Project.Project.AddFile (cls.CompilationUnit.FileName, BuildAction.Compile);
//			IdeApp.ProjectOperations.Save (Project.Project);
//			
//			// Make sure the database is up-to-date
//			ProjectDomService.Parse (Project.Project, cls.CompilationUnit.FileName);
		}
		
		void AddSignalsRec (CodeTypeDeclaration type, Stetic.Component comp)
		{
			foreach (Stetic.Signal signal in comp.GetSignals ()) {
				CodeMemberMethod met = new CodeMemberMethod ();
				met.Name = signal.Handler;
				met.Attributes = MemberAttributes.Family;
				met.ReturnType = new CodeTypeReference (signal.SignalDescriptor.HandlerReturnTypeName);
				
				foreach (Stetic.ParameterDescriptor pinfo in signal.SignalDescriptor.HandlerParameters)
					met.Parameters.Add (new CodeParameterDeclarationExpression (pinfo.TypeName, pinfo.Name));
					
				type.Members.Add (met);
			}
			foreach (Stetic.Component cc in comp.GetChildren ()) {
				AddSignalsRec (type, cc);
			}
		}
		
		internal bool IsValidClass (IType cls)
		{
			foreach (var bt in cls.DirectBaseTypes) {
				if (bt.ReflectionName == rootWidget.Component.Type.ClassName)
					return true;
				
				var baseCls = bt;
				if (baseCls != null && IsValidClass (baseCls))
					return true;
			}
			return false;
		}
	}
	
	class OpenDocumentFileProvider: ITextFileProvider
	{
		public IEditableTextFile GetEditableTextFile (FilePath filePath)
		{
			foreach (Document doc in IdeApp.Workbench.Documents) {
				if (doc.FileName == filePath) {
					IEditableTextFile ef = doc.GetContent<IEditableTextFile> ();
					if (ef != null) return ef;
				}
			}
			return null;
		}
	}
}
