// 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.Globalization;
using System.Threading;
using System.Windows.Forms;
using OpenLiveWriter.BlogClient;
using OpenLiveWriter.CoreServices.UI;
using OpenLiveWriter.Extensibility.BlogClient;
using OpenLiveWriter.Localization;
using OpenLiveWriter.Localization.Bidi;
using OpenLiveWriter.Controls;
using OpenLiveWriter.CoreServices;
using OpenLiveWriter.PostEditor;
using Timer = System.Windows.Forms.Timer;

namespace OpenLiveWriter.PostEditor.OpenPost
{
    internal class BlogPostSourceListBox : ListBox
    {
        #region Initialization and Cleanup

        public BlogPostSourceListBox()
        {
            _theme = new ControlTheme(this, true);
        }

        public void Initialize(bool _includeDrafts)
        {
            // get the list of blogs, determine how many items we will be displaying,
            // and then have that drive the view mode
            string[] blogIds = BlogSettings.GetBlogIds();
            int itemCount = (_includeDrafts ? 1 : 0) + 1 + blogIds.Length;
            _showLargeIcons = itemCount <= 5;

            // configure owner draw
            DrawMode = DrawMode.OwnerDrawFixed;
            SelectionMode = SelectionMode.One;
            HorizontalScrollbar = false;
            IntegralHeight = false;
            ItemHeight = CalculateItemHeight(_showLargeIcons);

            // populate list
            if (_includeDrafts)
                _draftsIndex = Items.Add(new PostSourceItem(new LocalDraftsPostSource(), this));
            _recentPostsIndex = Items.Add(new PostSourceItem(new LocalRecentPostsPostSource(), this));

            ArrayList blogs = new ArrayList();
            foreach (string blogId in BlogSettings.GetBlogIds())
            {
                blogs.Add(new PostSourceItem(Res.Get(StringId.Blog), new RemoteWeblogBlogPostSource(blogId), this));
            }
            blogs.Sort();
            Items.AddRange(blogs.ToArray());
        }

        public void SelectDrafts()
        {
            SelectedIndex = _draftsIndex;
        }

        public void SelectRecentPosts()
        {
            SelectedIndex = _recentPostsIndex;
        }

        public IPostEditorPostSource SelectedPostSource
        {
            get
            {
                return (SelectedItem as PostSourceItem).PostSource;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {

            }
            base.Dispose(disposing);
        }

        #endregion

        #region Painting

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            // screen invalid drawing states
            if (DesignMode || e.Index == -1)
                return;

            // get post-source we are rendering
            IPostEditorPostSource postSource = (Items[e.Index] as PostSourceItem).PostSource;

            // determine image and text to use
            Image postSourceImage;
            if (e.Index == _draftsIndex)
                postSourceImage = _showLargeIcons ? _draftsImageLarge : _draftsImage;
            else if (e.Index == _recentPostsIndex)
                postSourceImage = _showLargeIcons ? _recentPostsImageLarge : _recentPostsImage;
            else
                postSourceImage = _showLargeIcons ? _weblogImageLarge : _weblogImage;

            // determine state
            bool selected = (e.State & DrawItemState.Selected) > 0;

            // calculate colors
            Color backColor, textColor;
            if (selected)
            {
                if (Focused)
                {
                    backColor = _theme.backColorSelectedFocused;
                    textColor = _theme.textColorSelectedFocused;
                }
                else
                {
                    backColor = _theme.backColorSelected;
                    textColor = _theme.textColorSelected;
                }
            }
            else
            {
                backColor = _theme.backColor;
                textColor = _theme.textColor;
            }

            BidiGraphics g = new BidiGraphics(e.Graphics, e.Bounds);

            // draw background
            using (SolidBrush solidBrush = new SolidBrush(backColor))
                g.FillRectangle(solidBrush, e.Bounds);

            if (_showLargeIcons)
            {
                // center the image within the list box
                int imageLeft = e.Bounds.Left + ((e.Bounds.Width / 2) - (ScaleX(postSourceImage.Width) / 2));
                int imageTop = e.Bounds.Top + ScaleY(LARGE_TOP_INSET);
                g.DrawImage(false, postSourceImage, new Rectangle(imageLeft, imageTop, ScaleX(postSourceImage.Width), ScaleY(postSourceImage.Height)));

                // setup string format
                TextFormatFlags stringFormat = TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl | TextFormatFlags.ExpandTabs | TextFormatFlags.WordEllipsis | TextFormatFlags.HorizontalCenter;

                // calculate standard text drawing metrics
                int leftMargin = ScaleX(ELEMENT_PADDING);
                int topMargin = imageTop + ScaleY(postSourceImage.Height) + ScaleY(ELEMENT_PADDING);
                int fontHeight = g.MeasureText(postSource.Name, e.Font).Height;

                // caption
                // calculate layout rectangle
                Rectangle layoutRectangle = new Rectangle(
                    leftMargin,
                    topMargin,
                    e.Bounds.Width - (2 * ScaleX(ELEMENT_PADDING)),
                    fontHeight * TITLE_LINES);

                // draw caption
                g.DrawText(postSource.Name, e.Font, layoutRectangle, textColor, stringFormat);

            }
            else
            {
                // draw post icon
                g.DrawImage(false, postSourceImage,
                                      new Rectangle(e.Bounds.Left + ScaleX(ELEMENT_PADDING), e.Bounds.Top + ScaleY(SMALL_TOP_INSET),
                                      ScaleX(postSourceImage.Width), ScaleY(postSourceImage.Height)));

                // setup string format
                TextFormatFlags stringFormat = TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl | TextFormatFlags.ExpandTabs | TextFormatFlags.WordEllipsis;

                // calculate standard text drawing metrics
                int leftMargin = ScaleX(ELEMENT_PADDING) + ScaleX(postSourceImage.Width) + ScaleX(ELEMENT_PADDING);
                int topMargin = e.Bounds.Top + ScaleY(SMALL_TOP_INSET);
                int fontHeight = g.MeasureText(postSource.Name, e.Font).Height;

                // caption
                // calculate layout rectangle
                Rectangle layoutRectangle = new Rectangle(
                    leftMargin,
                    topMargin,
                    e.Bounds.Width - leftMargin - ScaleX(ELEMENT_PADDING),
                    fontHeight * TITLE_LINES);

                // draw caption
                g.DrawText(postSource.Name, e.Font, layoutRectangle, textColor, stringFormat);
            }

            // draw focus rectange if necessary
            e.DrawFocusRectangle();
        }

        private class ControlTheme : ControlUITheme
        {
            internal Color backColor;
            internal Color textColor;
            internal Color backColorSelected;
            internal Color textColorSelected;
            internal Color backColorSelectedFocused;
            internal Color textColorSelectedFocused;
            internal Color topLineColor;

            public ControlTheme(Control control, bool applyTheme) : base(control, applyTheme)
            {
            }

            protected override void ApplyTheme(bool highContrast)
            {
                base.ApplyTheme(highContrast);

                backColor = SystemColors.Window;
                textColor = SystemColors.ControlText;
                backColorSelected = !highContrast ? SystemColors.ControlLight : SystemColors.InactiveCaption;
                textColorSelected = !highContrast ? SystemColors.ControlText : SystemColors.InactiveCaptionText;
                backColorSelectedFocused = SystemColors.Highlight;
                textColorSelectedFocused = SystemColors.HighlightText;
                topLineColor = SystemColors.ControlLight;
            }
        }
        private ControlTheme _theme;

        private int CalculateItemHeight(bool showLargeIcons)
        {
            int textHeight = Convert.ToInt32(Font.GetHeight() * TITLE_LINES);

            if (showLargeIcons)
            {
                return ScaleY(LARGE_TOP_INSET) + ScaleY(_weblogImageLarge.Height) + ScaleY(ELEMENT_PADDING) + textHeight + ScaleY(LARGE_BOTTOM_INSET);
            }
            else
            {
                return ScaleY(SMALL_TOP_INSET) + textHeight + ScaleY(SMALL_BOTTOM_INSET);
            }
        }

        #endregion

        #region Accessibility
        internal class BlogPostSourceListBoxAccessibility : ControlAccessibleObject
        {
            private BlogPostSourceListBox _listBox;
            public BlogPostSourceListBoxAccessibility(BlogPostSourceListBox ownerControl) : base(ownerControl)
            {
                _listBox = ownerControl;
            }

            public override AccessibleObject GetChild(int index)
            {
                return _listBox.Items[index] as AccessibleObject;
            }

            public override int GetChildCount()
            {
                return _listBox.Items.Count;
            }

            public void NotifySelectionChanged(int index)
            {
                if (index >= 0)
                    NotifyClients(AccessibleEvents.Focus, index);
            }

            public void NotifySelectionChanged()
            {
                NotifySelectionChanged(_listBox.SelectedIndex);
            }

            public override AccessibleRole Role
            {
                get { return AccessibleRole.List; }
            }
        }

        class PostSourceItem : AccessibleObject, IComparable
        {
            private IPostEditorPostSource _source;
            private string _accName;
            private BlogPostSourceListBox _listbox;

            public PostSourceItem(IPostEditorPostSource source, BlogPostSourceListBox ownerControl)
            {
                _source = source;
                _accName = source.Name;
                _listbox = ownerControl;
            }
            public PostSourceItem(string accName, IPostEditorPostSource source, BlogPostSourceListBox ownerControl)
            {
                _source = source;
                _accName = accName;
                _listbox = ownerControl;
            }

            public override string ToString()
            {
                return Name;
            }

            public override string Value
            {
                get { return _accName; }
                set { base.Value = value; }
            }

            public override string Name
            {
                get { return _source.Name; }
                set { base.Name = value; }
            }

            private bool IsSelected()
            {
                return _listbox.SelectedItem == this && _listbox.Focused;
            }

            public override AccessibleStates State
            {
                get
                {
                    AccessibleStates state = AccessibleStates.Focusable;
                    if (IsSelected())
                        state = state | AccessibleStates.Focused;
                    return state;
                }
            }

            public override AccessibleRole Role
            {
                get { return AccessibleRole.ListItem; }
            }

            public override AccessibleObject Parent
            {
                get { return _listbox.AccessibilityObject; }
            }

            public IPostEditorPostSource PostSource { get { return _source; } }

            public int CompareTo(object obj)
            {
                try
                {
                    return string.Compare(_source.Name, ((PostSourceItem)obj)._source.Name, true, CultureInfo.CurrentCulture);
                }
                catch (InvalidCastException)
                {
                    Debug.Fail("InvalidCastException");
                    return 0;
                }
                catch (NullReferenceException)
                {
                    Debug.Fail("NullRefException");
                    return 0;
                }
            }
        }

        protected override AccessibleObject CreateAccessibilityInstance()
        {
            if (_accessibleObject == null)
                _accessibleObject = new BlogPostSourceListBoxAccessibility(this);
            return _accessibleObject;
        }
        BlogPostSourceListBoxAccessibility _accessibleObject;

        #endregion

        #region High DPI Scaling
        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            SaveScale(factor.Width, factor.Height);
            base.ScaleControl(factor, specified);
        }

        protected override void ScaleCore(float dx, float dy)
        {
            SaveScale(dx, dy);
            base.ScaleCore(dx, dy);
        }

        private void SaveScale(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

        #region Private Data

        // item metrics
        private const int SMALL_TOP_INSET = 7;
        private const int SMALL_BOTTOM_INSET = 7;
        private const int LARGE_TOP_INSET = 7;
        private const int LARGE_BOTTOM_INSET = 4;
        private const int ELEMENT_PADDING = 3;
        private const int TITLE_LINES = 2;

        // view mode
        private bool _showLargeIcons;

        // special list values
        private int _draftsIndex;
        private int _recentPostsIndex;

        // images
        private Image _draftsImage = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectDraftPostings.png");
        private Image _recentPostsImage = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectRecentPostings.png");
        private Image _weblogImage = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectWebLogPostings.png");
        private Image _draftsImageLarge = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectDraftPostingsLarge.png");
        private Image _recentPostsImageLarge = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectRecentPostingsLarge.png");
        private Image _weblogImageLarge = ResourceHelper.LoadAssemblyResourceBitmap("OpenPost.Images.SelectWebLogPostingsLarge.png");

        #endregion
    }
}