/// <summary>
        /// 	Projects Enumeration of drug exposure from the raw set of drug exposure entities. 
        /// 	During build:
        ///	override the drug's start/end date using the end date of the corresponding observation period.
        /// </summary>
        /// <param name="drugExposures">raw set of drug exposures entities</param>
        /// <param name="visitOccurrences">the visit occurrences entities for current person</param>
        /// <param name="observationPeriods">the observation periods entities for current person</param>
        /// <returns>Enumeration of drug exposure entities</returns>
        public override IEnumerable<DrugExposure> BuildDrugExposures(DrugExposure[] drugExposures,
         Dictionary<long, VisitOccurrence> visitOccurrences,
         ObservationPeriod[] observationPeriods)
        {
            if (observationPeriods.Length != 0)
             {
            foreach (var drugExposure in drugExposures)
            {
               drugExposure.StartDate = drugExposure.DaysSupply.HasValue
                  ? observationPeriods[0].EndDate.AddDays(drugExposure.DaysSupply.Value*-1)
                  : observationPeriods[0].EndDate;

               drugExposure.EndDate = observationPeriods[0].EndDate;
               yield return drugExposure;
            }
             }
        }
        /// <summary>
        /// Projects Enumeration of ConditionOccurrence from the raw set of ConditionOccurrence entities. 
        /// 	During build:
        /// 	override the condition's start date using the end date of the corresponding observation period.
        /// </summary>
        /// <param name="conditionOccurrences">raw set of condition occurrence entities</param>
        /// <param name="visitOccurrences">the visit occurrence entities for current person</param>
        /// <param name="observationPeriods">the observation period entities for current person</param>
        /// <returns>Enumeration of condition occurrence entities</returns>
        public override IEnumerable<ConditionOccurrence> BuildConditionOccurrences(
         ConditionOccurrence[] conditionOccurrences, Dictionary<long, VisitOccurrence> visitOccurrences,
         ObservationPeriod[] observationPeriods)
        {
            if (observationPeriods.Length != 0)
             {
            foreach (var conditionOccurrence in conditionOccurrences)
            {
               var sourceValue = conditionOccurrence.SourceValue;
               if (sourceValue.Length < 2) continue;

               if (sourceValue.Substring(sourceValue.Length - 2) != "_1") continue;
               if (conditionOccurrence.ConceptId > 0)
               {
                  conditionOccurrence.StartDate = observationPeriods[0].StartDate;
                  conditionOccurrence.EndDate = null;

                  yield return conditionOccurrence;
               }
            }
             }
        }
        /// <summary>
        /// Projects Enumeration of ConditionOccurrence from the raw set of ConditionOccurrence entities. 
        /// 	During build:
        /// 	override the condition's start date using the start date of the corresponding visit.
        ///   overide TypeConceptId per CDM Mapping spec. 
        /// </summary>
        /// <param name="conditionOccurrences">raw set of condition occurrence entities</param>
        /// <param name="vo">the visit occurrence entities for current person</param>
        /// <param name="op">the observation period entities for current person</param>
        /// <returns>Enumeration of condition occurrence entities</returns>
        public override IEnumerable<ConditionOccurrence> BuildConditionOccurrences(ConditionOccurrence[] conditionOccurrences, Dictionary<long, VisitOccurrence> vo, ObservationPeriod[] op)
        {
            var result = new HashSet<ConditionOccurrence>();
             var dateClaimTypeDictionary = new Dictionary<DateTime, Dictionary<string, List<IEntity>>>();
             foreach (var conditionOccurrence in conditionOccurrences)
             {
            var visitOccurrence = GetVisitOccurrence(conditionOccurrence);
            if (visitOccurrence == null)
               continue;

            conditionOccurrence.VisitOccurrenceId = visitOccurrence.Id;
            conditionOccurrence.StartDate = visitOccurrence.StartDate;
            conditionOccurrence.EndDate = null;
            conditionOccurrence.TypeConceptId = GetConditionTypeConceptId(conditionOccurrence.TypeConceptId.Value, visitOccurrence.ConceptId);

            AddToDateClaimTypeDictionary(dateClaimTypeDictionary, conditionOccurrence, visitOccurrence);
             }

             foreach (var sameStartDate in conditionOccurrences.Where(c => c.VisitOccurrenceId.HasValue && !string.IsNullOrEmpty(c.SourceValue)).GroupBy(c => c.StartDate))
             {
            foreach (var sameVisit in sameStartDate.GroupBy(c => c.VisitOccurrenceId))
            {
               foreach (var sameSource in sameVisit.GroupBy(c => c.SourceValue))
               {
                  foreach (var sameConcept in sameSource.GroupBy(c => c.ConceptId))
                  {
                     var conditionOccurrence = sameConcept.OrderBy(c => c.TypeConceptId).ToList()[0];

                     if (!CodeValidator.IsValidIcd9(conditionOccurrence.SourceValue)) continue;

                     conditionOccurrence.ProviderKey = GetProviderKey(vo[conditionOccurrence.VisitOccurrenceId.Value],
                                                                      conditionOccurrence, dateClaimTypeDictionary);
                     result.Add(conditionOccurrence);
                  }
               }
            }
             }

             return base.BuildConditionOccurrences(result.ToArray(), vo, op);
        }
        private static IEnumerable<VisitOccurrence> CleanVisitOccurrences(IEnumerable<VisitOccurrence> visitOccurrences, ObservationPeriod[] observationPeriods)
        {
            foreach (var visitOccurrence in visitOccurrences)
             {
            if (!visitOccurrence.EndDate.HasValue || visitOccurrence.EndDate < visitOccurrence.StartDate)
               visitOccurrence.EndDate = visitOccurrence.StartDate;

            var observationPeriod = observationPeriods.FirstOrDefault(op => visitOccurrence.StartDate.Between(op.StartDate, op.EndDate) || visitOccurrence.EndDate.Value.Between(op.StartDate, op.EndDate));

            if (observationPeriod == null) continue;

            if (visitOccurrence.StartDate < observationPeriod.StartDate)
               visitOccurrence.StartDate = observationPeriod.StartDate;

            if (visitOccurrence.EndDate > observationPeriod.EndDate)
               visitOccurrence.EndDate = observationPeriod.EndDate;

            yield return visitOccurrence;
             }
        }
        /// <summary>
        /// Projects Enumeration of Visit Occurrence from the raw set of Visit Occurrence entities. 
        ///  </summary>
        /// <param name="rawVisitOccurrences">raw set of Visit Occurrence entities</param>
        /// <param name="observationPeriods">the observation periods entities for current person</param>
        /// <returns>Enumeration of Visit Occurrence</returns>
        public override IEnumerable<VisitOccurrence> BuildVisitOccurrences(VisitOccurrence[] rawVisitOccurrences, ObservationPeriod[] observationPeriods)
        {
            var visitOccurrences = CleanVisitOccurrences(rawVisitOccurrences, observationPeriods).ToList();

             var ipVisits = GetIPClaims(visitOccurrences).ToList();
             var erVisits = new List<VisitOccurrence>();
             var opVisits = new List<VisitOccurrence>();

             foreach (var visitOccurrence in visitOccurrences.Where(visitOccurrence => visitOccurrence.ConceptId != 9201))
             {
            var ip = ipVisits.FirstOrDefault(v => visitOccurrence.StartDate.Between(v.StartDate, v.EndDate.Value));

            if (visitOccurrence.ConceptId == 9203)
            {
               if (ip == null || (visitOccurrence.StartDate == ip.StartDate && visitOccurrence.EndDate == ip.StartDate))
               //ER - 9203
               {
                  erVisits.Add(visitOccurrence);
               }
               else
               {
                  if (!rawVisits.ContainsKey(visitOccurrence.SourceRecordGuid))
                     rawVisits.Add(visitOccurrence.SourceRecordGuid, ip);
               }
            }
            else if (ip == null)
            {
               opVisits.Add(visitOccurrence);
            }
            else
            {
               if (!rawVisits.ContainsKey(visitOccurrence.SourceRecordGuid))
                  rawVisits.Add(visitOccurrence.SourceRecordGuid, ip);
            }
             }

             foreach (var ipVisit in ipVisits)
             {
            yield return ipVisit;
             }

             // collapse claims with the same FST_DT in ER table as one unique ER visit
             foreach (var patplanidGroup in erVisits.GroupBy(vo => vo.AdditionalFields["pat_planid"]))
             {
            foreach (var erGroup in patplanidGroup.GroupBy(v => v.StartDate))
            {
               var visit = erGroup.First();
               visit.EndDate = erGroup.Max(v => v.EndDate);
               foreach (
                   var visitOccurrence in
                       erGroup.Where(visitOccurrence => !rawVisits.ContainsKey(visitOccurrence.SourceRecordGuid)))
               {
                  rawVisits.Add(visitOccurrence.SourceRecordGuid, visit);
               }

               yield return visit;
            }
             }

             foreach (var patplanidGroup in opVisits.GroupBy(vo => vo.AdditionalFields["pat_planid"]))
             {
            foreach (var opGroup in patplanidGroup.GroupBy(v => v.StartDate))
            {
               foreach (var opGroup1 in opGroup.GroupBy(v => v.AdditionalFields["prov"]))
               {
                  var visit = opGroup1.First();
                  visit.EndDate = opGroup1.Max(v => v.EndDate);
                  foreach (
                      var visitOccurrence in
                          opGroup1.Where(visitOccurrence => !rawVisits.ContainsKey(visitOccurrence.SourceRecordGuid))
                      )
                  {
                     rawVisits.Add(visitOccurrence.SourceRecordGuid, visit);
                  }

                  yield return visit;
               }
            }
             }
        }
        /// <summary>
        /// Projects Enumeration of ProcedureOccurrence from the raw set of ProcedureOccurence entities.
        /// During build:
        /// override the procedure's start date using the end date of the corresponding visit.
        /// overide TypeConceptId per CDM Mapping spec.
        /// truncate procedure's dates to the corresponding observation period dates
        /// </summary>
        /// <param name="procedureOccurrences">raw set of procedure occurrence entities</param>
        /// <param name="visitOccurrences">the visit occurrence entities for current person</param>
        /// <param name="observationPeriods">the observation period entities for current person</param>
        /// <returns>Enumeration of procedure occurrence entities</returns>
        public override IEnumerable<ProcedureOccurrence> BuildProcedureOccurrences(ProcedureOccurrence[] procedureOccurrences, Dictionary<long, VisitOccurrence> visitOccurrences, ObservationPeriod[] observationPeriods)
        {
            var result = new HashSet<ProcedureOccurrence>();
             var cost = new Dictionary<ProcedureOccurrence, List<ProcedureCost>>();
             var dateClaimTypeDictionary = new Dictionary<DateTime, Dictionary<string, List<IEntity>>>();

             var cdProcedures = new List<ProcedureOccurrence>();
             var otherProcedures = new List<ProcedureOccurrence>();

             foreach (var procedureOccurrence in procedureOccurrences)
             {
            var visitOccurrence = GetVisitOccurrence(procedureOccurrence);
            if (visitOccurrence == null)
               continue;

            procedureOccurrence.VisitOccurrenceId = visitOccurrence.Id;

            var observationPeriod = observationPeriods.FirstOrDefault(op => op.StartDate.Between(procedureOccurrence.StartDate, procedureOccurrence.EndDate.Value) || op.EndDate.Between(procedureOccurrence.StartDate, procedureOccurrence.EndDate.Value));
            if (observationPeriod != null)
            {
               if (procedureOccurrence.StartDate < observationPeriod.StartDate)
                  procedureOccurrence.StartDate = observationPeriod.StartDate;

               if (procedureOccurrence.StartDate > observationPeriod.EndDate)
                  procedureOccurrence.StartDate = observationPeriod.EndDate;
            }

            procedureOccurrence.EndDate = null;
            procedureOccurrence.TypeConceptId = GetProcedureTypeConceptId(procedureOccurrence.TypeConceptId.Value, visitOccurrence.ConceptId);

            if (procedureOccurrence.TypeConceptId == 38000272 || procedureOccurrence.TypeConceptId == 38000254)
            {
               if (procedureOccurrence.ProcedureCosts != null)
               {
                  if (!cost.ContainsKey(procedureOccurrence))
                     cost.Add(procedureOccurrence, new List<ProcedureCost>());

                  cost[procedureOccurrence].AddRange(procedureOccurrence.ProcedureCosts);
               }
               cdProcedures.Add(procedureOccurrence);
            }
            else
            {
               procedureOccurrence.StartDate = visitOccurrence.EndDate.Value;

               if (CodeValidator.IsValidIcd9Procedure(procedureOccurrence.SourceValue))
                  otherProcedures.Add(procedureOccurrence);
            }

            AddToDateClaimTypeDictionary(dateClaimTypeDictionary, procedureOccurrence, visitOccurrence);
             }

             foreach (var procedureOccurrence in BuildProceduresCd(cdProcedures, cost))
             {
            result.Add(procedureOccurrence);
             }

             foreach (var procedureOccurrence in BuildProceduresOther(otherProcedures, visitOccurrences, dateClaimTypeDictionary))
             {
            result.Add(procedureOccurrence);
             }

             return base.BuildProcedureOccurrences(result.ToArray(), visitOccurrences, observationPeriods);
        }
 /// <summary>
 /// 	Projects Enumeration of drug exposure from the raw set of drug exposure & procedure occurrence entities. 
 /// 	During build:
 ///	overide TypeConceptId per CDM Mapping spec. 
 /// </summary>
 /// <param name="de">raw set of drug exposures entities</param>
 /// <param name="visitOccurrences">the visit occurrences entities for current person</param>
 /// <param name="observationPeriods">the observation periods entities for current person</param>
 /// <returns>Enumeration of drug exposure entities</returns>
 public override IEnumerable<DrugExposure> BuildDrugExposures(DrugExposure[] de, Dictionary<long, VisitOccurrence> visitOccurrences, ObservationPeriod[] observationPeriods)
 {
     var drugExposures = PrepareDrugExposures(de, visitOccurrences).ToArray();
      return base.BuildDrugExposures(drugExposures, visitOccurrences, observationPeriods);
 }
        /// <summary>
        /// Projects death entity from the raw set of death entities.
        /// </summary>
        /// <param name="deathRaw">raw set of death entities</param>
        /// <param name="visitOccurrences">the visit occurrence entities for current person</param>
        /// <param name="observationPeriods">the observation period entities for current person</param>
        /// <returns>death entity</returns>
        public override Death BuildDeath(Death[] deathRaw, Dictionary<long, VisitOccurrence> visitOccurrences, ObservationPeriod[] observationPeriods)
        {
            var death = FilterDeathRecords(deathRaw).ToList();

             if (death.Any())
             {
            var maxStartDate = death.Max(d => d.StartDate);
            var result = death.Where(d => d.StartDate == maxStartDate).OrderByDescending(d => d.Primary).First();

            result.CauseConceptId = 0;

            var maxVisitStartDate = visitOccurrences.Values.Max(vo => vo.StartDate);

            if (maxVisitStartDate < result.StartDate.AddDays(32))
            {
               return result;
            }
             }

             return null;
        }
        /// <summary>
        /// Projects Enumeration of Observations from the raw set of Observation entities. 
        /// During build:
        /// override the observations start date using the start date of the corresponding observation period.
        /// </summary>
        /// <param name="observations">raw set of observations entities</param>
        /// <param name="visitOccurrences">the visit occurrences entities for current person</param>
        /// <param name="observationPeriods">the observation periods entities for current person</param>
        /// <returns>Enumeration of Observation from the raw set of Observation entities</returns>
        public override IEnumerable<Observation> BuildObservations(Observation[] observations,
         Dictionary<long, VisitOccurrence> visitOccurrences,
         ObservationPeriod[] observationPeriods)
        {
            if (observationPeriods.Length != 0)
             {
            var unique = new HashSet<Observation>();
            foreach (var observation in observations)
            {
               if (observation.ConceptId == -999) continue;
               observation.StartDate = observationPeriods[0].StartDate;
               observation.ValueAsConceptId = Convert.ToInt32(observation.UnitsConceptId);
               observation.UnitsConceptId = null;
               observation.UnitsSourceValue = null;

               unique.Add(observation);
            }

            foreach (var observation in unique)
            {
               yield return observation;
            }
             }
        }