///<summary>Returns all vaccinepat objects for pneumonia and influenza CQMs.  These are basically just medicationpat objects, so we will use the same object with two optional fields to identify them as vaccinepats instead of medicationpats.  The isGiven bool set to true will return only those with the CompletionStatus=0 (complete).  If isGiven=false, all will be returned and the logic to determine whether it was given or not will take place in calculation.  The only time we want vaccines with a status other than complete is when we are looking for vaccines NotAdministered in the influenza vaccine measure.  These NotAdministered vaccines will be due to an intolerance or allergy and entered as such.</summary>
		private static Dictionary<long,List<EhrCqmMedicationPat>> GetVaccines(List<long> listPatNums,List<string> listValueSetOIDs,DateTime dateStart,DateTime dateEnd,bool isGiven) {
			Dictionary<long,List<EhrCqmMedicationPat>> retval=new Dictionary<long,List<EhrCqmMedicationPat>>();
			//if no patients, return a new empty dictionary
			if(listPatNums!=null && listPatNums.Count==0) {
				return retval;
			}
			string command="SELECT vaccinepat.VaccinePatNum,vaccinepat.PatNum,vaccinepat.DateTimeStart,vaccinepat.DateTimeEnd,vaccinedef.CVXCode,vaccinepat.CompletionStatus "
				+"FROM vaccinepat "
				+"INNER JOIN vaccinedef ON vaccinepat.VaccineDefNum=vaccinedef.VaccineDefNum "
				+"WHERE DATE(vaccinepat.DateTimeStart) BETWEEN "+POut.Date(dateStart)+" AND "+POut.Date(dateEnd)+" ";
			if(isGiven) {
				command+="AND vaccinepat.CompletionStatus=0 ";//CompletionStatus=0 (complete)
			}
			else {
				command+="AND vaccinepat.CompletionStatus IN(0,2) ";//CompletionStatus=0 (complete) or 2 (NotAdministered), we do not care about refused or partially administered for our measures
			}
			if(listPatNums!=null && listPatNums.Count>0) {
				command+="AND vaccinepat.PatNum IN("+string.Join(",",listPatNums)+") ";
			}
			command+="ORDER BY vaccinepat.PatNum,vaccinepat.DateTimeStart DESC";
			DataTable tableAllVaccinePats=Db.GetTable(command);
			if(tableAllVaccinePats.Rows.Count==0) {
				return retval;
			}
			List<EhrCode> listEhrCodes=EhrCodes.GetForValueSetOIDs(listValueSetOIDs,false);//this list will only contain one code, CVX 33 - pneumococcal polysaccharide vaccine, 23 valent
			Dictionary<long,EhrCode> dictVaccinePatNumEhrCode=new Dictionary<long,EhrCode>();
			for(int i=tableAllVaccinePats.Rows.Count-1;i>-1;i--) {
				bool isValidVaccine=false;
				for(int j=0;j<listEhrCodes.Count;j++) {
					if(tableAllVaccinePats.Rows[i]["CVXCode"].ToString()==listEhrCodes[j].CodeValue) {
						dictVaccinePatNumEhrCode.Add(PIn.Long(tableAllVaccinePats.Rows[i]["VaccinePatNum"].ToString()),listEhrCodes[j]);
						isValidVaccine=true;
						break;
					}
				}
				if(!isValidVaccine) {
					tableAllVaccinePats.Rows.RemoveAt(i);
				}
			}
			for(int i=0;i<tableAllVaccinePats.Rows.Count;i++) {
				EhrCqmMedicationPat ehrVacPatCur=new EhrCqmMedicationPat();
				ehrVacPatCur.EhrCqmMedicationPatNum=0;
				ehrVacPatCur.EhrCqmVaccinePatNum=PIn.Long(tableAllVaccinePats.Rows[i]["VaccinePatNum"].ToString());
				ehrVacPatCur.PatNum=PIn.Long(tableAllVaccinePats.Rows[i]["PatNum"].ToString());
				ehrVacPatCur.CVXCode=tableAllVaccinePats.Rows[i]["CVXCode"].ToString();
				ehrVacPatCur.CompletionStatus=(VaccineCompletionStatus)PIn.Int(tableAllVaccinePats.Rows[i]["CompletionStatus"].ToString());
				ehrVacPatCur.DateStart=PIn.DateT(tableAllVaccinePats.Rows[i]["DateTimeStart"].ToString());
				ehrVacPatCur.DateStop=PIn.DateT(tableAllVaccinePats.Rows[i]["DateTimeEnd"].ToString());
				EhrCode ehrCodeCur=dictVaccinePatNumEhrCode[ehrVacPatCur.EhrCqmVaccinePatNum];
				ehrVacPatCur.CodeSystemName=ehrCodeCur.CodeSystem;
				ehrVacPatCur.CodeSystemOID=ehrCodeCur.CodeSystemOID;
				ehrVacPatCur.ValueSetName=ehrCodeCur.ValueSetName;
				ehrVacPatCur.ValueSetOID=ehrCodeCur.ValueSetOID;
				string descript=ehrCodeCur.Description;
				Cvx cvxCur=Cvxs.GetByCode(ehrVacPatCur.CVXCode);
				if(cvxCur!=null) {
					descript=cvxCur.Description;
				}
				ehrVacPatCur.Description=descript;//description either from cvx table or, if not in table, default to EhrCode object description
				if(retval.ContainsKey(ehrVacPatCur.PatNum)) {
					retval[ehrVacPatCur.PatNum].Add(ehrVacPatCur);
				}
				else {
					retval.Add(ehrVacPatCur.PatNum,new List<EhrCqmMedicationPat>() { ehrVacPatCur });
				}
			}
			return retval;
		}
		private static void GenerateMedPatsEntry(EhrCqmMedicationPat mPatCur) {
			_isWriterW=false;
			Start("entry","typeCode","DRIV");
			if(mPatCur.EhrCqmMedicationPatNum!=0) {//either Medication, Active: or Medication, Ordered:
				if(mPatCur.PatNote!="") {//Medication, Order:
					Start("substanceAdministration","classCode","SBADM","moodCode","RQO");
					_x.WriteComment("Plan of Care Activity Substance Administration Template");
					TemplateId("2.16.840.1.113883.10.20.22.4.42");
					_x.WriteComment("Medication Order Template");
					TemplateId("2.16.840.1.113883.10.20.24.3.47");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmMedicationPatNum.ToString());
					_x.WriteElementString("text","Medication Order: "+mPatCur.ValueSetName);
					StartAndEnd("statusCode","code","new");
				}
				else {//Medication, Active:
					Start("substanceAdministration","classCode","SBADM","moodCode","EVN");
					_x.WriteComment("Medication Activity Template");
					TemplateId("2.16.840.1.113883.10.20.22.4.16");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmMedicationPatNum.ToString());
					_x.WriteElementString("text","Medication Active: "+mPatCur.ValueSetName);
					StartAndEnd("statusCode","code","completed");
				}
				Start("effectiveTime");
				_x.WriteAttributeString("xsi","type",null,"IVL_TS");
				DateElement("low",mPatCur.DateStart);
				DateElement("high",mPatCur.DateStop);
				End("effectiveTime");
				Start("consumable");
				Start("manufacturedProduct","classCode","MANU");
				_x.WriteComment("Medication Information Template");
				TemplateId("2.16.840.1.113883.10.20.22.4.23");
				StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmMedicationPatNum.ToString()+mPatCur.CVXCode);
				Start("manufacturedMaterial");
				Start("code","code",mPatCur.RxCui.ToString(),"displayName",mPatCur.Description,"codeSystem",mPatCur.CodeSystemOID,"codeSystemName",mPatCur.CodeSystemName);
				_x.WriteAttributeString("sdtc","valueSet",null,mPatCur.ValueSetOID);
				End("code");
				End("manufacturedMaterial");
				End("manufacturedProduct");
				End("consumable");
				End("substanceAdministration");
			}
			else {//if EhrCqmMedicationPat==0 then it is a vaccine, so EhrCqmVaccinePatNum!=0
				//if NotAdministered then it is a Medication Allergy/Intolerance
				if(mPatCur.CompletionStatus==VaccineCompletionStatus.NotAdministered) {
					Start("observation","classCode","OBS","moodCode","EVN");
					_x.WriteComment("Substance or Device Allergy - Intolerance Observation");
					TemplateId("2.16.840.1.113883.10.20.24.3.90");
					_x.WriteComment("Allergy - Intolerance Observation Template");
					TemplateId("2.16.840.1.113883.10.20.22.4.7");
					_x.WriteComment("Medication Allergy Template");
					TemplateId("2.16.840.1.113883.10.20.24.3.44");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmVaccinePatNum.ToString());
					StartAndEnd("code","code","ASSERTION","displayName","Assertion","codeSystem","2.16.840.1.113883.5.4","codeSystemName","ActCode");
					StartAndEnd("statusCode","code","completed");
					Start("effectiveTime");
					DateElement("low",mPatCur.DateStart);
					DateElement("high",mPatCur.DateStop);
					End("effectiveTime");
					Start("value");
					_x.WriteAttributeString("xsi","type",null,"CD");
					Attribs("code","416098002","displayName","Drug allergy","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed);
					End("value");
					Start("participant","typeCode","CSM");
					Start("participantRole","classCode","MANU");
					Start("playingEntity","classCode","MMAT");
					Start("code","code",mPatCur.CVXCode,"displayName",mPatCur.Description,"codeSystem",mPatCur.CodeSystemOID,"codeSystemName",mPatCur.CodeSystemName);
					_x.WriteAttributeString("sdtc","valueSet",null,mPatCur.ValueSetOID);
					End("code");
					_x.WriteElementString("text","Medication Allergy: "+mPatCur.ValueSetName);
					End("playingEntity");
					End("participantRole");
					End("participant");
					End("observation");
				}
				else {//otherwise it is a Medication Administered
					Start("act","classCode","ACT","moodCode","EVN");
					_x.WriteComment("Medication Administered Template");
					TemplateId("2.16.840.1.113883.10.20.24.3.42");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmVaccinePatNum.ToString());
					StartAndEnd("code","code","416118004","displayName","Administration","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed);
					StartAndEnd("statusCode","code","completed");
					Start("effectiveTime");
					_x.WriteAttributeString("xsi","type",null,"IVL_TS");
					DateElement("low",mPatCur.DateStart);
					DateElement("high",mPatCur.DateStop);
					End("effectiveTime");
					Start("entryRelationship","typeCode","COMP");
					Start("substanceAdministration","classCode","SBADM","moodCode","EVN");
					_x.WriteComment("Medication Activity");
					TemplateId("2.16.840.1.113883.10.20.22.4.16");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmVaccinePatNum.ToString()+mPatCur.PatNum.ToString());
					_x.WriteElementString("text","Medication Administered: "+mPatCur.ValueSetName);
					StartAndEnd("statusCode","code","completed");
					Start("effectiveTime");
					_x.WriteAttributeString("xsi","type",null,"IVL_TS");
					DateElement("low",mPatCur.DateStart);
					DateElement("high",mPatCur.DateStop);
					End("effectiveTime");
					Start("consumable");
					Start("manufacturedProduct","classCode","MANU");
					_x.WriteComment("Medication Information Template");
					TemplateId("2.16.840.1.113883.10.20.22.4.23");
					StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MedPat.ToString()+mPatCur.EhrCqmMedicationPatNum.ToString()+mPatCur.CVXCode);
					Start("manufacturedMaterial");
					Start("code","code",mPatCur.CVXCode,"displayName",mPatCur.Description,"codeSystem",mPatCur.CodeSystemOID,"codeSystemName",mPatCur.CodeSystemName);
					_x.WriteAttributeString("sdtc","valueSet",null,mPatCur.ValueSetOID);
					End("code");
					End("manufacturedMaterial");
					End("manufacturedProduct");
					End("consumable");
					End("substanceAdministration");
					End("entryRelationship");
					End("act");
				}
			}
			End("entry");
			_isWriterW=true;			
		}
		///<summary>Get the medication information for medications where the code belongs to one of the value sets in the supplied list and the medication start date is in the supplied date range.  Ordered by PatNum,DateStart so first found for patient is most recent to make calculation easier.  If there is a PatNote, this is a Medication Order.  If there is no note and there is either no stop date or the stop date is after the measurement period end date, this is an active Medication.</summary>
		private static Dictionary<long,List<EhrCqmMedicationPat>> GetMedPats(List<long> listPatNums,List<string> listValueSetOIDs,DateTime dateStart,DateTime dateEnd) {
			Dictionary<long,List<EhrCqmMedicationPat>> retval=new Dictionary<long,List<EhrCqmMedicationPat>>();
			//if no patients, return a new empty dictionary
			if(listPatNums!=null && listPatNums.Count==0) {
				return retval;
			}
			List<EhrCode> listEhrCodes=EhrCodes.GetForValueSetOIDs(listValueSetOIDs,false);
			string rxcuiCodes="";
			for(int i=0;i<listEhrCodes.Count;i++) {
				if(i>0) {
					rxcuiCodes+=",";
				}
				rxcuiCodes+=listEhrCodes[i].CodeValue;
			}
			string command="SELECT medicationpat.MedicationPatNum,medicationpat.PatNum,medicationpat.DateStart,medicationpat.DateStop,medicationpat.PatNote,"
				+"(CASE WHEN medication.RxCui IS NULL THEN medicationpat.RxCui ELSE medication.RxCui END) AS RxCui "
				+"FROM medicationpat "
				+"LEFT JOIN medication ON medication.MedicationNum=medicationpat.MedicationNum "
				+"WHERE (medication.RxCui IS NOT NULL OR medicationpat.RxCui>0) "
				+"AND DATE(medicationpat.DateStart) BETWEEN "+POut.Date(dateStart)+" AND "+POut.Date(dateEnd)+" ";
				//not going to check stop date, the measures only specify 'starts before or during' without any reference to whether or not the medication has stopped
				//+"AND (YEAR(medicationpat.DateStop)<1880 OR medicationpat.DateStop>"+POut.Date(dateEnd)+") "//no valid stop date or stop date after measurement period end date
			if(listPatNums!=null && listPatNums.Count>0) {
				command+="AND medicationpat.PatNum IN("+string.Join(",",listPatNums)+") ";
			}
			if(rxcuiCodes!="") {
				command+="AND (medicationpat.RxCui IN("+rxcuiCodes+") OR medication.RxCui IN("+rxcuiCodes+")) ";
			}
			command+="ORDER BY medicationpat.PatNum,medicationpat.DateStart DESC";
			DataTable tableAllMedPats=Db.GetTable(command);
			if(tableAllMedPats.Rows.Count==0) {
				return retval;
			}
			Dictionary<long,EhrCode> dictMedicationPatNumEhrCode=new Dictionary<long,EhrCode>();
			for(int i=tableAllMedPats.Rows.Count-1;i>-1;i--) {
				for(int j=0;j<listEhrCodes.Count;j++) {
					if(tableAllMedPats.Rows[i]["RxCui"].ToString()==listEhrCodes[j].CodeValue) {
						dictMedicationPatNumEhrCode.Add(PIn.Long(tableAllMedPats.Rows[i]["MedicationPatNum"].ToString()),listEhrCodes[j]);
						break;
					}
				}
			}
			for(int i=0;i<tableAllMedPats.Rows.Count;i++) {
				EhrCqmMedicationPat ehrMedPatCur=new EhrCqmMedicationPat();
				ehrMedPatCur.EhrCqmMedicationPatNum=PIn.Long(tableAllMedPats.Rows[i]["MedicationPatNum"].ToString());
				ehrMedPatCur.EhrCqmVaccinePatNum=0;
				ehrMedPatCur.PatNum=PIn.Long(tableAllMedPats.Rows[i]["PatNum"].ToString());
				ehrMedPatCur.PatNote=tableAllMedPats.Rows[i]["PatNote"].ToString();
				ehrMedPatCur.RxCui=PIn.Long(tableAllMedPats.Rows[i]["RxCui"].ToString());
				ehrMedPatCur.DateStart=PIn.Date(tableAllMedPats.Rows[i]["DateStart"].ToString());
				ehrMedPatCur.DateStop=PIn.Date(tableAllMedPats.Rows[i]["DateStop"].ToString());
				EhrCode ehrCodeCur=dictMedicationPatNumEhrCode[ehrMedPatCur.EhrCqmMedicationPatNum];
				ehrMedPatCur.CodeSystemName=ehrCodeCur.CodeSystem;
				ehrMedPatCur.CodeSystemOID=ehrCodeCur.CodeSystemOID;
				ehrMedPatCur.ValueSetName=ehrCodeCur.ValueSetName;
				ehrMedPatCur.ValueSetOID=ehrCodeCur.ValueSetOID;
				string descript=ehrCodeCur.Description;
				RxNorm rCur=RxNorms.GetByRxCUI(ehrMedPatCur.RxCui.ToString());
				if(rCur!=null) {
					descript=rCur.Description;
				}
				ehrMedPatCur.Description=descript;//description either from rxnorm table or, if not in table, default to EhrCode object description
				if(retval.ContainsKey(ehrMedPatCur.PatNum)) {
					retval[ehrMedPatCur.PatNum].Add(ehrMedPatCur);
				}
				else {
					retval.Add(ehrMedPatCur.PatNum,new List<EhrCqmMedicationPat>() { ehrMedPatCur });
				}
			}
			return retval;
		}