using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LinqToTwitter;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Reflection;
using Azyobuzi.UserStreamEx;
using System.Text.RegularExpressions;

namespace Azyobuzi.Twirunrun
{
    public partial class MainForm : Form, ITwirunrunMainForm
    {
        public MainForm()
        {
            InitializeComponent();

            返信ToolStripMenuItem.Click += GetTweetOperator("Reply");
            全員に返信ToolStripMenuItem.Click += GetTweetOperator("ReplyAll");
            rTToolStripMenuItem.Click += GetTweetOperator("Retweet");
            qTToolStripMenuItem.Click += GetTweetOperator("Quote");
            お気に入り登録ToolStripMenuItem.Click += GetTweetOperator("AddFavorite");
            お気に入り登録RTToolStripMenuItem.Click += GetTweetOperator("FavoriteAndRetweet");
            お気に入り解除ToolStripMenuItem.Click += GetTweetOperator("RemoveFavorite");
        }

        private EventHandler GetTweetOperator(string name)
        {
            var method = typeof(TimelineTabPage).GetMethods().Single(_ => _.Name == name && _.GetCustomAttributes(false).OfType<TweetOperatorAttribute>().Any());
            return (sender, e) =>
            {
                if (timelineTabs.SelectedTab != null)
                    method.Invoke(timelineTabs.SelectedTab, null);
            };
        }

        private string statusBrowserUri = "file:///" + (Application.StartupPath + @"\html\resultPage.html").Replace('\\', '/');

        TwitterContext twCtx = new TwitterContext();
        UserStreamEx.UserStream us;
        
        TwitterContext ITwirunrunMainForm.TwitterContext
        {
            get
            {
                return twCtx;
            }
        }

        UpdateStatusPanel ITwirunrunMainForm.UpdateStatusPanel
        {
            get
            {
                return updateStatusPanel1;
            }
        }

        private Version GetVersion()
        {
            return Assembly.GetExecutingAssembly().GetName().Version;
        }

        private void IniOAuth()
        {
            var auth = new EncryptedFileAuthorizer()
            {
                UserAgent = "Twirunrun v" + GetVersion().ToString()
            };
            auth.Authorize();

            twCtx.AuthorizedClient = auth;

            us = new UserStreamEx.UserStream(auth);
            us.Started += us_Started;
            us.Stopped += us_Stopped;
            us.NewTweet += us_NewTweet;
            us.NewDirectMessage += us_NewDirectMessage;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            Icons.AddCompleted =
                () => ((TimelineTabPage)timelineTabs.SelectedTab).Timeline.Refresh();
            IniOAuth();

            #region タブ初期化
            if (Settings.Instance.Tabs.Count == 0)
                Settings.Instance.Tabs.AddRange(
                    new[]
                    {
                        new TabInfo()
                        {
                            Name = "Home",
                            Type = TabTypes.Home,
                            Deletable = false,
                            NotifyNewTweet = true,
                            RefreshInterval = 90,
                            PostGet = true,
                            GetHits = 60
                        },
                        new TabInfo()
                        {
                            Name = "Mentions",
                            Type = TabTypes.Mentions,
                            Deletable = false,
                            NotifyNewTweet = true,
                            RefreshInterval = 180,
                            GetHits = 30
                        },
                        new TabInfo()
                        {
                            Name = "DirectMessages",
                            Type = TabTypes.DirectMessages,
                            Deletable = false,
                            NotifyNewTweet = true,
                            RefreshInterval = 300,
                            GetHits = 30
                        }
                    }
                );
            #endregion
            timelineTabs.TabPages.AddRange(Settings.Instance.Tabs.Select(tab => tab.CreateTabPage(timelineTabs)).ToArray());

            RecentStatusesCollection.Instance.CollectionChanged += (_sender, _e) =>
            {
                var newAtHashtagList = Settings.Instance.AtHashtagList.Concat(
                        _e.NewItems.Cast<StatusInfo>().SelectMany(_ =>
                            new[] { "@" + _.User.Identifier.ScreenName }
                            .Concat(_.Text.RegexMatches(@"[@＠][a-zA-Z0-9_]+")
                                .Cast<Match>()
                                .Select(atMatch => atMatch.ToString())
                                .Concat(_.Text.RegexMatches(@"(^|[^\w&])([#＃][a-zA-Z0-9_\-]+)")
                                    .Cast<Match>()
                                    .Select(hashtagMatch => hashtagMatch.Groups[2].ToString())
                                )
                            )
                        )
                    )
                    .Select(_ =>
                    {
                        if (_.FirstOrDefault() == '＠')
                            return "@" + _.Substring(1);
                        else if (_.FirstOrDefault() == '＃')
                            return "#" + _.Substring(1);
                        else
                            return _;
                    })
                    .Distinct()
                    .ToArray();
                Array.Sort(newAtHashtagList);
                Settings.Instance.AtHashtagList = newAtHashtagList;
            };

            Settings.Instance.PropertyChanged += Settings_PropertyChanged;
            Settings.Instance.RaiseAllEvents();

            statusBrowser1.Navigate(statusBrowserUri);
        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
            RefreshTimelines(GetTabPages(), true);
        }

        public IEnumerable<TimelineTabPage> GetTabPages()
        {
            return timelineTabs.TabPages.OfType<TimelineTabPage>();
        }

        public void RefreshTimelines(IEnumerable<TimelineTabPage> tabs, bool first)
        {
            var t = new Thread(() =>
                tabs.AsParallel().ForAll(tab =>
                    ((Action)(() => tab.RefreshTimeline(first)))
                    .TryCatch(ex => ShowError(tab, ex))
                ));
            t.IsBackground = true;
            t.Start();
        }

        private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var s = (Settings)sender;
            e.PropertyName.SelectCase()
                .Case("PostPanelPosition", () =>
                {
                    updateStatusPanel1.Dock = s.PostPanelPosition;
                    postPanelSplitter.Dock = s.PostPanelPosition;
                })
                .Case("TabAlignment", () => timelineTabs.Alignment = s.TabAlignment)
                .Case("PostPanelHeight", () => updateStatusPanel1.Height = s.PostPanelHeight)
                .Case("StatusBrowserHeight", () => statusBrowser1.Height = s.StatusBrowserHeight)
                .Case("StreamingEnable", () =>
                {
                    if (s.StreamingEnable)
                        us.Start(null, false);
                    else
                        us.Stop();
                })
                .Case("MainFormSetting", () => s.MainFormSetting.Restore(this));
        }

        private void updateStatusPanel1_RunUpdateStatus(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(updateStatusPanel1.Text))
            {
                var t = new Thread(_ =>
                {
                    StatusChange("投稿中...");
                    var args = (Tuple<string, string>)_;
                    var status = twCtx.UpdateStatus(args.Item1, args.Item2);
                    StatusChange("投稿完了");
                    RefreshTimelines(GetTabPages().Where(tab => tab.TabSettings.PostGet), false);
                });
                t.IsBackground = true;
                t.Start(Tuple.Create(updateStatusPanel1.Text, updateStatusPanel1.ReplyToId));

                updateStatusPanel1.Clear();
            }
            else
                RefreshTimelines(GetTabPages().Where(tab => tab.TabSettings.PostGet), false);
        }

        public void StatusChange(string status)
        {
            this.Invoke((MethodInvoker)(() => statusLabel.Text = status));
        }

        private void 終了XToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        public void ShowDetailStatus(StatusInfo status)
        {
            statusBrowser1.Scripts.ShowStatus(status);
        }

        private void statusBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
        {
            if (!e.Url.AbsoluteUri.Equals(Uri.EscapeUriString(statusBrowserUri), StringComparison.InvariantCultureIgnoreCase))
            {
                e.Cancel = true;
                var runExtension = Previewer.GetPreviewExtensions().FirstOrDefault(_ => _.IsUsable(e.Url.ToString()));
                if (runExtension != null)
                {
                    var t = new Thread(() =>
                    {
                        StatusChange("プレビュー取得中...");
                        var uri = e.Url.ToString();
                        var result = runExtension.GetPreviewData(uri);
                        StatusChange("プレビュー取得完了");
                        result.Item1.SelectCase()
                            .Case(PreviewDataTypes.Image, () =>
                            {
                                using (var f = new PictureViewer())
                                {
                                    f.PictureBox.Image = result.Item2 as Image;
                                    f.Text = uri;
                                    f.PictureBox.DoubleClick += (_sender, _e) => Process.Start(uri);
                                    f.ShowDialog();
                                }
                            })
                            .Case(PreviewDataTypes.Text, () =>
                            {
                                using (var f = new TextViewer())
                                {
                                    f.Txtbox.Text = result.Item2 as string;
                                    f.Text = uri;
                                    f.Txtbox.DoubleClick += (_sender, _e) => Process.Start(uri);
                                    f.ShowDialog();
                                }
                            });
                    });
                    t.Start();
                }
                else
                {
                    Process.Start(e.Url.ToString());
                }
            }
        }

        private bool closing = false;

        public new void Close()
        {
            closing = true;
            base.Close();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = e.CloseReason == CloseReason.UserClosing && !closing;

            if (e.Cancel)
            {
                this.Hide();
            }
            else
            {
                //設定保存
                var s = Settings.Instance;
                s.PostPanelHeight = updateStatusPanel1.Height;
                s.StatusBrowserHeight = statusBrowser1.Height;
            }
        }

        public void ShowBalloon(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
        {
            notifyIcon1.ShowBalloonTip(timeout, tipTitle, tipText, tipIcon);
        }
        
        public void ShowError(TimelineTabPage tabPage, Exception exception)
        {
            string message;
            Exception ex =
                exception.InnerException == null && !(exception.InnerException is TwitterQueryException)
                ? exception : exception.InnerException;
            var twitterex = ex as TwitterQueryException;
            if (twitterex != null)
            {
                if (twitterex.Response.Error == "See Inner Exception Details for more information." ||
                    twitterex.Response.Error == "error message not received from Twitter")
                {
                    var webex = twitterex.InnerException as WebException;
                    if (webex == null)
                    {
                        message = "不明なエラー";
                    }
                    else
                    {
                        var statuscode = ((HttpWebResponse)webex.Response).StatusCode;
                        message = string.Format("{0} {1}", (int)statuscode, statuscode.ToString());
                    }
                }
                else
                {
                    message = twitterex.Response.Error;
                }
            }
            else
            {
                message = ex.Message;
            }
            StatusChange(string.Format("{0}：{1}", tabPage.TabSettings.Name, message));
        }

        public void NotifyNewItems(IEnumerable<StatusInfo> newItems, string title)
        {
            this.Invoke((MethodInvoker)(() =>
            {
                    Settings.Instance.NotifyType.SelectCase()
                        .Case(NotifyTypes.NotifyForm,
                            () => NotifyForm.ShowItems(newItems, this))
                        .Case(NotifyTypes.Balloon,
                            () => ShowBalloon(
                                Settings.Instance.NotifyFormShowTime * 1000,
                                title,
                                string.Join(Environment.NewLine, newItems.Select(item =>
                                    string.Format("{0}：{1}", Settings.Instance.NameType == NameTypes.Name ?
                                        item.User.Name : item.User.Identifier.ScreenName,
                                        item.Text)
                                )),
                                ToolTipIcon.Info
                            )
                        );
            }));
        }

        private void us_Started(object sender, EventArgs e)
        {
            this.Invoke((MethodInvoker)(() =>
            {
                StatusChange("Streaming start");
                streamingStateToolStripMenuItem.Text = "Started";
                startStopToolStripMenuItem.Text = "&Stop";
            }));
        }

        private void us_Stopped(object sender, StoppedEventArgs e)
        {
            this.Invoke((MethodInvoker)(() =>
            {
                StatusChange("Streaming stop");
                Settings.Instance.StreamingEnable = false;
                streamingStateToolStripMenuItem.Text = "Stopped";
                startStopToolStripMenuItem.Text = "&Start";
                if (e.Reason != StopReason.UserStop && Settings.Instance.AutoRestart)
                    us.Start(null, false);
            }));
        }

        private void us_NewTweet(object sender, NewTweetEventArgs e)
        {
            var newItem = new StatusInfo(e.Status);
            lock (RecentStatusesCollection.Instance)
            {
                if (!RecentStatusesCollection.Instance.AsParallel().Contains(newItem))
                    RecentStatusesCollection.Instance.Add(newItem);
            }
            if (Settings.Instance.StreamingNotifyNewTweet)
                NotifyNewItems(new[] { newItem }, "Streaming 新着");
        }

        private void us_NewDirectMessage(object sender, NewDirectMessageEventArgs e)
        {
            var newItem = new StatusInfo(e.DirectMessage);
            lock (RecentStatusesCollection.Instance)
            {
                if (!RecentStatusesCollection.Instance.AsParallel().Contains(newItem))
                    RecentStatusesCollection.Instance.Add(newItem);
            }
            if (Settings.Instance.StreamingNotifyNewTweet)
                NotifyNewItems(new[] { newItem }, "Streaming 新着DM");
        }

        private void startStopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Settings.Instance.StreamingEnable = startStopToolStripMenuItem.Text == "&Start";
        }

        private void notifyIcon1_Click(object sender, EventArgs e)
        {
            this.Visible = true;
        }

        private void MainForm_BoundsChanged(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Normal)
                Settings.Instance.MainFormSetting = FormSettings.FromForm(this);
            else
                Settings.Instance.MainFormSetting.WindowState = this.WindowState;
        }

        public void SetApiLimit()
        {
            this.Invoke((Action)(() =>
            {
                apiLimitLabel.Text = string.Format("API:{0}/{1}",  twCtx.RateLimitRemaining, twCtx.RateLimitCurrent);
                apiLimitLabel.ToolTipText =
                    "リセット時刻：" +
                    new DateTime(1970, 1, 1, 0, 0, 0, 0)
                        .AddSeconds(twCtx.RateLimitReset)
                        .ToLocalTime()
                        .ToShortTimeString();
            }));
        }
    }
}
