using Dicom;
using Dicom.Imaging.Codec;
using EasyCaching.Core;
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Domain.Share;
using System.Text;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Infrastructure;

namespace IRaCIS.Core.Application.Services
{
    public class DicomArchiveService :BaseService, IDicomArchiveService
    {
        private readonly IRepository<DicomStudy> _studyRepository;
        private readonly IRepository<DicomSeries> _seriesRepository;
        private readonly IRepository<DicomInstance> _instanceRepository;
        private readonly IRepository<Dictionary> _dictionaryRepository;
        private readonly IEasyCachingProvider _provider;

        private readonly IWebHostEnvironment _hostEnvironment;

        private static object lockCodeGenerate = new object();


        private List<Guid> _instanceIdList = new List<Guid>();

        public DicomArchiveService(IRepository<DicomStudy> studyRepository,
            IRepository<DicomSeries> seriesRepository,
            IRepository<DicomInstance> instanceRepository,
            IWebHostEnvironment hostEnvironment,
            IRepository<Dictionary> dictionaryRepository,
            IEasyCachingProvider provider)
        {
            _hostEnvironment = hostEnvironment;
            _studyRepository = studyRepository;

            _seriesRepository = seriesRepository;

            _instanceRepository = instanceRepository;
            _dictionaryRepository = dictionaryRepository;
            _provider = provider;

        }

        public async Task<bool> DicomDBDataSaveChange()
        {
            var success = await _studyRepository.SaveChangesAsync();
            return success;
        }

      


        public async Task<(Guid StudyId, string StudyCode)> ArchiveDicomStreamAsync(Stream dicomStream,
            DicomTrialSiteSubjectInfo addtionalInfo, List<string> seriesInstanceUidList, List<string> instanceUidList)
        {


            DicomFile dicomFile = await DicomFile.OpenAsync(dicomStream, Encoding.Default);

            DicomDataset dataset = dicomFile.Dataset;

            //如果数据库存在该instance 记录 那么就不处理
            string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
            string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);

            if (instanceUidList.Any(t => t == sopInstanceUid))
            {
                return (IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString()), string.Empty);
            }


            var anonymize_AddFixedFiledList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value ?? new List<SystemAnonymization>();
            var anonymize_AddIRCInfoFiledList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddIRCInfoFiled).Value ?? new List<SystemAnonymization>();
            var anonymize_FixedFieldList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_FixedField).Value ?? new List<SystemAnonymization>();
            var anonymize_IRCInfoFieldList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_IRCInfoField).Value ?? new List<SystemAnonymization>();


            if (anonymize_AddFixedFiledList.Union(anonymize_AddIRCInfoFiledList).Union(anonymize_FixedFieldList).Union(anonymize_IRCInfoFieldList).Count() == 0)
            {
                //---未取到缓存匿名化配置数据,上传停止,请联系开发人员核实
                throw new BusinessValidationFailedException(_localizer["DAS_NoAnonCacheData"]);
            }


            foreach (var item in anonymize_AddFixedFiledList.Union(anonymize_FixedFieldList))
            {

                var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));

                dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
            }

            foreach (var item in anonymize_AddIRCInfoFiledList.Union(anonymize_IRCInfoFieldList))
            {

                var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));

                if (dicomTag == DicomTag.ClinicalTrialProtocolID)
                {
                    dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode);

                }
                if (dicomTag == DicomTag.ClinicalTrialSiteID)
                {
                    dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialSiteCode);

                }
                if (dicomTag == DicomTag.ClinicalTrialSubjectID)
                {
                    dataset.AddOrUpdate(dicomTag, addtionalInfo.SubjectCode);

                }
                if (dicomTag == DicomTag.ClinicalTrialTimePointID)
                {
                    dataset.AddOrUpdate(dicomTag, addtionalInfo.VisitNum.ToString());

                }
                if (dicomTag == DicomTag.PatientID)
                {
                    dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode + "_" + addtionalInfo.SubjectCode);

                }

            }


            DicomStudy dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
            DicomSeries dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
            DicomInstance dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries,out bool isInstanceNeedAdd);

            dicomSeries.DicomStudy = dicomStudy;


            var createtime = DateTime.Now;

            if (isStudyNeedAdd)
            {
                // 添加检查
                await _studyRepository.AddAsync(dicomStudy);
            }

            if (isSeriesNeedAdd)
            {
                dicomSeries.DicomStudy = dicomStudy;
                // 添加序列
                await _seriesRepository.AddAsync(dicomSeries);
            }


            var (physicalPath, relativePath) = FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id);

            dicomInstance.Path = relativePath;

            if (isInstanceNeedAdd)
            {
                await _instanceRepository.AddAsync(dicomInstance);

            }


            var samplesPerPixel = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, string.Empty);
            var photometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty);

            if (samplesPerPixel == "1" && (photometricInterpretation.ToUpper() == "MONOCHROME2" || photometricInterpretation.ToUpper() == "MONOCHROME1"))//MONOCHROME2
            {
                if (dataset.InternalTransferSyntax.IsEncapsulated)
                {
                    //正常保存  不做处理
                    await dicomFile.SaveAsync(physicalPath);
                }
                else
                {   //JPEGLSLossless 保存
                    await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(physicalPath);
                }
            }
            else
            {
                if (dataset.InternalTransferSyntax.IsEncapsulated)
                {
                    //正常保存  不做处理
                    await dicomFile.SaveAsync(physicalPath);
                }
                else 
                {
                    //RLELossless 保存
                    await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(physicalPath); //RLELossless

                }
            }
            return (dicomInstance.StudyId, dicomStudy.StudyCode);
        }


        private DicomStudy CreateDicomStudy(DicomDataset dataset, DicomTrialSiteSubjectInfo addtionalInfo, out bool isStudyNeedAdd)
        {

            string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
            Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString());



            // 每个线程都查询数据库最大的,和缓存中最大的,取最大值为基数生成Code
            //var id = Thread.CurrentThread.ManagedThreadId.ToString("00");

            //虽然每个文件都会进来,但是只要查询过,就会跟踪,不会再次查询数据库  这里线程并发会有问题,得加锁,不然生成Code 出错
            DicomStudy dicomStudy = _studyRepository.ImageFind(studyId, typeof(DicomStudy));

            if (dicomStudy != null)
            {
                isStudyNeedAdd = false;
                return dicomStudy;
            }

            //_logger.LogWarning($"Thread {id} ,studyUid{studyInstanceUid}, 生成StudyId:{studyId}");

            isStudyNeedAdd = true;

            //dataset.GetSingleValue<string>(DicomTag.StudyDate) + dataset.GetSingleValue<string>(DicomTag.StudyTime)

            var modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty);

            var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();

            var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;

            if (modality == "MR")
            {
                modalityForEdit = "MRI";
            }

            if (modality == "PT")
            {
                modalityForEdit = "PET";
            }
            if(modality== "PT、CT")
            {
                modalityForEdit = "PET-CT";
            }

            dicomStudy = new DicomStudy
            {
                Id = studyId,
                StudyInstanceUid = studyInstanceUid,
                /* StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, DateTime.Now).TimeOfDay),*///dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime),
                StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay),
                Modalities = modality,
                ModalityForEdit = modalityForEdit,
                Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
                InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
                PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
                PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
                PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
                PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
                BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),

                StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
                AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),

                //需要特殊处理
                PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),


                AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
                AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
                TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),


                SiteId = addtionalInfo.SiteId,
                TrialId = addtionalInfo.TrialId,
                SubjectId = addtionalInfo.SubjectId,
                SubjectVisitId = addtionalInfo.SubjectVisitId,
                //IsDoubleReview = addtionalInfo.IsDoubleReview,
                SeriesCount = 0,
                InstanceCount = 0
            };

            if (dicomStudy.PatientBirthDate.Length == 8)
            {
                dicomStudy.PatientBirthDate = $"{dicomStudy.PatientBirthDate[0]}{dicomStudy.PatientBirthDate[1]}{dicomStudy.PatientBirthDate[2]}{dicomStudy.PatientBirthDate[3]}-{dicomStudy.PatientBirthDate[4]}{dicomStudy.PatientBirthDate[5]}-{dicomStudy.PatientBirthDate[6]}{dicomStudy.PatientBirthDate[7]}";
            }

            lock (lockCodeGenerate)
            {

                //查询数据库获取最大的Code 没有记录则为0
                var dbStudyCodeIntMax = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.Code).DefaultIfEmpty().Max();

                //获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增
                var cacheMaxCodeInt = _provider.Get<int>($"{addtionalInfo.TrialId}_{StaticData.CacheKey.StudyMaxCode}").Value;

                int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1;

                dicomStudy.Code = currentNextCodeInt;

                dicomStudy.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy));

                _provider.Set<int>($"{addtionalInfo.TrialId}_{StaticData.CacheKey.StudyMaxCode}", dicomStudy.Code, TimeSpan.FromMinutes(30));

            }

            #region Setting Code old

            //var studyCode = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.StudyCode).OrderByDescending(c => c).FirstOrDefault();

            //var cacheMaxCode = _provider.Get<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value;

            //if (studyCode == null && string.IsNullOrEmpty(cacheMaxCode))
            //{
            //    dicomStudy.StudyCode = "ST" + 1.ToString().PadLeft(5, '0');

            //    _logger.LogWarning($"Thread {id} DB:{studyCode}  生成{ dicomStudy.StudyCode}");
            //}
            //else
            //{
            //    int dbNum = 0;

            //    int cacheNum = 0;

            //    if (studyCode != null)
            //    {
            //        int.TryParse(studyCode.Substring(studyCode.Length - 5, 5), out dbNum);
            //    }

            //    if (!string.IsNullOrEmpty(cacheMaxCode))
            //    {
            //        int.TryParse(cacheMaxCode.Substring(cacheMaxCode.Length - 5, 5), out cacheNum);
            //    }

            //    dbNum = cacheNum > dbNum ? cacheNum : dbNum;


            //    dicomStudy.StudyCode = "ST" + (++dbNum).ToString().PadLeft(5, '0');

            //    _logger.LogWarning($" Thread {id} DB:{studyCode} cache:{cacheNum} 生成{ dicomStudy.StudyCode}");
            //}

            //_provider.Set<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.StudyCode, TimeSpan.FromMinutes(30));


            #endregion



            return dicomStudy;





        }

        private DicomSeries CreateDicomSeries(DicomDataset dataset, DicomStudy dicomStudy, out bool isSeriesNeedAdd)
        {
            string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
            Guid seriesId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, seriesInstanceUid, dicomStudy.TrialId.ToString());

            //有几个序列会查询几次

            DicomSeries dicomSeries = _seriesRepository.ImageFind(seriesId, typeof(DicomSeries));

            if (dicomSeries != null)
            {
                isSeriesNeedAdd = false;
                return dicomSeries;
            }
            else
            {
                //var id = Thread.CurrentThread.ManagedThreadId.ToString("00");

                isSeriesNeedAdd = true;
                dicomSeries = new DicomSeries
                {
                    Id = seriesId,
                    StudyId = dicomStudy.Id,

                    StudyInstanceUid = dicomStudy.StudyInstanceUid,
                    SeriesInstanceUid = seriesInstanceUid,
                    SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
                    //SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
                    //SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
                    SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
                    Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
                    Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
                    SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),

                    ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
                    ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
                    BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
                    SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
                    ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
                    ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),

                    AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
                    AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
                    TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),

                    SiteId = dicomStudy.SiteId,
                    TrialId = dicomStudy.TrialId,
                    SubjectId = dicomStudy.SubjectId,
                    SubjectVisitId = dicomStudy.SubjectVisitId,

                    InstanceCount = 0
                };

                ++dicomStudy.SeriesCount;


                //_logger.LogWarning($"线程:{id},sericeId:{seriesId},count:{SeriesDic.Keys.Count}");

                return dicomSeries;
            }


        }

        private DicomInstance CreateDicomInstance(DicomDataset dataset, DicomStudy dicomStudy, DicomSeries dicomSeries, out bool isInstanceNeedAdd)
        {
            string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
            Guid instanceId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, dicomSeries.SeriesInstanceUid, sopInstanceUid, dicomStudy.TrialId.ToString());




            DicomInstance dicomInstance = new DicomInstance
            {
                Id = instanceId,
                StudyId = dicomStudy.Id,
                SeriesId = dicomSeries.Id,

                SiteId = dicomStudy.SiteId,
                TrialId = dicomStudy.TrialId,
                SubjectId = dicomStudy.SubjectId,
                SubjectVisitId = dicomStudy.SubjectVisitId,

                StudyInstanceUid = dicomStudy.StudyInstanceUid,
                SeriesInstanceUid = dicomSeries.SeriesInstanceUid,
                SopInstanceUid = sopInstanceUid,
                InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
                InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
                //InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
                //InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
                //dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
                CPIStatus = false,
                ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
                ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
                SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),

                SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
                NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
                PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
                ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
                FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
                WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
                WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
            };

            isInstanceNeedAdd = false;

            if (!_instanceIdList.Contains(instanceId))
            {
                ++dicomStudy.InstanceCount;
                ++dicomSeries.InstanceCount;

                isInstanceNeedAdd = true;

                _instanceIdList.Add(instanceId);
            }
       
            return dicomInstance;

        }








    }
}