using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security;
using System.Text.RegularExpressions;
using System.Web;

namespace ynhtm.API
{
    using Common;

    public class Upfile : BaseHandler
    {
        void PathCheck(string path)
        {
            if (!Regex.IsMatch(path, "^" + BLL.Setting.UPFILEPATH + "/", RegexOptions.IgnoreCase | RegexOptions.Compiled)) throw new SecurityException();
        }

        string PathSafeEncode(string path)
        {
            if (path.IsNullOrWhiteSpace()) return path;
            if (Regex.IsMatch(path, @"\.+(\\|/)", RegexOptions.Compiled)) throw new SecurityException();
            return Utils.PathEncode(path);
        }

        bool IsDir(FileSystemInfo info)
        {
            return (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
        }

        public override void Process()
        {
            var user = this.User;
            var id = user.Identity;
            if (!id.IsAuthenticated || id.AuthenticationType != "Forms") throw new SecurityException();
            var request = this.Request;
            var response = this.Response;
            response.Clear();
            response.Cache.SetCacheability(HttpCacheability.NoCache);
            response.Cache.SetNoStore();
            response.ContentType = "text/plain";
            switch (request["Action"])
            {
                case "List":
                    this.List();
                    break;
                case "CreateDir":
                    if (!user.IsInRole("Admin")) throw new SecurityException();
                    this.CreateDir();
                    break;
                case "DelDir":
                    if (!user.IsInRole("Admin")) throw new SecurityException();
                    this.DelDir();
                    break;
                case "DelFile":
                    if (!user.IsInRole("Admin")) throw new SecurityException();
                    this.DelFile();
                    break;
                case "Upfile":
                    this.WriteFile();
                    break;
            }
            response.End();
        }

        void List()
        {
            var request = this.Request;
            var response = this.Response;
            var server = this.Server;
            var path = this.PathSafeEncode(request["Path"]);
            var xml = this.OutputXml;
            xml.WriteStartDocument();
            xml.WriteStartElement("upfile");
            xml.WriteAttributeString("version", "1.0");
            var url = new Uri(request.Url, path.IsNullOrWhiteSpace() ? BLL.Setting.UPFILEPATH : BLL.Setting.UPFILEPATH + "/" + path);
            var dir = new DirectoryInfo(server.MapPath(url.LocalPath));
            if (dir.Exists)
            {
                foreach (var i in dir.GetFileSystemInfos().OrderByDescending(i => i.LastWriteTime).OrderByDescending(i => this.IsDir(i) ? 1 : 0))
                {
                    if (this.IsDir(i))
                    {
                        xml.WriteStartElement("dir");
                        xml.WriteElementString("path", path + "/" + i.Name);
                        xml.WriteElementString("files", ((DirectoryInfo)i).GetFileSystemInfos().LongLength.ToString());
                        xml.WriteEndElement();
                    }
                    else
                    {
                        xml.WriteStartElement("file");
                        xml.WriteElementString("url", url + "/" + i.Name);
                        xml.WriteElementString("length", ((FileInfo)i).Length.ToString());
                        xml.WriteEndElement();
                    }
                }
            }
            xml.WriteEndDocument();
            xml.Close();
        }

        void CreateDir()
        {
            var response = this.Response;
            var path = this.PathSafeEncode(this.Request["Path"]);
            var dir = new DirectoryInfo(this.Server.MapPath(BLL.Setting.UPFILEPATH + "/" + path));
            if (!dir.Exists)
            {
                dir.Create();
                response.Write(path);
            }
        }

        void DelDir()
        {
            var path = this.PathSafeEncode(this.Request["Path"]);
            if (path.IsNullOrWhiteSpace()) throw new SecurityException();
            Directory.Delete(this.Server.MapPath(BLL.Setting.UPFILEPATH + "/" + path), true);
        }

        void DelFile()
        {
            var path = new Uri(this.Request["Url"]).LocalPath;
            this.PathCheck(path);
            File.Delete(this.Server.MapPath(path));
        }

        void WriteFile()
        {
            var request = this.Request;
            var response = this.Response;
            var server = this.Server;
            var path = BLL.Setting.UPFILEPATH + "/" + this.PathSafeEncode(request["FileName"]);
            if (!Regex.IsMatch(path, BLL.Setting.ALLOWEDEXTS, RegexOptions.IgnoreCase | RegexOptions.Compiled)) throw new SecurityException();
            var stream = request.InputStream;
            var count = 0;
            var read = 0L;
            var len = long.Parse(request["Length"]);
            var setting = new BLL.Setting();
            var settingItem = setting.Get();
            //水印
            Image mask = null;
            if (request["PicMask"] == "1" && settingItem.PicMaskEnabled && Regex.IsMatch(path, @"\.(jpg|jpeg|bmp|png)$", RegexOptions.IgnoreCase | RegexOptions.Compiled))
            {
                var s = settingItem.PicMaskFile;
                if (!s.IsNullOrWhiteSpace())
                {
                    s = server.MapPath(s);
                    if (File.Exists(s))
                    {
                        mask = Image.FromFile(s);
                        path = Path.ChangeExtension(path, ".jpg");
                    }
                }
            }
            //命名
            var rename = request["Rename"] == "2";
            if (!rename && request["Rename"] != "1")
            {
                var s = server.MapPath(path);
                rename = File.Exists(s) || Directory.Exists(s);
            }
            if (rename) path = Regex.Replace(path, @"[^/\\]*(\.[^\.]+)$", DateTime.Now.Ticks.ToString("X") + "$1", RegexOptions.Compiled);
            var url = new Uri(request.Url, path);
            path = server.MapPath(url.LocalPath);
            Directory.CreateDirectory(server.MapPath(BLL.Setting.UPFILEPATH));
            stream.Seek(0, SeekOrigin.Begin);
            if (mask != null)
            {
                using (var bmp = Bitmap.FromStream(stream))
                {
                    bmp.Mask(mask, (ContentAlignment)settingItem.PicMaskPos);
                    bmp.SaveJpeg(settingItem.PicQuality, path);
                    read = stream.Length;
                }
            }
            else
            {
                var buffer = new byte[30720];
                using (var writer = File.OpenWrite(path))
                {
                    while ((count = stream.Read(buffer, 0, 30720)) != 0)
                    {
                        writer.Write(buffer, 0, count);
                        writer.Flush();
                        read += count;
                    }
                }
            }
            if (read < len)
                File.Delete(path);
            else
                response.Write(url);
        }
    }
}