using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Browser;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace ynhtm.Upfile.Lib
{
    public class Item : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public event EventHandler<CancelEventArgs> Click;
        public event EventHandler Delete;
        public event EventHandler Done;
        public event EventHandler Error;

        public Item(string path, long files)
        {
            var source = new BitmapImage();
            this.Status = ItemStatus.Done;
            var n = path;
            var m = Regex.Match(path, @"([^/]+)(/|$)", RegexOptions.RightToLeft);
            if (m.Success) n = m.Result("$1");
            this.Name = n;
            this.Info = files + " 个对象";
            this.ItemType = ItemType.Dir;
            this.Url = new Uri(path, UriKind.Relative);
            this.Source = source;
            source.UriSource = new Uri("./Images/Dir.png", UriKind.Relative);
        }

        public Item(Uri url, long length)
        {
            var source = new BitmapImage();
            this.Status = ItemStatus.Done;
            this.Name = Path.GetFileName(url.ToString());
            this.Info = Utils.GetFileSize(length);
            this.ItemType = ItemType.File;
            this.Url = url;
            this.Source = source;
            if (Utils.IsPic(this.Name))
            {
                source.DownloadProgress += (object sender, DownloadProgressEventArgs e) =>
                {
                    this.Text = e.Progress + "%";
                };
                source.ImageFailed += (object sender, ExceptionRoutedEventArgs e) =>
                {

                };
                source.ImageOpened += (object sender, RoutedEventArgs e) =>
                {
                    this.Text = null;
                    this.Stretch = Stretch.Uniform;
                };
                source.UriSource = new Uri(url, "/API/Thumb.ashx?Size=150%2c150&Url=" + url.ToString().UrlEncode());
            }
            else
                source.UriSource = new Uri("./Images/File.png", UriKind.Relative);
        }

        public Item(FileInfo file, Dispatcher dispatcher)
        {
            var source = new BitmapImage() { CreateOptions = BitmapCreateOptions.IgnoreImageCache };
            this.Name = file.Name;
            this.Info = Utils.GetFileSize(file.Length);
            this.ItemType = ItemType.File;
            this.Source = source;
            this.Text = "等待上传";
            var stream = file.OpenRead();
            this.Stream = stream;
            this.Dispatcher = dispatcher;
            if (Utils.IsPic(this.Name))
            {
                source.SetSource(stream);
                this.Stretch = Stretch.Uniform;
            }
            else
                source.UriSource = new Uri("./Images/File.png", UriKind.Relative);
        }

        ItemType itemType;
        public ItemType ItemType
        {
            get { return this.itemType; }
            private set
            {
                if (this.itemType == value) return;
                this.itemType = value;
                this.OnPropertyChanged("ItemType");
            }
        }

        Uri url;
        public Uri Url
        {
            get { return this.url; }
            private set
            {
                if (this.url == value) return;
                this.url = value;
                this.OnPropertyChanged("Url");
            }
        }

        string name;
        public string Name
        {
            get { return this.name; }
            private set
            {
                if (this.name == value) return;
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }

        BitmapImage source;
        public BitmapImage Source
        {
            get { return this.source; }
            private set
            {
                if (this.source == value) return;
                this.source = value;
                this.OnPropertyChanged("Source");
            }
        }

        Stretch stretch;
        public Stretch Stretch
        {
            get { return this.stretch; }
            private set
            {
                if (this.stretch == value) return;
                this.stretch = value;
                this.OnPropertyChanged("Stretch");
            }
        }

        string info;
        public string Info
        {
            get { return this.info; }
            private set
            {
                if (this.info == value) return;
                this.info = value;
                this.OnPropertyChanged("Info");
            }
        }

        ItemStatus status;
        public ItemStatus Status
        {
            get { return this.status; }
            internal set
            {
                if (this.status == value) return;
                this.status = value;
                this.OnPropertyChanged("Status");
            }
        }

        protected Stream Stream { get; private set; }

        protected Dispatcher Dispatcher { get; private set; }

        string text;
        public string Text
        {
            get { return this.text; }
            private set
            {
                if (this.text == value) return;
                this.text = value;
                this.OnPropertyChanged("Text");
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        internal virtual void OnClick(CancelEventArgs args)
        {
            if (this.Click != null) this.Click(this, args);
        }

        internal virtual void OnDelete()
        {
            if (this.Delete != null) this.Delete(this, EventArgs.Empty);
        }

        internal virtual void OnDone()
        {
            if (this.Done != null) this.Done(this, EventArgs.Empty);
        }

        internal virtual void OnError()
        {
            if (this.Error != null) this.Error(this, EventArgs.Empty);
        }

        public virtual void Upfile(string path, string cookieHeader, int picMask, int rename)
        {
            if (this.Status != ItemStatus.None) return;
            var stream = this.Stream;
            var len = stream.Length; // 将长度保存为变量，内容长度和计算进度时有用
            var url = new Uri(HtmlPage.Document.DocumentUri, "/API/Upfile.ashx?Action=Upfile&FileName=" + path.UrlEncode() + "/" + this.Name.UrlEncode() + "&Length=" + len + "&PicMask=" + picMask + "&Rename=" + rename);
            this.Status = ItemStatus.Post;
            var request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(url);
            /* 伪造Cookie发送 */
            var cookie = new CookieContainer();
            foreach (var i in cookieHeader.Split(';')) cookie.SetCookies(url, i);
            request.CookieContainer = cookie; // 设置伪造好的Cookie
            request.ContentLength = len; // 文件流长度
            request.AllowWriteStreamBuffering = false; // 允许写入流时缓存，因为要写入多次就上传多少，而不要缓存，所以：false
            request.Method = "POST";
            request.BeginGetRequestStream((IAsyncResult ar) =>
            {
                try
                {
                    using (var writer = request.EndGetRequestStream(ar))
                    {
                        stream.Seek(0, SeekOrigin.Begin);
                        var count = 0;
                        var per = len * 0.01;
                        var send = 0L;
                        var buffer = new byte[30720];
                        while ((count = stream.Read(buffer, 0, 30720)) != 0)
                        {
                            if (this.Status != ItemStatus.Post)
                            {
                                request.Abort();
                                return;
                            }
                            writer.Write(buffer, 0, count);
                            writer.Flush();
                            send += count;
                            this.Dispatcher.BeginInvoke(delegate() { this.Text = Math.Min((int)(send / per), 100) + "%"; });
                        }
                    }
                    request.BeginGetResponse(this.UpfileCallback, request);
                }
                catch
                {
                    if (this.Status != ItemStatus.Canceled)
                    {
                        this.Dispatcher.BeginInvoke(delegate()
                        {
                            this.Status = ItemStatus.Error;
                            this.Text = "上传失败";
                            this.OnError();
                        });
                    }
                }
            }, null);
        }

        void UpfileCallback(IAsyncResult ar)
        {
            try
            {
                var request = (HttpWebRequest)ar.AsyncState;
                var response = (HttpWebResponse)request.EndGetResponse(ar);
                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    var url = new Uri(reader.ReadToEnd());
                    this.Dispatcher.BeginInvoke(delegate()
                    {
                        this.Status = ItemStatus.Done;
                        this.Name = Path.GetFileName(url.ToString());
                        this.Url = url;
                        this.Text = null;
                        this.OnDone();
                    });
                }
            }
            catch
            {
                if (this.Status != ItemStatus.Canceled)
                {
                    this.Dispatcher.BeginInvoke(delegate()
                    {
                        this.Status = ItemStatus.Error;
                        this.Text = "上传失败";
                        this.OnError();
                    });
                }
            }
        }
    }
}
