//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2017 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace EhLib
{

  public enum BorderSide
    { Left, Right, Top, Bottom }

  public enum ControlBorderStyle
    { None, FixedSingle, Fixed3D, Custom };

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class ControlBorderSide
  {
    private bool visible = true;
    private Color color = SystemColors.WindowFrame;
    private bool colorStored;
    private readonly ControlBorderSides controlBorderSides;

    public ControlBorderSide(ControlBorderSides controlBorderSides)
    {
      this.controlBorderSides = controlBorderSides;
    }

    [DefaultValue(true)]
    public bool Visible
    {
      get
      {
        return visible;
      }
      set
      {
        if (value != visible)
        {
          visible = value;
          VisibleChanged();
        }
      }  
    }

    public Color Color
    {
      get
      {
        if (colorStored)
          return color;
        else
          return DefaultColor();
      }

      set
      {
        if ((colorStored == false) || (color != value))
        {
          colorStored = true;
          color = value;
          ColorChanged();
        }
      }
    }

    private void VisibleChanged()
    {
      controlBorderSides.BorderSideChanged(this);
    }

    protected virtual void ColorChanged()
    {
      controlBorderSides.BorderSideChanged(this);
    }

    protected virtual Color DefaultColor()
    {
      return controlBorderSides.ControlBorder.Color;
    }

    protected virtual bool ShouldSerializeColor()
    {
      return colorStored;
    }

    public virtual void ResetColor()
    {
      if (colorStored)
      {
        colorStored = false;
        ColorChanged();
      }
    }

  }

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class ControlBorderSides
  {
    private readonly ControlBorder controlBorder;
    private readonly ControlBorderSide left;
    private readonly ControlBorderSide right;
    private readonly ControlBorderSide top;
    private readonly ControlBorderSide bottom;

    public ControlBorderSides(ControlBorder controlBorder)
    {
      this.controlBorder = controlBorder;
      left = new ControlBorderSide(this);
      right = new ControlBorderSide(this);
      top = new ControlBorderSide(this);
      bottom = new ControlBorderSide(this);
    }

    [Browsable(false)]
    public ControlBorder ControlBorder 
    { 
        get { return controlBorder; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ControlBorderSide Left 
    {
        get { return left; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ControlBorderSide Right
    {
        get { return right; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ControlBorderSide Top
    {
        get { return top; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ControlBorderSide Bottom
    {
        get { return bottom; }
    }

    internal void BorderSideChanged(ControlBorderSide controlBorderSide)
    {
      controlBorder.BorderSideChanged(controlBorderSide);
    }

    public ControlBorderSide GetSide(BorderSide borderSide)
    {
      switch (borderSide)
      {
        case BorderSide.Top: return Top;
        case BorderSide.Right: return Right;
        case BorderSide.Bottom: return Bottom;
        case BorderSide.Left: return Left;
        default: return null;
      }
    }
  }

  [ToolboxItem(false)]
  public class ControlBorder : Component
  {
    #region privates
    private ControlBorderStyle style = ControlBorderStyle.FixedSingle;
    //private Control control;
    private readonly ControlBorderSides borderSides;
    private Color color = SystemColors.WindowFrame;
    private bool colorStored;

    private EventHandler onBorderStateChanged;
    #endregion

    #region constructor
    public ControlBorder()
    {
      //this.control = control;
      this.borderSides = new ControlBorderSides(this);
    }
    #endregion

    #region design-time properties
    public Color Color
    {
      get
      {
        if (colorStored)
          return color;
        else
          return DefaultColor();
      }

      set
      {
        if ((colorStored == false) || (color != value))
        {
          colorStored = true;
          color = value;
          ColorChanged();
        }
      }
    }

    [DefaultValue(ControlBorderStyle.FixedSingle)]
    public ControlBorderStyle Style
    {
      get
      {
        return style;
      }

      set
      {
        if (style != value)
        {
          style = value;
          OnBorderStateChanged();
        }
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ControlBorderSides BorderSides
    {
      get { return borderSides; }
    }
    #endregion

    #region methods
    private void OnBorderStateChanged()
    {
      if (onBorderStateChanged != null)
      {
        onBorderStateChanged(this, EventArgs.Empty);
      }
    }

    internal void BorderSideChanged(ControlBorderSide controlBorderSide)
    {
      OnBorderStateChanged();
    }

    public virtual void Paint(Graphics graphics, Rectangle borderRect)
    {
      Rectangle br = borderRect;

      if (this.Style == ControlBorderStyle.None)
      {
        //return;
        EhLibUtils.DoNothing();
      }
      else if (this.Style == ControlBorderStyle.Fixed3D)
      {
        Border3DStyle stl = Border3DStyle.SunkenOuter;
        ControlPaint.DrawBorder3D(graphics, br, stl);
      }
      else if (this.Style == ControlBorderStyle.FixedSingle)
      {
        br.Width--;
        br.Height--;
        //ControlPaint.DrawVisualStyleBorder(graphics, br);
        if (Application.RenderWithVisualStyles)
          ControlPaint.DrawVisualStyleBorder(graphics, br);
        else
        {
          using (Pen pen = new Pen(SystemColors.ButtonShadow))
          {
            graphics.DrawRectangle(pen, br);
          }
        }
      } 
      else if (this.Style == ControlBorderStyle.Custom)
      {
        if (BorderSides.Top.Visible)
        {
          using (var pen = new Pen(BorderSides.Top.Color))
          {
            graphics.DrawLine(pen, new Point(br.Left, br.Top), new Point(br.Right - 1, br.Top));
          }
        }
        if (BorderSides.Right.Visible)
        {
          using (var pen = new Pen(BorderSides.Right.Color))
          {
            graphics.DrawLine(pen, new Point(br.Right - 1, br.Top), new Point(br.Right - 1, br.Bottom - 1));
          }
        }
        if (BorderSides.Bottom.Visible)
        {
          using (var pen = new Pen(BorderSides.Bottom.Color))
          {
            graphics.DrawLine(pen, new Point(br.Right, br.Bottom - 1), new Point(br.Left, br.Bottom - 1));
          }
        }
        if (BorderSides.Left.Visible)
        {
          using (var pen = new Pen(BorderSides.Left.Color))
          {
            graphics.DrawLine(pen, new Point(br.Left, br.Bottom - 1), new Point(br.Left, br.Top));
          }
        }
      }

    }

    public bool BorderSideVisible(BorderSide borderSide)
    {
        if (Style == ControlBorderStyle.Fixed3D ||
            Style == ControlBorderStyle.FixedSingle ||
            (Style == ControlBorderStyle.Custom && BorderSides.GetSide(borderSide).Visible)
          )
          return true;
        else
          return false;
    }

    public int BorderSideWidth(BorderSide borderSide)
    {
      if (Style == ControlBorderStyle.Fixed3D ||
          Style == ControlBorderStyle.FixedSingle ||
          (Style == ControlBorderStyle.Custom && BorderSides.GetSide(borderSide).Visible)
        )
        return 1;
      else
        return 0;
    }

    public void ExcludeBorderRect(ref Rectangle controlRect)
    {
      controlRect.X = controlRect.X + BorderSideWidth(BorderSide.Left);
      controlRect.Y = controlRect.Y + BorderSideWidth(BorderSide.Top);
      controlRect.Width = controlRect.Width - BorderSideWidth(BorderSide.Left) - BorderSideWidth(BorderSide.Right);
      controlRect.Height = controlRect.Height - BorderSideWidth(BorderSide.Top) - BorderSideWidth(BorderSide.Bottom);
    }

    public bool HasAnyBorderSide()
    {
      int brd = BorderSideWidth(BorderSide.Left) + BorderSideWidth(BorderSide.Top) + BorderSideWidth(BorderSide.Right) + BorderSideWidth(BorderSide.Bottom);
      if (brd > 0)
        return true;
      else
        return false;
    }

    protected virtual void ColorChanged()
    {
      OnBorderStateChanged();
    }

    protected virtual Color DefaultColor()
    {
      return SystemColors.WindowFrame;
    }

    protected virtual bool ShouldSerializeColor()
    {
      return colorStored;
    }

    public virtual void ResetColor()
    {
      if (colorStored)
      {
        colorStored = false;
        ColorChanged();
      }
    }
    #endregion

    #region events
    public event EventHandler BorderStateChanged
    {
      add
      {
        this.onBorderStateChanged += value;
      }

      remove
      {
        this.onBorderStateChanged -= value;
      }
    }
    #endregion  
    
  }
}

