OSDN Git Service

HPのパーセント表示を切り替えるのをダブルクリックにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / NotificationManager.cs
index ddbcb85..e9fdef1 100644 (file)
 using System;\r
 using System.Collections.Generic;\r
 using System.IO;\r
-using System.Windows.Forms;\r
+using System.Linq;\r
 \r
 namespace KancolleSniffer\r
 {\r
     public class NotificationManager\r
     {\r
         private readonly NotificationQueue _notificationQueue;\r
-        private readonly NotificationConfig _notificationConfig = new NotificationConfig();\r
 \r
-        public class Notification\r
+        private enum Mode\r
         {\r
-            public string Title;\r
-            public string Body;\r
-            public string Name;\r
+            Normal,\r
+            Repeat,\r
+            Cont,\r
+            Preliminary\r
         }\r
 \r
-        public NotificationManager(Action<string, string, string> ring)\r
+        private class Notification\r
         {\r
-            _notificationQueue = new NotificationQueue(ring);\r
+            public string Key { get; set; }\r
+            public int Fleet { get; set; }\r
+            public string Subject { get; set; }\r
+            public int Repeat { get; set; }\r
+            public Mode Mode { get; set; }\r
+            public DateTime Schedule { get; set; }\r
         }\r
 \r
-        public void Enqueue(string key, int fleet, string subject)\r
+        public NotificationManager(Action<string, string, string> alarm, Func<DateTime> nowFunc = null)\r
+        {\r
+            _notificationQueue = new NotificationQueue(alarm, nowFunc);\r
+        }\r
+\r
+        public void Enqueue(string key, int fleet, string subject, int repeat = 0, bool preliminary = false)\r
         {\r
-            _notificationConfig.LoadConfig();\r
-            var notification = _notificationConfig[key];\r
             _notificationQueue.Enqueue(new Notification\r
             {\r
-                Title = ProcessFormatString(notification.Title, fleet, subject),\r
-                Body = ProcessFormatString(notification.Body, fleet, subject),\r
-                Name = notification.Name\r
+                Key = key,\r
+                Fleet = fleet,\r
+                Subject = subject,\r
+                Repeat = repeat,\r
+                Mode = preliminary ? Mode.Preliminary : Mode.Normal\r
             });\r
         }\r
 \r
-        public void Enqueue(string key, string subject)\r
+        public void Enqueue(string key, string subject, int repeat = 0)\r
         {\r
-            Enqueue(key, 0, subject);\r
+            Enqueue(key, 0, subject, repeat);\r
         }\r
 \r
-        private string ProcessFormatString(string format, int fleet, string subject)\r
+        public void Flash()\r
         {\r
-            var fn = new[] {"第一艦隊", "第二艦隊", "第三艦隊", "第四艦隊"};\r
-            var result = "";\r
-            var percent = false;\r
-            foreach (var ch in format)\r
-            {\r
-                if (ch == '%')\r
-                {\r
-                    if (percent)\r
-                    {\r
-                        percent = false;\r
-                        result += '%';\r
-                    }\r
-                    else\r
-                    {\r
-                        percent = true;\r
-                    }\r
-                }\r
-                else if (percent)\r
-                {\r
-                    percent = false;\r
-                    switch (ch)\r
-                    {\r
-                        case 'f':\r
-                            result += fn[fleet];\r
-                            break;\r
-                        case 's':\r
-                            result += subject;\r
-                            break;\r
-                        default:\r
-                            result += '%'.ToString() + ch;\r
-                            break;\r
-                    }\r
-                }\r
-                else\r
-                {\r
-                    result += ch;\r
-                }\r
-            }\r
-            return result;\r
+            _notificationQueue.Flash();\r
+        }\r
+\r
+        public void StopRepeat(string key, bool cont = false)\r
+        {\r
+            _notificationQueue.StopRepeat(key, cont);\r
+        }\r
+\r
+        public void StopRepeat(string key, int fleet)\r
+        {\r
+            _notificationQueue.StopRepeat(key, fleet);\r
         }\r
 \r
+        public void SuspendRepeat()\r
+        {\r
+            _notificationQueue.SuspendRepeat();\r
+        }\r
+\r
+        public void ResumeRepeat()\r
+        {\r
+            _notificationQueue.ResumeRepeat();\r
+        }\r
+\r
+        public string KeyToName(string key) => NotificationConfig.KeyToName(key);\r
+\r
         private class NotificationConfig\r
         {\r
-            private readonly Dictionary<string, Notification> _config = new Dictionary<string, Notification>();\r
+            public class Message\r
+            {\r
+                public string Title { get; set; }\r
+                public string Body { get; set; }\r
+                public string Name { get; set; }\r
+            }\r
+\r
+            private readonly Dictionary<string, Message> _config = new Dictionary<string, Message>();\r
 \r
-            private readonly Dictionary<string, Notification> _default = new Dictionary<string, Notification>\r
+            private readonly Dictionary<string, Message> _default = new Dictionary<string, Message>\r
             {\r
                 {\r
-                    "遠征終了", new Notification\r
+                    "遠征終了", new Message\r
                     {\r
                         Title = "遠征が終わりました",\r
-                        Body = "%s",\r
-                        Name = "遠征終了"\r
+                        Body = "%f艦隊 %s"\r
                     }\r
                 },\r
                 {\r
-                    "入渠終了", new Notification\r
+                    "入渠終了", new Message\r
                     {\r
                         Title = "入渠が終わりました",\r
-                        Body = "%s",\r
-                        Name = "入渠終了"\r
+                        Body = "%fドック %s"\r
                     }\r
                 },\r
                 {\r
-                    "建造完了", new Notification\r
+                    "建造完了", new Message\r
                     {\r
                         Title = "建造が終わりました",\r
-                        Body = "%s",\r
-                        Name = "建造完了"\r
+                        Body = "%fドック"\r
                     }\r
                 },\r
                 {\r
-                    "艦娘数超過", new Notification\r
+                    "艦娘数超過", new Message\r
                     {\r
                         Title = "艦娘が多すぎます",\r
-                        Body = "%s",\r
-                        Name = "艦娘数超過"\r
+                        Body = "%s"\r
                     }\r
                 },\r
                 {\r
-                    "装備数超過", new Notification\r
+                    "装備数超過", new Message\r
                     {\r
                         Title = "装備が多すぎます",\r
-                        Body = "%s",\r
-                        Name = "装備数超過"\r
+                        Body = "%s"\r
                     }\r
                 },\r
                 {\r
-                    "大破警告", new Notification\r
+                    "大破警告", new Message\r
                     {\r
                         Title = "大破した艦娘がいます",\r
-                        Body = "%s",\r
-                        Name = "大破警告"\r
+                        Body = "%s"\r
                     }\r
                 },\r
                 {\r
-                    "泊地修理20分経過", new Notification\r
+                    "泊地修理20分経過", new Message\r
                     {\r
-                        Title = "泊地修理 %f",\r
-                        Body = "20分経過しました。",\r
-                        Name = "泊地修理20分経過"\r
+                        Title = "泊地修理 %f艦隊",\r
+                        Body = "20分経過しました。"\r
                     }\r
                 },\r
                 {\r
-                    "泊地修理進行", new Notification\r
+                    "泊地修理進行", new Message\r
                     {\r
-                        Title = "泊地修理 %f",\r
-                        Body = "修理進行:%s",\r
-                        Name = "泊地修理進行"\r
+                        Title = "泊地修理 %f艦隊",\r
+                        Body = "修理進行:%s"\r
                     }\r
                 },\r
                 {\r
-                    "泊地修理完了", new Notification\r
+                    "泊地修理完了", new Message\r
                     {\r
-                        Title = "泊地修理 %f",\r
-                        Body = "修理完了:%s",\r
-                        Name = "泊地修理完了"\r
+                        Title = "泊地修理 %f艦隊",\r
+                        Body = "修理完了:%s"\r
                     }\r
                 },\r
                 {\r
-                    "疲労回復40", new Notification\r
+                    "疲労回復40", new Message\r
                     {\r
                         Title = "疲労が回復しました",\r
-                        Body = "%f 残り9分",\r
-                        Name = "疲労回復"\r
+                        Body = "%f艦隊 残り9分"\r
                     }\r
                 },\r
                 {\r
-                    "疲労回復49", new Notification\r
+                    "疲労回復49", new Message\r
                     {\r
                         Title = "疲労が回復しました",\r
-                        Body = "%f",\r
-                        Name = "疲労回復"\r
+                        Body = "%f艦隊"\r
                     }\r
                 }\r
             };\r
 \r
-            public Notification this[string key] => _config.TryGetValue(key, out Notification value) ? value : _default[key];\r
+            public static string KeyToName(string key) => key.StartsWith("疲労回復") ? key.Substring(0, 4) : key;\r
 \r
-            public void LoadConfig()\r
+            private void LoadConfig()\r
             {\r
                 const string fileName = "notification.json";\r
                 _config.Clear();\r
@@ -205,11 +195,10 @@ namespace KancolleSniffer
                     {\r
                         if (!_default.ContainsKey(entry.key))\r
                             continue;\r
-                        _config[entry.key] = new Notification\r
+                        _config[entry.key] = new Message\r
                         {\r
                             Title = entry.title,\r
-                            Body = entry.body,\r
-                            Name = _default[entry.key].Name\r
+                            Body = entry.body\r
                         };\r
                     }\r
                 }\r
@@ -221,42 +210,155 @@ namespace KancolleSniffer
                     throw new Exception($"{fileName}に誤りがあります。: ${ex.Message}", ex);\r
                 }\r
             }\r
+\r
+            public Message GenerateMessage(Notification notification)\r
+            {\r
+                LoadConfig();\r
+                var format = _config.TryGetValue(notification.Key, out Message value)\r
+                    ? value\r
+                    : _default[notification.Key];\r
+                var prefix = new[] {"", "[リピート] ", "[継続] ", "[予告] "}[(int)notification.Mode];\r
+                return new Message\r
+                {\r
+                    Title = prefix + ProcessFormatString(format.Title, notification.Fleet, notification.Subject),\r
+                    Body = ProcessFormatString(format.Body, notification.Fleet, notification.Subject),\r
+                    Name = KeyToName(notification.Key)\r
+                };\r
+            }\r
+\r
+            private string ProcessFormatString(string format, int fleet, string subject)\r
+            {\r
+                var fn = new[] {"第一", "第二", "第三", "第四"};\r
+                var result = "";\r
+                var percent = false;\r
+                foreach (var ch in format)\r
+                {\r
+                    if (ch == '%')\r
+                    {\r
+                        if (percent)\r
+                        {\r
+                            percent = false;\r
+                            result += '%';\r
+                        }\r
+                        else\r
+                        {\r
+                            percent = true;\r
+                        }\r
+                    }\r
+                    else if (percent)\r
+                    {\r
+                        percent = false;\r
+                        switch (ch)\r
+                        {\r
+                            case 'f':\r
+                                result += fn[fleet];\r
+                                break;\r
+                            case 's':\r
+                                result += subject;\r
+                                break;\r
+                            default:\r
+                                result += '%'.ToString() + ch;\r
+                                break;\r
+                        }\r
+                    }\r
+                    else\r
+                    {\r
+                        result += ch;\r
+                    }\r
+                }\r
+                return result;\r
+            }\r
         }\r
 \r
         private class NotificationQueue\r
         {\r
-            private readonly Action<string, string, string> _ring;\r
-            private readonly Queue<Notification> _queue = new Queue<Notification>();\r
-            private readonly Timer _timer = new Timer {Interval = 2000};\r
+            private readonly Action<string, string, string> _alarm;\r
+            private readonly List<Notification> _queue = new List<Notification>();\r
+            private readonly Func<DateTime> _nowFunc = () => DateTime.Now;\r
+            private readonly NotificationConfig _notificationConfig = new NotificationConfig();\r
+            private DateTime _lastAlarm;\r
+            private bool _suspend;\r
 \r
-            public NotificationQueue(Action<string, string, string> ring)\r
+            public NotificationQueue(Action<string, string, string> alarm, Func<DateTime> nowFunc = null)\r
             {\r
-                _ring = ring;\r
-                _timer.Tick += TimerOnTick;\r
+                _alarm = alarm;\r
+                if (nowFunc != null)\r
+                    _nowFunc = nowFunc;\r
             }\r
 \r
-            private void TimerOnTick(object obj, EventArgs e)\r
+            public void Enqueue(Notification notification)\r
             {\r
-                if (_queue.Count == 0)\r
-                {\r
-                    _timer.Stop();\r
-                    return;\r
-                }\r
-                var notification = _queue.Dequeue();\r
-                _ring(notification.Title, notification.Body, notification.Name);\r
+                _queue.Add(notification);\r
             }\r
 \r
-            public void Enqueue(Notification notification)\r
+            public void Flash()\r
             {\r
-                if (_timer.Enabled)\r
+                Alarm();\r
+            }\r
+\r
+            public void StopRepeat(string key, bool cont = false)\r
+            {\r
+                if (!cont)\r
                 {\r
-                    _queue.Enqueue(notification);\r
+                    _queue.RemoveAll(n => IsMatch(n, key));\r
                 }\r
                 else\r
                 {\r
-                    _ring(notification.Title, notification.Body, notification.Name);\r
-                    _timer.Start();\r
+                    foreach (var n in _queue.Where(n => IsMatch(n, key)))\r
+                    {\r
+                        n.Subject = "";\r
+                        n.Mode = Mode.Cont;\r
+                    }\r
+                }\r
+            }\r
+\r
+            public void StopRepeat(string key, int fleet)\r
+            {\r
+                _queue.RemoveAll(n => IsMatch(n, key) && n.Fleet == fleet);\r
+            }\r
+\r
+            private bool IsMatch(Notification n, string key) =>\r
+                n.Key.Substring(0, 4) == key.Substring(0, 4) && n.Schedule != default;\r
+\r
+            public void SuspendRepeat()\r
+            {\r
+                _suspend = true;\r
+            }\r
+\r
+            public void ResumeRepeat()\r
+            {\r
+                _suspend = false;\r
+            }\r
+\r
+            private void Alarm()\r
+            {\r
+                var now = _nowFunc();\r
+                if (now - _lastAlarm < TimeSpan.FromSeconds(2))\r
+                    return;\r
+                var first = _queue.FirstOrDefault(n => n.Schedule.CompareTo(now) <= 0 &&\r
+                                                       !(_suspend && n.Schedule != default));\r
+                if (first == null)\r
+                    return;\r
+                var message = _notificationConfig.GenerateMessage(first);\r
+                var similar = _queue.Where(n =>\r
+                        _notificationConfig.GenerateMessage(n).Name == message.Name && n.Schedule.CompareTo(now) <= 0)\r
+                    .ToArray();\r
+                var body = string.Join("\r\n", similar.Select(n => _notificationConfig.GenerateMessage(n).Body));\r
+                foreach (var n in similar)\r
+                {\r
+                    if (n.Repeat == 0)\r
+                    {\r
+                        _queue.Remove(n);\r
+                    }\r
+                    else\r
+                    {\r
+                        n.Schedule = now + TimeSpan.FromSeconds(n.Repeat);\r
+                        if (n.Mode == Mode.Normal)\r
+                            n.Mode = Mode.Repeat;\r
+                    }\r
                 }\r
+                _alarm(message.Title, body, message.Name);\r
+                _lastAlarm = now;\r
             }\r
         }\r
     }\r