/// <copyright company="JetBrains">
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
/// </copyright>

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;

using JetBrains.UI.Components.CustomListBox;

namespace JetBrains.UI.Components.RichTextListBox
{
	/// <summary>
	/// Paints items with rich text
	/// </summary>
	public class RichTextItemPainter : IItemPainter
	{
    /// <summary>
    /// Represents a list item
    /// </summary>
    private class ListItem
    {
      /// <summary>
      /// The text to use
      /// </summary>
      private RichText.RichText myRichText;

      /// <summary>
      /// The icon to use
      /// </summary>
      private Image myIcon;

      public RichText.RichText RichText
      {
        get { return myRichText; }
      }

      public Image Icon
      {
        get { return myIcon; }
      }

      public ListItem( RichText.RichText richText, Image icon )
      {
        myRichText = richText;
        myIcon = icon;
      }
    }

    /// <summary>
    /// List of handled Items
    /// </summary>
    private Hashtable myItems = new Hashtable();

    /// <summary>
    /// The list box to use
    /// </summary>
    private CustomListBox.CustomListBox myListBox;

    public CustomListBox.CustomListBox ListBox
    {
      get { return myListBox; }
      set { myListBox = value; }
    }

    /// <summary>
    /// Adds text to a List Item
    /// </summary>
    public void Add( object item, RichText.RichText text, Image icon )
    {
      myItems[item] = new ListItem(text, icon);      
    }

    /// <summary>
    /// Removes a Item
    /// </summary>
    public void Remove( object item )
    {
      myItems.Remove(item);      
    }

    public void Clear()
    {
      myItems = new Hashtable(myItems.Count);
    }

    #region IItemPainter Members

    public bool IsHandled(object item)
    {
      return myItems.Contains(item);
    }

    public System.Drawing.Size GetSize(object item, System.Drawing.Graphics g)
    {
      if (!IsHandled(item))
        return new Size();

      RichText.RichText text = ((ListItem)myItems[item]).RichText;
      IntPtr hdc = g.GetHdc();
      try
      {
        Size size = text.GetSize(hdc).ToSize();
        size.Height = myListBox.ItemHeight;
        size.Width += 3 + CalculateOffset(item);

        return size;
      }
      finally
      {
        g.ReleaseHdc( hdc );
      }
    }

    public void Draw( object item, Graphics g, Rectangle boundRect, bool drawSelected )
    {
      if (!IsHandled(item))
        return;

      RichText.RichText text = ((ListItem)myItems[item]).RichText;
      Image icon = ((ListItem)myItems[item]).Icon;

      Rectangle rect = CalculateItemRectangle(item, boundRect);
      Rectangle contentRect = CalculateContentRectangle(item, g, rect);

      // Center content vertically
      if (contentRect.Height < rect.Height)
      {
        int topOffset = (rect.Height - contentRect.Height) / 2;
        contentRect.Y += topOffset;
      }

      //object selectedItem = myListBox.SelectedIndex < 0 || myListBox.SelectedIndex >= myListBox.Items.Count 
      //  ? null : myListBox.Items[myListBox.SelectedIndex];
      
      g.FillRectangle(new SolidBrush(myListBox.BackColor), boundRect);

      if ( drawSelected )
      {
        Color backgroundColor = Colors.ListSelectionBackColor(myListBox.Focused);

        text = (RichText.RichText)text.Clone();
          
        g.FillRectangle(new SolidBrush(backgroundColor), rect);
        text.SetColors(SystemColors.HighlightText, backgroundColor);
      }

      if (icon != null)
        DrawIcon(g, icon, boundRect, item);

      IntPtr hdc = g.GetHdc();
      try
      {
        text.Draw(hdc, contentRect);
        //System.Console.WriteLine(text.ToString());
      }
      finally
      {
        g.ReleaseHdc( hdc );
      }

      if ( drawSelected && myListBox.Focused ) 
        DrawDottedRectangle(g, rect);
    }

    #endregion

    protected virtual void DrawIcon(Graphics g, Image icon, Rectangle boundRect, object item)
    {
      g.DrawImage(icon, boundRect.Left, boundRect.Top, icon.Width, icon.Height);
    }

    #region Private methods

    private Rectangle CalculateItemRectangle( object item, Rectangle boundRect )
    {
      Rectangle rect = boundRect;
      int offset = CalculateOffset(item);

      rect.X += offset;
      rect.Width -= offset;

      return rect;
    }

    private int CalculateOffset( object item )
    {
      Image icon = ((ListItem)myItems[item]).Icon;

      if (icon != null)
        return icon.Width;
      else
        return 16;
    }

    /// <summary>
    /// Calculated content rectangle of Item
    /// </summary>
    private Rectangle CalculateContentRectangle( object item, Graphics g, Rectangle rect )
    { 
      RichText.RichText text = ((ListItem)myItems[item]).RichText;
      IntPtr hdc = g.GetHdc();
      try
      {
        Rectangle result = new Rectangle(rect.Location, text.GetSize(hdc).ToSize());
        result.Width = rect.Width;
        return result;
      }
      finally
      {
        g.ReleaseHdc(hdc);
      }
    }

    /// <summary>
    /// Draws dotted rectangle
    /// </summary>
    private void DrawDottedRectangle( Graphics g, Rectangle rect )
    {
      Region oldClip = g.Clip;
      g.SetClip(rect);
      Pen pen = new Pen(new HatchBrush(HatchStyle.Percent50, SystemColors.Highlight, Color.Black), 1);
      rect.Width--;
      rect.Height--;
      g.DrawRectangle(pen, rect);
      g.SetClip(oldClip, CombineMode.Replace);
    }
    #endregion
  }
}