using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Globalization;

namespace CustomControls
{
  /// <summary>
  /// ドロップ ダウン メニューを表示する為のボタン コントロール クラスです。
  /// </summary>
  public sealed class DropDownMenuButton : ToggleButton
  {
    /// <summary>
    /// インスタンスを初期化します。
    /// </summary>
    public DropDownMenuButton()
    {
      var binding = new Binding("DropDownContextMenu.IsOpen") { Source = this };
      this.SetBinding(DropDownMenuButton.IsCheckedProperty, binding);
    }

    /// <summary>
    /// ドロップ ダウンとして表示するコンテキスト メニューを取得または設定します。
    /// </summary>
    public ContextMenu DropDownContextMenu
    {
      get
      {
        return this.GetValue(DropDownContextMenuProperty) as ContextMenu;
      }
      set
      {
        this.SetValue(DropDownContextMenuProperty, value);
      }
    }

    /// <summary>
    /// コントロールがクリックされた時のイベントです。
    /// </summary>
    protected override void OnClick()
    {
      if (this.DropDownContextMenu == null) { return; }

      this.DropDownContextMenu.PlacementTarget = this;
      this.DropDownContextMenu.Placement = PlacementMode.Bottom;
      this.DropDownContextMenu.IsOpen = !DropDownContextMenu.IsOpen;
    }

    /// <summary>
    /// ドロップ ダウンとして表示するメニューを表す依存プロパティです。
    /// </summary>
    public static readonly DependencyProperty DropDownContextMenuProperty = DependencyProperty.Register("DropDownContextMenu", typeof(ContextMenu), typeof(DropDownMenuButton), new UIPropertyMetadata(null));
  }

  //[TemplatePart(Name = "PART_Button", Type = typeof(ButtonBase))]
  public class SplitButton : ToggleButton
  {
    #region Dependency Properties

    public static readonly DependencyProperty DropDownContextMenuProperty = DependencyProperty.Register("DropDownContextMenu", typeof(ContextMenu), typeof(SplitButton), new UIPropertyMetadata(null));
    public static readonly DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(ImageSource), typeof(SplitButton));
    //public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(SplitButton));
    public static readonly DependencyProperty MainButtonContentProperty = DependencyProperty.Register("Content", typeof(string), typeof(SplitButton));
    public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(UIElement), typeof(SplitButton));
    public static readonly DependencyProperty MainButtonCommandProperty = DependencyProperty.Register("MainButtonCommand", typeof(ICommand), typeof(SplitButton), new FrameworkPropertyMetadata(null));
    public static readonly DependencyProperty DropDownButtonCommandProperty = DependencyProperty.Register("DropDownButtonCommand", typeof(ICommand), typeof(SplitButton), new FrameworkPropertyMetadata(null));

    #endregion

    #region Constructors

    public SplitButton()
    {
      // Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property 
      var binding = new Binding("DropDownContextMenu.IsOpen") { Source = this };
      SetBinding(IsCheckedProperty, binding);
    }

    #endregion

    #region Properties

    public ContextMenu DropDownContextMenu
    {
      get { return GetValue(DropDownContextMenuProperty) as ContextMenu; }
      set { SetValue(DropDownContextMenuProperty, value); }
    }

    public ImageSource Image
    {
      get { return GetValue(ImageProperty) as ImageSource; }
      set { SetValue(ImageProperty, value); }
    }

    //public string Content
    //{
    //  get { return GetValue(MainButtonContentProperty) as string; }
    //  set { SetValue(MainButtonContentProperty, value); }
    //}

    public UIElement Target
    {
      get { return GetValue(TargetProperty) as UIElement; }
      set { SetValue(TargetProperty, value); }
    }

    public ICommand MainButtonCommand
    {
      get { return GetValue(MainButtonCommandProperty) as ICommand; }
      set { SetValue(MainButtonCommandProperty, value); }
    }

    public ICommand DropDownButtonCommand
    {
      get { return GetValue(DropDownButtonCommandProperty) as ICommand; }
      set { SetValue(DropDownButtonCommandProperty, value); }
    }

    #endregion

    #region Public Override Methods

    /// <summary>
    /// 
    /// </summary>
    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();
      SetMainButtonCommand();
      SetMainButtonContent();
    }

    #endregion

    #region Protected Override Methods

    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);

      if (e.Property == MainButtonCommandProperty)
        SetMainButtonCommand();

      if (e.Property == MainButtonContentProperty)
        SetMainButtonContent();

      if (e.Property == DropDownButtonCommandProperty)
        Command = DropDownButtonCommand;
    }

    protected override void OnClick()
    {
      if (DropDownContextMenu == null) return;

      if (DropDownButtonCommand != null)
        DropDownButtonCommand.Execute(null);

      // If there is a drop-down assigned to this button, then position and display it 
      DropDownContextMenu.PlacementTarget = this;
      DropDownContextMenu.Placement = PlacementMode.Bottom;
      DropDownContextMenu.IsOpen = !DropDownContextMenu.IsOpen;
    }

    #endregion

    #region Private Methods

    private void SetMainButtonCommand()
    {
      // Set up the event handlers
      if (Template != null)
      {
        var button = Template.FindName("PART_Button", this) as ButtonBase;
        if (button != null) button.Command = MainButtonCommand;
      }
    }

    private void SetMainButtonContent()
    {
      // Set up the event handlers
      if (Template != null)
      {
        var button = Template.FindName("PART_Button", this) as ButtonBase;
        if (button != null) button.Content = Content;
      }
    }

    #endregion
  }

  static class Extensions
  {
    public static string FormatWith(this string s, params object[] args)
    {
      return string.Format(s, args);
    }
  }

  public class PathTrimTextBlock : TextBlock, INotifyPropertyChanged
  {

    FrameworkElement _container;


    public PathTrimTextBlock()
    {
      this.Loaded += new RoutedEventHandler(PathTrimTextBlock_Loaded);
    }

    void PathTrimTextBlock_Loaded(object sender, RoutedEventArgs e)
    {
      if (this.Parent == null) throw new InvalidOperationException("PathTrimTextBlock must have a container such as a Grid.");

      _container = (FrameworkElement)this.Parent;
      _container.SizeChanged += new SizeChangedEventHandler(container_SizeChanged);

      Text = GetTrimmedPath(_container.ActualWidth);
    }

    void container_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      Text = GetTrimmedPath(_container.ActualWidth);
    }

    public string Path
    {
      get { return (string)GetValue(PathProperty); }
      set {
        SetValue(PathProperty, value);
        Text = (_container == null) ? Path : GetTrimmedPath(_container.ActualWidth);
      }
    }

    // Using a DependencyProperty as the backing store for Path.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PathProperty =
        DependencyProperty.Register("Path", typeof(string), typeof(PathTrimTextBlock), new UIPropertyMetadata(""));

    string GetTrimmedPath(double width)
    {
      try
      {
        string filename = System.IO.Path.GetFileName(Path);
        string directory = System.IO.Path.GetDirectoryName(Path);
        List<string> dirs = new List<string>();
        dirs.AddRange(directory.Split('\\'));
        List<string> dirLength = new List<string>();
        dirLength.AddRange(directory.Split('\\'));
        dirLength.RemoveAt(0);
        dirLength.Sort((x, y) => y.Length - x.Length);

        FormattedText formatted;
        bool widthOK = false;
        bool changedWidth = false;

        do
        {
          formatted = new FormattedText(
              @"{0}\{1}".FormatWith(directory, filename),
              CultureInfo.CurrentCulture,
              FlowDirection.LeftToRight,
              FontFamily.GetTypefaces().First(),
              FontSize,
              Foreground
              );

          widthOK = formatted.Width < width - 10;

          if (!widthOK)
          {
            changedWidth = true;
            directory = dirs[0];
            int count = 1;
            while (count < dirs.Count && dirLength.Count > 0)
            {
              if (dirs[count] == dirLength[0])
              {
                if (dirs[count].Length > 1)
                {
                  dirs[count] = dirs[count].Substring(0, dirs[count].Length - 1);
                  dirLength[0] = dirLength[0].Substring(0, dirLength[0].Length - 1);
                  directory += @"\" + dirs[count] + "...";
                  count++;
                  continue;
                }
                else
                {
                  dirLength.RemoveAt(0);
                  dirs[count] = "...";
                  //continue;
                }
              }

              if (dirs[count] == "..." && directory.Length > 3 && directory.Substring(directory.Length - 3, 3) == "...")
              {
                // do nothing
              }
              else
              {
                directory += @"\" + dirs[count];
              }

              count++;
            }
            if (directory.Length < dirs[0].Length + 2) return @"...\" + filename;
          }

        } while (!widthOK);

        if (!changedWidth)
        {
          return Path;
        }
        return @"{0}\{1}".FormatWith(directory, filename);
      }
      catch (Exception)
      {
        //Console.WriteLine(e.Message + "\r\n" + e.StackTrace);
        return Path;
      }
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    void RaisePropertyChanged(string name)
    {
      if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

    #endregion
  }

}
