// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using OpenLiveWriter.Api;
using OpenLiveWriter.ApplicationFramework.Skinning;
using OpenLiveWriter.BlogClient;
using OpenLiveWriter.ApplicationFramework;
using OpenLiveWriter.Controls;
using OpenLiveWriter.CoreServices;
using OpenLiveWriter.Interop.Windows;
using OpenLiveWriter.Extensibility.BlogClient;
using OpenLiveWriter.Localization;
using OpenLiveWriter.Localization.Bidi;
using OpenLiveWriter.PostEditor;

namespace OpenLiveWriter.PostEditor.PostPropertyEditing.CategoryControl
{

	internal class CategoryDropDownControlM1 : ComboBox, IBlogPostEditor, IBlogCategorySettings, INewCategoryContext
	{
		#region IBlogPostEditor Members

		void IBlogPostEditor.Initialize(IBlogPostEditingContext editingContext, IBlogClientOptions clientOptions)
		{
			// categories from blog
			CategoryContext.SetBlogCategories(_targetBlog.Categories) ;

			// pickup new categories from the blog-post
			CategoryContext.SetNewCategories(editingContext.BlogPost.NewCategories);

            // combine all post categories before settting
			ArrayList selectedCategories = new ArrayList();
			selectedCategories.AddRange(editingContext.BlogPost.NewCategories);
			selectedCategories.AddRange(editingContext.BlogPost.Categories);
			_lastSelectedCategories = new BlogPostCategory[0];
			CategoryContext.SelectedCategories = selectedCategories.ToArray(typeof(BlogPostCategory)) as BlogPostCategory[] ;
			
			toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList);
			_isDirty = false ;
		}

        void IBlogPostEditor.SaveChanges(BlogPost post, BlogPostSaveOptions options)
		{
			post.Categories = CategoryContext.SelectedExistingCategories;
			post.NewCategories = CategoryContext.SelectedNewCategories;
			_isDirty = false ;
		}

		private ToolTip2 toolTipCategories;

		bool IBlogPostEditor.ValidatePublish()
		{
			// see if we need to halt publishing to specify a category
			if ( Visible )
			{
				if ( PostEditorSettings.CategoryReminder && (CategoryContext.Categories.Length > 0) && (CategoryContext.SelectedCategories.Length == 0) )
				{
					if ( DisplayMessage.Show(MessageId.CategoryReminder, FindForm()) == DialogResult.No )
					{
						Focus() ;
						return false ;
					}
				}
			}

			// proceed
			return true ;
		}

		void IBlogPostEditor.OnPublishSucceeded(BlogPost blogPost, PostResult postResult)
		{
			_isDirty = false ;
		}

		void INewCategoryContext.NewCategoryAdded(BlogPostCategory category)
		{
			_categoryContext.CommitNewCategory(category) ;
			_isDirty = false ;
		}


		bool IBlogPostEditor.IsDirty
		{
			get
			{
				return _isDirty ;
			}
		}
		private bool _isDirty = false ;

		void IBlogPostEditor.OnBlogChanged(Blog newBlog)
		{
			// save dirty state
			bool isDirty = _isDirty ;

			_targetBlog = newBlog ;

			AdaptToBlogOptions() ;

            // a new blog always wipes out "New" categories
            CategoryContext.SetNewCategories(new BlogPostCategory[0]);

			_lastSelectedCategories = new BlogPostCategory[0];
			CategoryContext.SelectedCategories = new BlogPostCategory[0];

			// restore dirty state
			_isDirty = isDirty ;
		}
		private Blog _targetBlog ;


		void IBlogPostEditor.OnBlogSettingsChanged(bool templateChanged)
		{
			AdaptToBlogOptions() ;
		}

		private void AdaptToBlogOptions()
		{
			if ( _targetBlog.ClientOptions.SupportsMultipleCategories )
				CategoryContext.SelectionMode = CategoryContext.SelectionModes.MultiSelect;
			else
				CategoryContext.SelectionMode = CategoryContext.SelectionModes.SingleSelect;

			CategoryContext.SupportsAddingCategories = _targetBlog.ClientOptions.SupportsNewCategories ;
			CategoryContext.SupportsHierarchicalCategories = _targetBlog.ClientOptions.SupportsHierarchicalCategories ;
			CategoryContext.MaxCategoryNameLength = _targetBlog.ClientOptions.MaxCategoryNameLength ;
		
			// categories from blog
			CategoryContext.SetBlogCategories(_targetBlog.Categories) ;
		}

        public void OnClosing(CancelEventArgs e)
        {

        }

        public void OnPostClosing(CancelEventArgs e)
        {

        }

		#endregion


		public CategoryDropDownControlM1() : base()
		{
            if (DesignMode)
                _categoryContext = new CategoryContext();
            
            // This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			
			// prevent editing and showing of drop down
			DropDownStyle = ComboBoxStyle.DropDownList ;

			// fully cusotm painting
			IntegralHeight = false ;
			DrawMode = DrawMode.OwnerDrawFixed ;
			Items.Add(String.Empty) ;
		}

		public void Initialize(IWin32Window parentFrame, CategoryContext categoryContext)
		{
		    _categoryContext = categoryContext ?? new CategoryContext();
			_parentFrame = parentFrame ;
			_categoryContext.BlogCategorySettings = this ;
			_categoryContext.Changed += new CategoryContext.CategoryChangedEventHandler(_categoryContext_Changed);
		}
		

		// replace standard drop down behavior with category form
		protected override void WndProc(ref Message m)
		{
			if (IsDropDownMessage(m))
			{
				if (_categoryDisplayForm == null)
					DisplayCategoryForm();
				else
					Focus() ;
			}
			else
			{
				base.WndProc(ref m);
			}
		}


		// replace standard painting behavior
		protected override void OnDrawItem(DrawItemEventArgs evt)
		{
		    BidiGraphics g = new BidiGraphics(evt.Graphics, ClientRectangle);

			// determine the text color based on whether we have categories
			bool hasCategories = _categoryContext.SelectedCategories.Length > 0 ;
			Color textColor = hasCategories ? SystemColors.ControlText : SystemColors.GrayText ; //Color.FromArgb(200, SystemColors.ControlText) ;
			
			// margins
			const int HORIZONTAL_MARGIN = 1 ;

			// draw the icon (if we have one)
			const int ICON_MARGIN = 4 ;
			int textMargin = 0 ;
			if ( _icon != null )
			{
				g.DrawImage(true, _icon, new Rectangle(ScaleX(ICON_MARGIN), ScaleY(2), ScaleX(_icon.Width), ScaleY(_icon.Height)));
				textMargin += ScaleX(ICON_MARGIN + _icon.Width) ;
			}

			// draw the text
			int leftMargin = textMargin  ;
			int rightMargin = ScaleX(HORIZONTAL_MARGIN) ;
			Rectangle textRegion = new Rectangle(new Point(leftMargin, -1), new Size(Width - leftMargin - rightMargin, Height) );
			
			TextFormatFlags tempFormat = DisplayFormat;
			Rectangle tempRectangle = textRegion;

		    g.DrawText(
				_categoryContext.Text,
                Font, tempRectangle, textColor, tempFormat);

			// draw focus rectangle (only if necessary)
			evt.DrawFocusRectangle();
		}

        BlogPostCategory[] IBlogCategorySettings.RefreshCategories(bool ignoreErrors)
        {
            try
            {
                _targetBlog.RefreshCategories();
            }
            catch (BlogClientOperationCancelledException)
            {
                // show no UI for operation cancelled
                Debug.WriteLine("BlogClient operation cancelled");
            }
            catch (Exception ex)
            {
                if (!ignoreErrors)
                    DisplayableExceptionDisplayForm.Show(_parentFrame, ex);
            }
            return _targetBlog.Categories;
        }

		void IBlogCategorySettings.UpdateCategories(BlogPostCategory[] categories)
		{
			using ( BlogSettings blogSettings = BlogSettings.ForBlogId(_targetBlog.Id) )
				blogSettings.Categories = categories ;
		}

		private CategoryContext CategoryContext
		{
			get
			{
				return _categoryContext;
			}
		}


		public void DisplayCategoryForm()
		{
			Focus();
		
            /*
			_categoryDisplayForm = new CategoryDisplayFormM1(this, _categoryContext) ;
			_categoryDisplayForm.MinDropDownWidth = 0;
			IMiniFormOwner miniFormOwner = FindForm() as IMiniFormOwner;
			if (miniFormOwner != null)
				_categoryDisplayForm.FloatAboveOwner(miniFormOwner);
			_categoryDisplayForm.Closed += new EventHandler(_categoryDisplayForm_Closed);
			using ( new WaitCursor() )
				_categoryDisplayForm.Show();
            */

		    Point anchor = PointToScreen(new Point(RightToLeft == RightToLeft.Yes ? 0 : ClientSize.Width, ClientSize.Height));
		    _categoryDisplayForm = new CategoryDisplayFormW3M1(_categoryContext, anchor);
		    _categoryDisplayForm.SelfDispose = true;
            IMiniFormOwner miniFormOwner = FindForm() as IMiniFormOwner;
            if (miniFormOwner != null)
                _categoryDisplayForm.FloatAboveOwner(miniFormOwner);
            _categoryDisplayForm.Closed += _categoryDisplayForm_Closed;
            using (new WaitCursor())
                _categoryDisplayForm.Show();
		}
		private CategoryDisplayFormW3M1 _categoryDisplayForm = null;

		private void _categoryContext_Changed(object sender, CategoryContext.CategoryChangedEventArgs e)
		{
			if ( !CategoryListsAreEqual( _lastSelectedCategories, CategoryContext.SelectedCategories ) )
				_isDirty = true ;

			// always record last selected categories
			_lastSelectedCategories = CategoryContext.SelectedCategories ;

			toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList);
			Invalidate();
			Update();
		}
		private BlogPostCategory[] _lastSelectedCategories = new BlogPostCategory[0];

		private bool CategoryListsAreEqual( BlogPostCategory[] categories1, BlogPostCategory[] categories2 )
		{
			if ( categories1.Length == categories2.Length )
			{
				for (int i=0; i<categories1.Length; i++)
				{
					if ( !categories1[i].Equals(categories2[i]) )
						return false ;
				}

				// if we got this far they are equal
				return true ;
			}
			else
			{
				return false ;
			}
		}

		private void _categoryDisplayForm_Closed(object sender, EventArgs e)
		{
			_categoryDisplayForm = null ;
			Invalidate();
		}
		
		
		
		private TextFormatFlags DisplayFormat
		{
			get { return TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix; }
		}

		private bool IsDropDownMessage(Message m)
		{
			// Left Mouse Button while form is not displaying
			if ( m.Msg == WM.LBUTTONDOWN || m.Msg == WM.LBUTTONDBLCLK )   
			{
				return true ;
			}
 
			// F4
			else if ( m.Msg == WM.KEYDOWN )
			{
				Keys keyCombo = (Keys)(int)m.WParam & Keys.KeyCode ; 

				if ( keyCombo == Keys.F4 )
					return true ;
				else 
					return false ;
			}

				// Alt+Arrow
			else if ( m.Msg == WM.SYSKEYDOWN )
			{
				int wparam = m.WParam.ToInt32();
				int lparam = m.LParam.ToInt32();

				if ((wparam == 0x28 && (lparam == 0x21500001 || lparam == 0x20500001)) ||    //Alt+Down, Alt+NumDown
					(wparam == 0x26 && (lparam == 0x21480001 || lparam == 0x20480001)))      //Alt+Up, Alt+NumUp
				{
					return true ;
				}
				else
				{
					return false ;
				}
			}

				// Not a drop-down message
			else
			{
				return false ;
			}
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}


		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.toolTipCategories = new ToolTip2(this.components);
			// 
			// CategoryDropDownControlM1
			// 
			this.Size = new System.Drawing.Size(138, 48);

		}
		#endregion

		#region High DPI Scaling
        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            SaveScaleState(factor.Width, factor.Height);
            base.ScaleControl(factor, specified);
        }
		protected override void ScaleCore(float dx, float dy)
		{
		    SaveScaleState(dx, dy);
		    base.ScaleCore(dx, dy);
		}

	    private void SaveScaleState(float dx, float dy)
	    {
	        scale = new PointF(scale.X*dx, scale.Y*dy);
	    }

	    private PointF scale = new PointF(1f, 1f);
		
		protected int ScaleX(int x)
		{
			return (int) (x*scale.X);
		}
		
		protected int ScaleY(int y)
		{
			return (int) (y*scale.Y);
		}
		#endregion

		private IWin32Window _parentFrame;
		private System.ComponentModel.IContainer components;
		private CategoryContext _categoryContext;
        private Bitmap _icon = null;


	    public void OnClosed() {}
	    public void OnPostClosed() {}
	}
}
