// 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.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.PostEditor;
using OpenLiveWriter.PostEditor.PostPropertyEditing.CategoryControl.DisplayMessages;

namespace OpenLiveWriter.PostEditor.PostPropertyEditing.CategoryControl
{

    internal class CategoryDropDownControl : ComboBox, IBlogPostEditor, IBlogCategorySettings
    {
        #region IBlogPostEditor Members

        void IBlogPostEditor.Initialize(IBlogPostEditingContext editingContext)
        {
            CategoryContext.SelectedCategories = editingContext.BlogPost.Categories ;
            toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList);
            _isDirty = false ;
        }

        void IBlogPostEditor.SaveChanges(BlogPost post)
        {
            post.Categories = CategoryContext.SelectedCategories;
            _isDirty = false ;
        }

        private System.Windows.Forms.ToolTip 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(typeof(CategoryReminderDisplayMessage), FindForm(), "\r\n\r\n") == DialogResult.No )
                    {
                        Focus() ;
                        return false ;
                    }
                }
            }

            // proceed
            return true ;
        }

        void IBlogPostEditor.OnPublishSucceeded(BlogPost blogPost, PostResult postResult)
        {

        }

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

        void IBlogPostEditor.OnBlogChanged(Blog newBlog)
        {
            _targetBlog = newBlog ;

            if ( newBlog.ClientOptions.SupportsMultipleCategories )
                CategoryContext.SelectionMode = CategoryContext.SelectionModes.MultiSelect;
            else
                CategoryContext.SelectionMode = CategoryContext.SelectionModes.SingleSelect;

            CategoryContext.SetBlogCategories(_targetBlog.Categories);
            CategoryContext.SelectedCategories = new BlogPostCategory[0];
        }
        private Blog _targetBlog ;


        void IBlogPostEditor.OnBlogSettingsChanged(bool templateChanged)
        {
            // make sure we have the lastest categoires (in case the underlying target blog changed)
            CategoryContext.SetBlogCategories(_targetBlog.Categories) ;
        }

        #endregion

        public CategoryDropDownControl() : base()
        {
            // 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) ;

            _categoryContext = new CategoryContext();
        }

        public void Initialize(IWin32Window parentFrame)
        {
            _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))
            {
                DisplayCategoryForm();
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        // replace standard painting behavior
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            // determine the text color based on whether we have categories
            bool hasCategories = _categoryContext.SelectedCategories.Length > 0 ;
            Color textColor = hasCategories ? SystemColors.ControlText : SystemColors.GrayText;

            // draw the text
            const int HORIZONTAL_MARGIN = 2 ;
            const int VERTICAL_MARGIN = 1 ;
            Rectangle textRegion = new Rectangle(new Point(HORIZONTAL_MARGIN, VERTICAL_MARGIN), new Size(Width - 2*HORIZONTAL_MARGIN, Height - 2*VERTICAL_MARGIN) );
            using (Brush textBrush = new SolidBrush(textColor))
            {
                e.Graphics.DrawString(
                    _categoryContext.Text,
                    Font, textBrush, textRegion, DisplayFormat );
            }

            // draw focus rectangle (only if necessary)
            e.DrawFocusRectangle();
        }
        private const int MARGIN = 1;

        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;
            }
        }

        private void DisplayCategoryForm()
        {
            // If we just dismissed the form, don't display it
            if (_recentlyClosedForm)
                return;

            Focus();

            _categoryDisplayForm = new CategoryDisplayForm(this, _categoryContext);
            //_categoryDisplayForm.MinDropDownWidth = 0;
            IMiniFormOwner miniFormOwner = FindForm() as IMiniFormOwner;
            if (miniFormOwner != null)
                _categoryDisplayForm.FloatAboveOwner(miniFormOwner);
            _categoryDisplayForm.Closed += new EventHandler(_categoryDisplayForm_Closed);
            _categoryDisplayForm.Show();
        }
        private CategoryDisplayFormBase _categoryDisplayForm = null;

        private void _categoryContext_Changed(object sender, CategoryContext.CategoryChangedEventArgs e)
        {
            _isDirty = true ;

            toolTipCategories.SetToolTip(this, CategoryContext.FormattedCategoryList);
            Invalidate();
            Update();
        }

        private void _categoryDisplayForm_Closed(object sender, EventArgs e)
        {
            // This timer makes it so when you click the control to dismiss the form,
            // we don't instantly redisplay the form (same goes for hitting enter)
            _recentlyClosedForm = true;
            Timer t = new Timer();
            t.Interval = 100;
            t.Tick += new EventHandler(t_Tick);
            t.Start();

            _categoryDisplayForm.Closed -= new EventHandler(_categoryDisplayForm_Closed);
            Invalidate();
        }

        private void t_Tick(object sender, EventArgs e)
        {
            _recentlyClosedForm = false;
            Timer t = (Timer)sender;
            t.Stop();
            t.Tick -= new EventHandler(t_Tick);
            t.Dispose();
        }

        private StringFormat DisplayFormat
        {
            get
            {
                if (_displayFormat == null)
                {
                    _displayFormat = new StringFormat();
                    _displayFormat.Trimming = StringTrimming.EllipsisCharacter;
                    _displayFormat.Alignment = StringAlignment.Near;
                    _displayFormat.LineAlignment = StringAlignment.Center;
                    _displayFormat.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit;

                }
                return _displayFormat;
            }
        }
        private StringFormat _displayFormat;

        private bool IsDropDownMessage(Message m)
        {
            // Left Mouse Button
            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 System.Windows.Forms.ToolTip(this.components);
            //
            // CategoryDropDownControl
            //
            this.Size = new System.Drawing.Size(138, 48);

        }
        #endregion

        private IWin32Window _parentFrame;
        private bool _recentlyClosedForm = false;
        private System.ComponentModel.IContainer components;
        private CategoryContext _categoryContext;

    }

}