using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Azyobuzi.Twirunrun
{
    public abstract class TimelineTabPage : TabPage
    {
        public TimelineTabPage(TabInfo tabsettings)
        {
            TabSettings = tabsettings;
            this.Text = TabSettings.Name;
            Timeline.Dock = DockStyle.Fill;
            this.Controls.Add(Timeline);
            TabSettings.PropertyChanged += TabSettings_PropertyChanged;
            Timeline.SelectedIndexChanged += (sender, e) =>
            {
                if (Timeline.SelectedIndices.Count != 0)
                    this.GetMainForm().ShowDetailStatus(Timeline.Statuses[Timeline.SelectedIndices[0]]);
            };
            Timeline.DoubleClick += (sender, e) =>
            {
                if (Timeline.SelectedIndices.Count == 0) return;
                var selecting = Timeline.Statuses[Timeline.SelectedIndices[0]];
                this.GetMainForm().UpdateStatusPanel.SetReplyTo(
                     selecting.Type == StatusTypes.Retweet ?
                            selecting.Tweet.Retweet.ID : selecting.Id,
                    new[] { selecting.User.Identifier.ScreenName },
                    false
                );
            };
            RefreshTimer.Elapsed += RefreshTimer_Tick;
            if (TabSettings.RefreshInterval > 0)
            {
                RefreshTimer.Interval = TabSettings.RefreshInterval * 1000d;
                RefreshTimer.Start();
            }
        }

        public StatusesListView Timeline = new StatusesListView();

        protected System.Timers.Timer RefreshTimer = new System.Timers.Timer();

        public TabInfo TabSettings { private set; get; }

        private void TabSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            e.PropertyName.SelectCase()
                .Case("Name", () =>
                {
                    if (!refreshing) this.Text = TabSettings.Name;
                })
                .Case("RegfreshInterval", () =>
                {
                    if (TabSettings.RefreshInterval > 0)
                    {
                        RefreshTimer.Interval = TabSettings.RefreshInterval * 1000d;
                        RefreshTimer.Start();
                    }
                    else RefreshTimer.Stop();
                });
        }

        public abstract void RefreshTimeline(int page, int count, bool first);
        public virtual void RefreshTimeline(bool first)
        {
            RefreshTimeline(1, TabSettings.GetHits, first);
        }

        private void RefreshTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
        {
            this.GetMainForm().RefreshTimelines(new TimelineTabPage[] { this }, false);
        }

        private bool refreshing = false;

        /// <summary>
        /// 取得開始を通知します。
        /// </summary>
        protected void NotifyStarted()
        {
            refreshing = true;
            RefreshTimer.Stop();
            this.Invoke((MethodInvoker)(() => this.Text = "更新中..."));
            this.GetMainForm().StatusChange(TabSettings.Name + "を更新中...");
        }

        /// <summary>
        /// 取得完了を通知します。
        /// </summary>
        protected void NotifyCompleted()
        {
            this.GetMainForm().StatusChange(TabSettings.Name + "：取得しました");
            refreshing = false;
            this.Invoke((MethodInvoker)(() => this.Text = TabSettings.Name));
            if (TabSettings.RefreshInterval > 0) RefreshTimer.Start();
            this.GetMainForm().SetApiLimit();
        }

        /// <summary>
        /// 新着通知をします。設定によっては何もしない
        /// </summary>
        protected void NotifyNewItems(IEnumerable<StatusInfo> newItems)
        {
            if (TabSettings.NotifyNewTweet)
            {
                if (Settings.Instance.NotifyCount > 0)
                    newItems = newItems.Take(Settings.Instance.NotifyCount);
                this.GetMainForm().NotifyNewItems(newItems, TabSettings.Name + "の新着");
            }
        }

        #region ツイート操作
        [TweetOperator(OperatorName = "返信")]
        public void Reply()
        {
            if (!CanReply) return;
            if (Timeline.GetSelectedStatus().Type == StatusTypes.DirectMessage)
            {
                DirectMessage();
            }
            else
            {
                var selected = Timeline.GetSelectedStatuses();
                this.GetMainForm().UpdateStatusPanel.SetReplyTo(
                    selected.Length == 1 ? selected[0].Id : null,
                    selected.Select(_ => _.User.Identifier.ScreenName).ToArray(),
                    false
                );
            }
        }

        public bool CanReply
        {
            get
            {
                return Timeline.GetSelectedStatus() != null;
            }
        }

        [TweetOperator(OperatorName = "全員に返信")]
        public void ReplyAll()
        {
            if (!CanReplyAll) return;
            string replyTo = Timeline.GetSelectedStatuses().Length == 1 ?
                Timeline.GetSelectedStatus().Id : null;
            bool replyAll = !string.IsNullOrEmpty(replyTo);
            this.GetMainForm().UpdateStatusPanel.SetReplyTo(
                replyTo,
                Timeline.GetSelectedStatuses()
                    .SelectMany(_ =>
                        new[] { _.User.Identifier.ScreenName }.Concat(
                            _.Text.RegexMatches(@"[@＠]([a-zA-Z0-9_]+)")
                                .Cast<Match>()
                                .Select(match => match.Groups[1].ToString())
                        )
                    )
                    .Distinct()
                    .ToArray(),
                replyAll
            );
        }

        public bool CanReplyAll
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatuses().All(_ => _.Type != StatusTypes.DirectMessage);
            }
        }

        [TweetOperator(OperatorName = "DM作成")]
        public void DirectMessage()
        {
            if (!CanDirectMessage) return;
            this.GetMainForm().UpdateStatusPanel.Text =
                 string.Format("D {0} {1}",
                     Timeline.GetSelectedStatus().User.Identifier.ScreenName,
                     this.GetMainForm().UpdateStatusPanel.Text);
        }

        public bool CanDirectMessage
        {
            get
            {
                return Timeline.GetSelectedStatus() != null;
            }
        }

        [TweetOperator(OperatorName = "RT")]
        public void Retweet()
        {
            if (!CanRetweet) return;
            this.GetMainForm().StatusChange("RT実行中...");
            var missStatuses = new HashSet<Tuple<StatusInfo, Exception>>();
            Timeline.GetSelectedStatuses()
                .OrderBy(_ => _)
                .ForEach(tweet =>
                    ((Action)(() => this.GetMainForm().TwitterContext.Retweet(tweet.Id)))
                        .TryCatch(
                            ex => missStatuses.Add(Tuple.Create(tweet, ex))
                        )
                );
            if (missStatuses.Any())
                this.GetMainForm().StatusChange(string.Format("RT完了（{0}件失敗）", missStatuses.Count));
            else
                this.GetMainForm().StatusChange("RT完了");
        }

        public bool CanRetweet
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatuses().All(_ => _.Type != StatusTypes.DirectMessage);
            }
        }

        [TweetOperator(OperatorName = "QT")]
        public void Quote()
        {
            if (!CanQuote) return;
            var status = Timeline.GetSelectedStatus();
            this.GetMainForm().UpdateStatusPanel.Text +=
                string.Format(" QT @{1}: {2}", status.User.Identifier.ScreenName, status.Text);
        }

        public bool CanQuote
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatus().Type != StatusTypes.DirectMessage;
            }
        }

        [TweetOperator(OperatorName = "お気に入り登録")]
        public void AddFavorite()
        {
            if (!CanAddFavorite) return;
            this.GetMainForm().StatusChange("お気に入り登録中...");
            var missStatuses = new HashSet<Tuple<StatusInfo, Exception>>();
            Timeline.GetSelectedStatuses().AsParallel()
                .ForAll(tweet =>
                    ((Action)(() => this.GetMainForm().TwitterContext.CreateFavorite(tweet.Id)))
                        .TryCatch(
                            ex => missStatuses.Add(Tuple.Create(tweet, ex))
                        )
                );
            if (missStatuses.Any())
                this.GetMainForm().StatusChange(string.Format("Fav完了（{0}件失敗）", missStatuses.Count));
            else
                this.GetMainForm().StatusChange("Fav完了");
        }

        public bool CanAddFavorite
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatuses().All(_ => _.Type != StatusTypes.DirectMessage);
            }
        }

        [TweetOperator(OperatorName = "お気に入り登録＆RT")]
        public void FavoriteAndRetweet()
        {
            if (!CanFavoriteAndRetweet) return;
            Retweet();
            AddFavorite();
        }

        public bool CanFavoriteAndRetweet
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatuses().All(_ => _.Type != StatusTypes.DirectMessage);
            }
        }

        [TweetOperator(OperatorName = "お気に入り解除")]
        public void RemoveFavorite()
        {
            if (!CanRemoveFavorite) return;
            this.GetMainForm().StatusChange("お気に入り解除中...");
            var missStatuses = new HashSet<Tuple<StatusInfo, Exception>>();
            Timeline.GetSelectedStatuses().AsParallel()
                .ForAll(tweet =>
                    ((Action)(() => this.GetMainForm().TwitterContext.DestroyFavorite(tweet.Id)))
                        .TryCatch(
                            ex => missStatuses.Add(Tuple.Create(tweet, ex))
                        )
                );
            if (missStatuses.Any())
                this.GetMainForm().StatusChange(string.Format("Fav解除完了（{0}件失敗）", missStatuses.Count));
            else
                this.GetMainForm().StatusChange("Fav解除完了");
        }

        public bool CanRemoveFavorite
        {
            get
            {
                return Timeline.GetSelectedStatus() != null &&
                    Timeline.GetSelectedStatuses().All(_ => _.Type != StatusTypes.DirectMessage);
            }
        }
        #endregion
    }

    [AttributeUsage(AttributeTargets.Method)]
    internal class TweetOperatorAttribute
        : Attribute
    {
        public string OperatorName { set; get; }
    }
}
