/// <summary>
 /// 返回所有的事件记录中含有比 rect 的日期还要早的记录。
 /// </summary>
 /// <param name="entries">存储事件链的模型类</param>
 /// <param name="rect">用于日期比较的目标方块</param>
 /// <returns></returns>
 private static List<StatistTotalByDateTime> EarlierThanEarliestRectangle(List<StatistTotalByDateTime> entries, Rectangle rect) {
     List<StatistTotalByDateTime> earlierThanEarliestRectangleLik = new List<StatistTotalByDateTime>();
     foreach (var item in entries) {
         if (item.DateTime < DatetimeParser.ParseExpressToDateTime(rect.Name, DateMode.DateWithSlash)) {
             earlierThanEarliestRectangleLik.Add(item);
         }
     }
     return earlierThanEarliestRectangleLik;
 }
 /// <summary>
 /// 该构造器接收一个字符串序列,把它转换成StatistTotalByDateTime链表,同时接收一个 DateMode 指示日期字符串的分割方式
 /// </summary>
 /// <param name="lines">文本序列</param>
 /// <param name="dateMode">指示日期字符串的分割方式</param>
 public StatistTotalByDateTimeModel(IEnumerable <string> lines, DateMode dateMode)
 {
     foreach (var line in lines)
     {
         if (line != "" && line != "\r")     // 忽略空行
         {
             StatistTotalByDateTime statist = DatetimeParser.ParseExpressToStatistTotalByDateTime(line, dateMode);
             this.Add(statist);
         }
     }
 }
        /// <summary>
        /// 打开文件选取器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void OpenFileButtonAsync_Click(object sender, RoutedEventArgs e)
        {
            FileOpenPicker openPicker = new FileOpenPicker {
                ViewMode = PickerViewMode.Thumbnail,
                SuggestedStartLocation = PickerLocationId.ComputerFolder
            };

            openPicker.FileTypeFilter.Add(".txt");
            openPicker.FileTypeFilter.Add(".mast");

            _file = await openPicker.PickSingleFileAsync();

            if (_file != null)
            {
                ProgressBoard progressBoard = new ProgressBoard();
                ProgressBoard.SlideOn(CurrentRectanglesCanvas, progressBoard);
                ResetRectangle();  // 每次选择文件之后都要重置方块颜色

                Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(_file);
                string text = await FileIO.ReadTextAsync(_file);

                try {
                    IEnumerable <string> lines = DatetimeParser.SplitByLine(text);
                    _model = new StatistTotalByDateTimeModel(lines);
                    ExtendStackCanvasByFilterOldRecorders(EarlierThanEarliestRectangle(_model.ToStatistTotalByDateTimeArray().ToList(), _earliestRectangle), _earliestRectangle);
                    ProgressBoard.CancelOn(CurrentRectanglesCanvas, progressBoard);
                    foreach (Canvas canvas in StackCanvas.Children)
                    {
                        ProgressBoard.SlideOn(canvas, new ProgressBoard());
                    }
                    TioSalamanca[] res = _model.GroupDateTimesByTotal();
#if DEBUG
                    for (int level = 0; level < res.Length; level++)
                    {
                        Debug.WriteLine($"level: {level + 1}");
                        Debug.WriteLine($"  List res[{level}]:");
                        foreach (var group in res[level])
                        {
                            Debug.WriteLine($"    Total: {group.Key}");
                            foreach (var item in group)
                            {
                                Debug.WriteLine($"      {item}");
                            }
                        }
                    }
#endif
                    DrawRectangleColor(res, false);
                }
                catch (ArgumentException err) {
                    PopErrorDialogAsync(err.Message);
                }
                _saveMode = SaveMode.OrginalFile; // 表示当前的操作基于磁盘上已有的文件
            }
        }
 /// <summary>
 /// 筛选出没有在当前 RectanglesCanvas 面板中显示的、日期更久远的记录,
 /// 然后根据这些记录在 StackCanvas 面板中生成新的方块面板,接着在这些
 /// 新生成的面板中着色日期更久远的记录。
 /// </summary>
 private void ExtendStackCanvasByFilterOldRecorders(List<StatistTotalByDateTime> oldRecorders, Rectangle earliestRectangle, int canvasOrdinal = 1) {
     Canvas oldRectanglesCanvas = new Canvas() {
         Name = $"OldRectanglesCanvas_{canvasOrdinal}"
     };
     Rectangle oldRect = this.RectanglesLayout(oldRectanglesCanvas, DatetimeParser.ParseExpressToDateTime(earliestRectangle.Name, DateMode.DateWithSlash).AddDays(-1));
     DateTag(oldRectanglesCanvas);
     this.StackCanvas.Children.Insert(0, oldRectanglesCanvas);
     List<StatistTotalByDateTime> newOldRecorders = EarlierThanEarliestRectangle(oldRecorders, oldRect);
     if (newOldRecorders.Count > 0) {
         ExtendStackCanvasByFilterOldRecorders(newOldRecorders, oldRect, canvasOrdinal + 1);
     }
 }
 /// <summary>
 /// 添加一个条目
 /// </summary>
 /// <param name="rectName">接收一个字符串表达式,格式为“mm dd yyyy x{Total}”或“mm/dd/yyyy”</param>
 public void AddEntry(string rectName)
 {
     this.AddEntry(DatetimeParser.ParseExpressToStatistTotalByDateTime(rectName, DateMode.DateWithSlash));
 }
        /// <summary>
        /// 给相应的 Rectangle 标记上日期和星期数
        /// </summary>
        /// <param name="canvas"></param>
        private static void DateTag(Canvas canvas) {
            /*
             * 下面的查询语句用来提取画布中最左侧的一列方块(res1)和最上侧的一行方块(res2),
             * 然后将这些方块标记上星期数和月份
             */
            IEnumerable<IGrouping<double, UIElement>> leftGroup = from rect in canvas.Children
                                                                  group rect by Canvas.GetLeft(rect);
            IEnumerable<IGrouping<double, UIElement>> topGroup = from rect in canvas.Children
                                                                 group rect by Canvas.GetTop(rect);
            double minLeftGroupKey = leftGroup.Min(group => group.Key);
            double minTopGroupKey = topGroup.Min(group => group.Key);
            IGrouping<double, UIElement> res1 = (from g in leftGroup
                                                 where g.Key == minLeftGroupKey
                                                 select g).First();
            IGrouping<double, UIElement> res2 = (from g in topGroup
                                                 where g.Key == minTopGroupKey
                                                 select g).First();

            /*
             * 给周一、周三、周五的方块打上标记 Mon、Wed、Fri
             */
            foreach (Rectangle rect in res1) {
                if (DatetimeParser.ParseExpressToDateTime((rect as Rectangle).Name, DateMode.DateWithSlash).DayOfWeek == DayOfWeek.Monday ||
                    DatetimeParser.ParseExpressToDateTime((rect as Rectangle).Name, DateMode.DateWithSlash).DayOfWeek == DayOfWeek.Wednesday ||
                    DatetimeParser.ParseExpressToDateTime((rect as Rectangle).Name, DateMode.DateWithSlash).DayOfWeek == DayOfWeek.Friday) {
                    var tbx = new TextBlock() {
                        Text = DatetimeParser.ParseExpressToDateTime((rect as Rectangle).Name, DateMode.DateWithSlash).DayOfWeek.ToString().Substring(0, 3),
                        FontSize = 10,
                        Foreground = new SolidColorBrush(Windows.UI.Colors.Gray)
                    };
                    Canvas.SetLeft(tbx, Canvas.GetLeft(rect) - 30);
                    Canvas.SetTop(tbx, Canvas.GetTop(rect));
                    canvas.Children.Add(tbx);
                }
            }

            /*
             * 给每个月份开头的方块打上标记,从 Jan 到 Dec
             */
            Rectangle previous = null;
            foreach (Rectangle rect in res2.Reverse()) {
                void setTopTag(string text) {
                    var tbx = new TextBlock() {
                        Text = text,
                        FontSize = 10,
                        Foreground = new SolidColorBrush(Windows.UI.Colors.Gray)
                    };
                    Canvas.SetLeft(tbx, Canvas.GetLeft(rect));
                    Canvas.SetTop(tbx, Canvas.GetTop(rect) - 15);
                    canvas.Children.Add(tbx);
                }
                if (previous == null) {
                    setTopTag(DatetimeParser.NumberToMonth(DatetimeParser.ParseExpressToDateTime(rect.Name, DateMode.DateWithSlash).Month));
                }
                else {
                    int monthOfPreviousRectangle = DatetimeParser.ParseExpressToDateTime(previous.Name, DateMode.DateWithSlash).Month;
                    int monthOfCurrentRectangle = DatetimeParser.ParseExpressToDateTime(rect.Name, DateMode.DateWithSlash).Month;
                    if (monthOfCurrentRectangle != monthOfPreviousRectangle) {
                        setTopTag(DatetimeParser.NumberToMonth(DatetimeParser.ParseExpressToDateTime(rect.Name, DateMode.DateWithSlash).Month));
                    }
                }
                previous = rect;
            }
        }