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


namespace IRaCIS.Core.Application.Services
{
    public class DicomArchiveService : IDicomArchiveService
    {
        private readonly IRepository<DicomStudy> _studyRepository;
        private readonly IRepository<DicomSeries> _seriesRepository;
        private readonly IRepository<DicomInstance> _instanceRepository;
        private readonly IRepository<DataInspection> _inspectionService;
        private readonly IEasyCachingProvider _provider;
        private readonly DicomFileStoreHelper _dicomFileStoreHelper;


        private static object lockCodeGenerate = new object();

        public DicomArchiveService(IRepository<DicomStudy> studyRepository,
            IRepository<DicomSeries> seriesRepository,
            IRepository<DicomInstance> instanceRepository,
            IHostEnvironment hostEnvironment,
            IRepository<DataInspection> inspectionService,
            DicomFileStoreHelper dicomFileStoreHelper,
            IEasyCachingProvider provider)
        {
            _dicomFileStoreHelper = dicomFileStoreHelper;

            _studyRepository = studyRepository;

            _seriesRepository = seriesRepository;

            _instanceRepository = instanceRepository;
            this._inspectionService = inspectionService;
            _provider = provider;

        }

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

        public async Task<Tuple<Guid, string>> 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 new Tuple<Guid, string>(IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString()), string.Empty) ;
            }


            var anonymize_AddFixedFiledList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddFixedFiled).Value;
            var anonymize_AddIRCInfoFiled = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddIRCInfoFiled).Value;
            var anonymize_FixedField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_FixedField).Value;
            var anonymize_IRCInfoField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_IRCInfoField).Value;



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

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

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

            foreach (var item in anonymize_AddIRCInfoFiled.Union(anonymize_IRCInfoField))
            {

                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);

                }

            }


            #region 调整废弃
            ////按照配置文件 匿名化
            //foreach (var anonymizeItem in SystemConfig.AnonymizeTagList)
            //{
            //    if (anonymizeItem.Enable)
            //    {
            //        ushort group = Convert.ToUInt16(anonymizeItem.Group, 16);
            //        ushort element = Convert.ToUInt16(anonymizeItem.Element, 16);
            //        dataset.AddOrUpdate(new DicomTag(group, element), anonymizeItem.ReplaceValue);
            //    }
            //}


            //if (AppSettings.AddClinicalInfo) //是否需要写入临床信息
            //{
            //    //Dicom 文件中写入临床信息
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, addtionalInfo.TrialCode); //Trial
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolName, addtionalInfo.TrialIndication); //indication
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialSponsorName, addtionalInfo.Sponsor);//sponsor
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, addtionalInfo.SiteCode); //SiteId
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteName, addtionalInfo.SiteName);//SiteName
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, addtionalInfo.SubjectCode + " " + addtionalInfo.SubjectSex);//SubjectId
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, addtionalInfo.VisitNum.ToString()); // TimePoint
            //    dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointDescription, addtionalInfo.VisitName + " " + addtionalInfo.SVUPDES);
            //}

            //DicomStudy dicomStudy = null;
            //DicomSeries dicomSeries = null;
            //DicomInstance dicomInstance = null;

            //lock (lockTest)
            //{
            //     dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
            //     dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
            //     dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries);

            //    if (isStudyNeedAdd) _studyRepository.Add(dicomStudy);
            //    if (isSeriesNeedAdd) _seriesRepository.Add(dicomSeries);
            //    _instanceRepository.Add(dicomInstance);
            //}
            #endregion





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

            dicomSeries.DicomStudy = dicomStudy;


           var createtime=DateTime.Now;
           
            if (isStudyNeedAdd)
            {
                // 添加检查
                await _studyRepository.AddAsync(dicomStudy);
            }
               
            if (isSeriesNeedAdd)
            {
                dicomSeries.DicomStudy = dicomStudy;
                // 添加序列
                await _seriesRepository.AddAsync(dicomSeries);
            }


        
            await  _instanceRepository.AddAsync(dicomInstance);



            string filePath = _dicomFileStoreHelper.CreateInstanceFilePath(dicomStudy, dicomSeries.Id, dicomInstance.Id);

            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(filePath);
                }
                else
                {
                    await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(filePath);
                }
            }
            else
            {
                if (dataset.InternalTransferSyntax.IsEncapsulated) await dicomFile.SaveAsync(filePath);
                else await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(filePath); //RLELossless
            }
            return new Tuple<Guid, string>(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)

            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 = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
                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.StudyMaxCode}").Value;

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

                dicomStudy.Code = currentNextCodeInt;

                dicomStudy.StudyCode = "ST" + currentNextCodeInt.ToString("D5");

                _provider.Set<int>($"{addtionalInfo.TrialId }_{ StaticData.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)
        {
            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),
            };

            ++dicomStudy.InstanceCount;
            ++dicomSeries.InstanceCount;

            return dicomInstance;



        }








    }
}
