using Kivii;
using Kivii.Linq;
using Kivii.Pdf;
using Kivii.Web;
using Njust.Pdf.Analysis.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Njust.Pdf.Analysis.Tranforms
{
    #region CRUDQ
    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseCreate:RestfulCreate<Analyse>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseUpdate : RestfulUpdate<Analyse>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseDelete : RestfulDelete<Analyse>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseQuery : RestfulQuery<Analyse>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseRead : RestfulRead<Analyse>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseAllRead : RestfulRead<AnalyseAll>
    {
    }
    #endregion

    [RequiresAnyRole(SystemRoles.Everyone)]
    [Route("/Analysis/PreCheck/{Kvid}")]
    public class AnalysePreCheck : RestfulExecution<Analyse>
    {
        public Guid Kvid { get; set; }
        public string View { get; set; }
        public override object OnExecution(IRequest req, IResponse res)
        {
            if (Kvid==Guid.Empty) return HttpError.NotFound("Need Kvid!");
            var conn = KiviiContext.GetOpenedDbConnection<Analyse>();

            var analyse = conn.SingleById<Analyse>(Kvid);

            if(analyse==null)return HttpError.NotFound("This Item is Unannounced!");
            if (View.IsNullOrEmpty()) View = "Template.PDFRead";
            return new HttpResult
            {
                Response = analyse,
                View = View
            };
        }
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseImport : RestfulExecution<Analyse>
    {
        public string DomainName { get; set; }

        public override object OnExecution(IRequest req, IResponse res)
        {
            #region 数据有效性验证
            DomainName.ThrowIfNullOrEmpty("请选择所属领域！");
            (req.Files.Length <= 0).ThrowIfTrue("请传入PDF文件！");
            #endregion

            var rtns = new RestfulQueryResponse<Analyse>();
            rtns.Results = new List<Analyse>();
            //调用平台功能 打开数据库连接
            var conn = KiviiContext.GetOpenedDbConnection<Analyse>();

            var preInserts = new List<Analyse>();

            var files = new Dictionary<string, Stream>();

            foreach(var file in req.Files)
            {
                if (!file.FileName.ToLower().EndsWith(".pdf")) continue;
                var stream = file.InputStream;
                stream.Position = 0;
                var hashCode = AnalyseExtension.ComputeMD5(stream);
                if (files.ContainsKey(hashCode)) continue;
                files.Add(hashCode, stream);

                var analyseKvid = Guid.NewGuid();
                var path = $"/Storages/Pdf/Analysis/{analyseKvid}/{file.FileName}";

                var analyse = new Analyse();
                analyse.HashCode = hashCode;//AnalyseExtension.ComputeMD5(file.InputStream);
                analyse.Kvid = analyseKvid;
                analyse.FileName = file.FileName;
                analyse.ImportPath = path;
                analyse.ImportTime = DateTime.Now;
                analyse.DomainName = DomainName;
                analyse.ImportUserKvid = KiviiContext.CurrentMember.Kvid;
                analyse.ImportUserName = KiviiContext.CurrentMember.FullName;
                preInserts.Add(analyse);
            }

            var exists = conn.Select<Analyse>(o => Sql.In(o.HashCode, preInserts.ConvertAll(p => p.HashCode)));

            foreach (var item in preInserts)
            {
                var stream = files[item.HashCode];
                var exist = exists.FirstOrDefault(o => o.HashCode == item.HashCode);
                if (exist != null)
                {
                    KiviiContext.VirtualFiles.WriteFile(exist.ImportPath, stream);
                    rtns.Results.Add(exist);
                    continue;
                }
                item.IsImaged = true;
                conn.Insert(item);
                item.RemoveAllOnlyProperties();
                rtns.Results.Add(item);
                stream.Position = 0;
                KiviiContext.VirtualFiles.WriteFile(item.ImportPath, stream);

                var imgPath = $"/Storages/Pdf/Analysis/Images/{item.Kvid}";
                var reader = new PdfReader(stream);
                var fileName = item.FileName.ToLower().Replace(".pdf", "");
                for (var i = 0; i < reader.XrefSize; i++)
                {
                    var pdfObj = reader.GetPdfObject(i);
                    if (pdfObj == null || !pdfObj.IsStream()) continue;
                    var pdfStream = (PdfStream)pdfObj;
                    var subType = pdfStream.Get(PdfName.SUBTYPE);
                    if (subType == null || subType.ToString() != PdfName.IMAGE.ToString()) continue;
                    var bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStream);
                    if (bytes == null) continue;
                    try
                    {
                        var ms = new MemoryStream(bytes);
                        ms.Position = 0;
                        KiviiContext.VirtualFiles.WriteFile($"{imgPath}/{fileName}_{i}.jpg", ms);

                        //var img = System.Drawing.Image.FromStream(ms);
                        //img.Save(Path.Combine(tbxExportImagesPath.Text, $"{i}.jpg"), System.Drawing.Imaging.ImageFormat.Jpeg);
                    }
                    catch
                    { }
                }
            }

            rtns.Total = rtns.Results.Count();
            return rtns;
        }
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseAnalysing : RestfulExecution<Analyse>
    {
        public List<Analyse> Items { get; set; }

        public override object OnExecution(IRequest req, IResponse res)
        {
            Items.ThrowIfNullOrEmpty("Need Items!");

            var conn = KiviiContext.GetOpenedDbConnection<AnalyseAll>();
            var exists = conn.Select<AnalyseAll>(o => Sql.In(o.HashCode, Items.ConvertAll(p => p.HashCode)));

            var rtns = new RestfulQueryResponse<AnalyseAll>();
            rtns.Results = new List<AnalyseAll>();
            if (exists.IsNullOrEmpty()) return rtns;

            foreach(var item in Items)
            {
                if (item.HashCode.IsNullOrEmpty()) continue;
                var exist = exists.FirstOrDefault(o => o.HashCode == item.HashCode);
                if (exist == null) continue;
                if (!exist.IsImaged)
                {
                    var queryPdfEntityFile = conn.From<EntityDbFile<Analyse>>();
                    queryPdfEntityFile.Where(o => o.ParentKvid == Guid.Empty & o.DbFolderPath == "/Pdf/Analysis" & o.Extension.ToLower() == ".pdf" & o.OwnerKvid == exist.Kvid);
                    var pdfFile = conn.Single(queryPdfEntityFile);
                    if (pdfFile != null)
                    {
                        var physicalStorageFilePath = pdfFile.GetPhysicalPath();
                        var imgPath = $"/Storages/Pdf/Analysis/Images/{exist.Kvid}";
                        if (File.Exists(physicalStorageFilePath))
                        {
                            var reader = new PdfReader(physicalStorageFilePath);
                            var fileName = exist.FileName.ToLower().Replace(".pdf", "");
                            for (var i = 0; i < reader.XrefSize; i++)
                            {
                                var pdfObj = reader.GetPdfObject(i);
                                if (pdfObj == null || !pdfObj.IsStream()) continue;
                                var pdfStream = (PdfStream)pdfObj;
                                var subType = pdfStream.Get(PdfName.SUBTYPE);
                                if (subType == null || subType.ToString() != PdfName.IMAGE.ToString()) continue;
                                var bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStream);
                                if (bytes == null) continue;
                                try
                                {
                                    var ms = new MemoryStream(bytes);
                                    ms.Position = 0;
                                    KiviiContext.VirtualFiles.WriteFile($"{imgPath}/{fileName}_{i}.jpg", ms);
                                }
                                catch
                                { }
                            }
                            exist.IsImaged = true;
                            exist.AddOnlyProperties(o => o.IsImaged);
                        }
                    }
                }
                exist.Allbody = item.Allbody.Replace("\n", "");
                exist.AddOnlyProperties(o => o.Allbody);
                exist.AnlysisTime = DateTime.Now;
                exist.AddOnlyProperties(o => o.AnlysisTime);
                exist.AnlysisUserName = KiviiContext.CurrentMember.FullName;
                exist.AddOnlyProperties(o => o.AnlysisUserName);
                exist.IsAnlysis = true;
                exist.AddOnlyProperties(o => o.IsAnlysis);
                conn.UpdateOnly(exist);
                exist.RemoveAllOnlyProperties();
                rtns.Results.Add(exist);
            }

            rtns.Total = rtns.Results.Count();
            return rtns;
        }
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AnalyseChecking : RestfulExecution<Analyse>
    {
        public List<Analyse> Items { get; set; }

        public override object OnExecution(IRequest req, IResponse res)
        {
            Items.ThrowIfNullOrEmpty("Need Items!");

            var conn = KiviiContext.GetOpenedDbConnection<Analyse>();
            var rtns = new RestfulQueryResponse<Analyse>();
            rtns.Results = new List<Analyse>();

            conn.InitEntityType<Author>();
            conn.InitEntityType<Keyword>();
            conn.InitEntityType<Organ>();
            var allAuthors = conn.Select<Author>(o => Sql.In(o.AnalyseKvid, Items.ConvertAll(p => p.Kvid)));
            var allKeywords = conn.Select<Keyword>(o => Sql.In(o.AnalyseKvid, Items.ConvertAll(p => p.Kvid)));
            var allOrgans = conn.Select<Organ>(o => Sql.In(o.AnalyseKvid, Items.ConvertAll(p => p.Kvid)));
            var allItems = conn.Select<Analyse>(o => Sql.In(o.Kvid, Items.ConvertAll(p => p.Kvid)));
            allItems.ThrowIfNullOrEmpty("未找到要复核的项!");

            var trans = conn.OpenTransaction();

            try
            {
                foreach (var item in Items)
                {
                    if (item == null) continue;
                    if (item.Kvid == Guid.Empty) continue;
                    var exist = allItems.FirstOrDefault(o => o.Kvid == item.Kvid);
                    if (exist == null) continue;
                    item.RemoveAllOnlyProperties();
                    
                    #region Author
                    var currentAuthors = allAuthors.Where(o => o.AnalyseKvid == item.Kvid).ToList();
                    if (!item.Author.IsNullOrEmpty())
                    {
                        var authors = item.Author.Split( ',', '，', ';', '；').ToList();
                        foreach (var auth in authors)
                        {
                            if (currentAuthors.Exists(o => o.Name == auth)) continue;
                            var author = new Author();
                            author.Name = auth;
                            author.DomainName = exist.DomainName;
                            author.Language = exist.Language;
                            author.AnalyseKvid = exist.Kvid;
                            conn.Insert(author);
                        }
                        item.Author = string.Join(";", authors);
                        item.AddOnlyProperties(o => o.Author);
                    }

                    #endregion

                    #region Keyword
                    var currentKeywords = allKeywords.Where(o => o.AnalyseKvid == item.Kvid).ToList();
                    if (!item.Keywords.IsNullOrEmpty())
                    {
                        var keywords = item.Keywords.Split(',', '，', ';', '；').ToList();
                        foreach (var key in keywords)
                        {
                            if (currentKeywords.Exists(o => o.Title == key)) continue;
                            var keyword = new Keyword();
                            keyword.Title = key;
                            keyword.DomainName = exist.DomainName;
                            keyword.Language = exist.Language;
                            keyword.AnalyseKvid = exist.Kvid;
                            conn.Insert(keyword);
                        }
                        item.Keywords = string.Join(";", keywords);
                        item.AddOnlyProperties(o => o.Keywords);
                    }
                    #endregion

                    #region Organ
                    var currentOrgans = allOrgans.Where(o => o.AnalyseKvid == item.Kvid).ToList();
                    if (!item.Organization.IsNullOrEmpty())
                    {
                        var organs = item.Organization.Split(',', '，', ';', '；').ToList();
                        foreach (var key in organs)
                        {
                            if (currentOrgans.Exists(o => o.Title == key)) continue;
                            var organ = new Organ();
                            organ.Title = key;
                            organ.DomainName = exist.DomainName;
                            organ.Language = exist.Language;
                            organ.AnalyseKvid = exist.Kvid;
                            conn.Insert(organ);
                        }
                        item.Organization = string.Join(";", organs);
                        item.AddOnlyProperties(o => o.Organization);
                    }
                    #endregion

                    if (!exist.IsImaged)
                    {
                        var queryPdfEntityFile = conn.From<EntityDbFile<Analyse>>();
                        queryPdfEntityFile.Where(o => o.ParentKvid == Guid.Empty & o.DbFolderPath == "/Pdf/Analysis" & o.Extension.ToLower() == ".pdf" & o.OwnerKvid == exist.Kvid);
                        var pdfFile = conn.Single(queryPdfEntityFile);
                        if (pdfFile != null)
                        {
                            Console.WriteLine("------------------pdfFile is not null------------------");
                            var physicalStorageFilePath = pdfFile.GetPhysicalPath();
                            Console.WriteLine($"------------------physicalStorageFilePath:{physicalStorageFilePath}------------------");
                            var imgPath = $"/Storages/Pdf/Analysis/Images/{exist.Kvid}";
                            if (File.Exists(physicalStorageFilePath))
                            {
                                Console.WriteLine("------------------File is Existed------------------");
                                var reader = new PdfReader(physicalStorageFilePath);
                                var fileName = exist.FileName.ToLower().Replace(".pdf", "");
                                for (var i = 0; i < reader.XrefSize; i++)
                                {
                                    var pdfObj = reader.GetPdfObject(i);
                                    if (pdfObj == null || !pdfObj.IsStream()) continue;
                                    var pdfStream = (PdfStream)pdfObj;
                                    var subType = pdfStream.Get(PdfName.SUBTYPE);
                                    if (subType == null || subType.ToString() != PdfName.IMAGE.ToString()) continue;
                                    var bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStream);
                                    if (bytes == null) continue;
                                    try
                                    {
                                        var ms = new MemoryStream(bytes);
                                        ms.Position = 0;
                                        KiviiContext.VirtualFiles.WriteFile($"{imgPath}/{fileName}_{i}.jpg", ms);
                                    }
                                    catch
                                    { }
                                }
                                Console.WriteLine("------------------exist.IsImaged = true------------------");
                                item.IsImaged = true;
                                item.AddOnlyProperties(o => o.IsImaged);
                            }
                        }
                    }

                    if (!item.Title.IsNullOrEmpty()) item.AddOnlyProperties(o => o.Title);
                    if (!item.Type.IsNullOrEmpty()) item.AddOnlyProperties(o => o.Type);
                    if (!item.Category.IsNullOrEmpty()) item.AddOnlyProperties(o => o.Category);
                    if (!item.Source.IsNullOrEmpty())
                    {
                        item.AddOnlyProperties(o => o.Source);
                    }
                    if (!item.PublishTime.IsNullOrEmpty()) item.AddOnlyProperties(o => o.PublishTime);
                    if (!item.Country.IsNullOrEmpty()) item.AddOnlyProperties(o => o.Country);
                    if (!item.Fund.IsNullOrEmpty())
                    {
                        item.Fund = item.Fund.Replace("\n", "");
                        item.AddOnlyProperties(o => o.Fund);
                    }
                    if (!item.Abstract.IsNullOrEmpty())
                    {
                        item.Abstract = item.Abstract.Replace("\n", "");
                        item.AddOnlyProperties(o => o.Abstract);
                    }
                    if (!item.Mainbody.IsNullOrEmpty())
                    {
                        item.Mainbody = item.Mainbody.Replace("\n", "");
                        item.AddOnlyProperties(o => o.Mainbody);
                    }
                    if (!item.Reference.IsNullOrEmpty())
                    {
                        item.Reference = item.Reference.Replace("\n", "");
                        item.AddOnlyProperties(o => o.Reference);
                    }
                    item.CheckTime = DateTime.Now;
                    item.AddOnlyProperties(o => o.CheckTime);
                    item.IsChecked = true;
                    item.AddOnlyProperties(o => o.IsChecked);
                    if (!item.Language.IsNullOrEmpty()) item.AddOnlyProperties(o => o.Language);
                    conn.UpdateOnly(item);
                    item.RemoveAllOnlyProperties();
                    rtns.Results.Add(item);

                    trans.Commit();
                }
            }
            catch (Exception ex)
            {
                trans.Rollback();
                throw ex;
            }

            rtns.Total = rtns.Results.Count();
            return rtns;
        }
    }

    #region CRUDQ
    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AuthorCreate : RestfulCreate<Author>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AuthorUpdate : RestfulUpdate<Author>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AuthorDelete : RestfulDelete<Author>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AuthorQuery : RestfulQuery<Author>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class AuthorRead : RestfulRead<Author>
    {
    }
    #endregion

    #region CRUDQ
    [RequiresAnyRole(SystemRoles.Everyone)]
    public class KeywordCreate : RestfulCreate<Keyword>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class KeywordUpdate : RestfulUpdate<Keyword>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class KeywordDelete : RestfulDelete<Keyword>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class KeywordQuery : RestfulQuery<Keyword>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class KeywordRead : RestfulRead<Keyword>
    {
    }
    #endregion

    #region CRUDQ
    [RequiresAnyRole(SystemRoles.Everyone)]
    public class OrganCreate : RestfulCreate<Organ>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class OrganUpdate : RestfulUpdate<Organ>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class OrganDelete : RestfulDelete<Organ>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class OrganQuery : RestfulQuery<Organ>
    {
    }

    [RequiresAnyRole(SystemRoles.Everyone)]
    public class OrganRead : RestfulRead<Organ>
    {
    }
    #endregion
}
