internal string ConvertTimeToUtc(string effectiveTime)
 {
     if (!string.IsNullOrEmpty(effectiveTime))
     {
         try
         {
             return(XdsMetadataHelper.GetUtcTime(effectiveTime));
         }
         catch (Exception ex)
         {
             throw new Exception("Error parsing effective time in CDA document: " + ex.Message);
         }
     }
     else
     {
         return("");
     }
 }
        public XdsMetadata(
            XmlDocument cdaDocument,
            string repositoryId,

            // FormatCodes formatCode,
            string formatCode,
            string formatCodeName,

            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            int?size,
            string hash,
            bool isUpdateMetadata,
            string uuidOfDocumentToReplace)
        {
            this.formatCode     = formatCode;
            this.formatCodeName = formatCodeName;

            this.healthcareFacilityTypeCode = healthcareFacilityTypeCode;
            this.practiceSetting            = practiceSetting;
            this.isUpdateMetadata           = isUpdateMetadata;

            this.uuidOfDocumentToReplace = uuidOfDocumentToReplace;

            if (isUpdateMetadata)
            {
                this.repositoryId = repositoryId;
                this.size         = size.Value;
                this.hash         = hash;
            }

            var xnm = new XmlNamespaceManager(cdaDocument.NameTable);

            xnm.AddNamespace("cda", "urn:hl7-org:v3");
            xnm.AddNamespace("ext", "http://ns.electronichealth.net.au/Ci/Cda/Extensions/3.0");

            // Document Type
            documentTypeCode = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:code/@code", xnm));
            var classCode = GetClassCodeEnum(documentTypeCode);

            documentTypeCodeSystemName = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:code/@codeSystemName", xnm));
            documentTypeDisplayName    = classCode.GetAttributeValue <CodedValueAttribute, string>(a => a.AlternateName);
            // 14/10 Updated Spec says we should use the AlternateName for both Type and Class Code
            documentClassCodeDisplayName = classCode.GetAttributeValue <CodedValueAttribute, string>(a => a.AlternateName);

            documentTypeCode_cl07           = documentTypeCode;
            documentTypeDisplayName_cl07    = documentTypeDisplayName;
            documentTypeCodeSystemName_cl07 = documentTypeCodeSystemName;

            // The exception is for Advance Care Information = which has Advance Care Planning Document/Goals of Care Document subtypes
            if (documentTypeCode == "100.16975")
            {
                // Get document desc and code from the body
                documentTypeCode_cl07        = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry/cda:act/cda:reference/cda:externalDocument/cda:code/@code", xnm));
                documentTypeDisplayName_cl07 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry/cda:act/cda:reference/cda:externalDocument/cda:code/@displayName", xnm));

                if (documentTypeCode_cl07 == "" || documentTypeDisplayName_cl07 == "")
                {
                    throw new ArgumentException("The Advance Care Information does not reference an externalDocument.");
                }
            }

            // Get time stuff
            effectiveTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:effectiveTime/@value", xnm));
            effectiveTime = ConvertTimeToUtc(effectiveTime);

            // Set service start and stop time
            if (documentTypeCode == "51852-2")
            {
                // If document is Specialist Letter
                serviceStartTime = effectiveTime;
                serviceStopTime  = effectiveTime;
            }
            else if (documentTypeCode == "100.16764")
            {
                // If document is PCEHR Prescription Record
                var authorTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:time/@value", xnm));

                serviceStartTime = ConvertTimeToUtc(authorTime);
                serviceStopTime  = serviceStartTime;
            }
            else if (documentTypeCode == "100.16765")
            {
                // If document is PCEHR Dispense Record
                var supplyTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/" +
                                                                             "cda:component/cda:section[cda:code/@code='102.16210' and cda:code/@codeSystem='1.2.36.1.2001.1001.101']/cda:entry/" +
                                                                             "cda:substanceAdministration/cda:entryRelationship/cda:supply/cda:effectiveTime/@value", xnm));

                serviceStartTime = ConvertTimeToUtc(supplyTime);
                serviceStopTime  = serviceStartTime;
            }
            else if (documentTypeCode == "100.32001")
            {
                // R5: If document is Pathology Report - Could contain multiple records
                // Need to get LATEST Date - if multiple returned

                XmlNodeList nList = cdaDocument.SelectNodes("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component" +
                                                            "/cda:section[cda:code/@code='101.20018']/cda:component" +
                                                            "/cda:section[cda:code/@code='102.16144']/cda:entry/cda:observation" +
                                                            "/cda:entryRelationship/cda:observation[cda:code/@code='102.16156']" +
                                                            "/cda:effectiveTime/@value", xnm);
                var firstCollectionDateTime = "";
                var lastCollectionDateTime  = "";
                if (nList.Count == 1)
                {
                    firstCollectionDateTime = nList[0].Value;
                    lastCollectionDateTime  = nList[0].Value;
                }
                else if (nList.Count > 1)
                {
                    string[] sDates = new string[nList.Count];
                    for (int i = 0; i < nList.Count; i++)
                    {
                        sDates[i] = nList[i].Value;
                    }
                    Array.Sort(sDates);
                    //Get last entry
                    firstCollectionDateTime = sDates[0];
                    lastCollectionDateTime  = sDates[nList.Count - 1];
                }

                serviceStartTime = ConvertTimeToUtc(firstCollectionDateTime);
                serviceStopTime  = ConvertTimeToUtc(lastCollectionDateTime);
            }
            else if (documentTypeCode == "100.16957")
            {
                // R5: If document is Diagnostic Imaging - Could contain multiple records
                // Need to get LATEST Date - if multiple returned
                XmlNodeList nList = cdaDocument.SelectNodes("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component" +
                                                            "/cda:section/cda:component/cda:section/cda:entry/cda:observation" +
                                                            "/cda:entryRelationship/cda:act[cda:code/@code='102.16511']/cda:entryRelationship" +
                                                            "/cda:observation/cda:effectiveTime/@value", xnm);
                var firstImagingDateTime = "";
                var lastImagingDateTime  = "";
                if (nList.Count == 1)
                {
                    firstImagingDateTime = nList[0].Value;
                    lastImagingDateTime  = nList[0].Value;
                }
                else if (nList.Count > 1)
                {
                    string[] sDates = new string[nList.Count];
                    for (int i = 0; i < nList.Count; i++)
                    {
                        sDates[i] = nList[i].Value;
                    }
                    Array.Sort(sDates);
                    //Get last entry
                    firstImagingDateTime = sDates[0];
                    lastImagingDateTime  = sDates[nList.Count - 1];
                }

                serviceStartTime = ConvertTimeToUtc(firstImagingDateTime);
                serviceStopTime  = ConvertTimeToUtc(lastImagingDateTime);
            }
            else if (documentTypeCode == "100.16975")
            {
                // Advance Care Planning Document
                var authorTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:code/@code='101.16973']/cda:entry/cda:act/cda:author/cda:time/@value", xnm));
                serviceStartTime = ConvertTimeToUtc(authorTime);
                serviceStopTime  = serviceStartTime;
            }
            else
            {
                // For other document types set service start and stop time to encompassingEncounter/effectiveTime if available
                string startTime1 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/@value", xnm));
                string startTime2 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/cda:low/@value", xnm));
                string stopTime   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/cda:high/@value", xnm));

                serviceStartTime = !string.IsNullOrEmpty(startTime1)
                                       ? startTime1
                                       : !string.IsNullOrEmpty(startTime2)
                                             ? startTime2
                                             : effectiveTime;
                serviceStartTime = ConvertTimeToUtc(serviceStartTime);

                serviceStopTime = !string.IsNullOrEmpty(stopTime)
                                      ? stopTime
                                      : !string.IsNullOrEmpty(startTime1)
                                            ? startTime1
                                            : effectiveTime;

                serviceStopTime = ConvertTimeToUtc(serviceStopTime);
            }

            // As per DEXS-T123, serviceStartTime and serviceStopTime must be at least 8 chars long eg UTC formats: YYYYMMDD, YYYYMMDDhhmm, and YYYYMMDDhhmmss
            if (serviceStartTime.Length < 8)
            {
                throw new ArgumentException("The serviceStartTime retrieved from the CDA document must be at least 8 digits long.");
            }
            if (serviceStopTime.Length < 8)
            {
                throw new ArgumentException("The serviceStopTime retrieved from the CDA document must be at least 8 digits long.");
            }

            // Get Data
            languageCode = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:languageCode/@code", xnm));
            //Added as some documents do not include it. (4/2/15)
            if (String.IsNullOrEmpty(languageCode))
            {
                languageCode = "en-AU";
            }
            cdaTitle      = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:title", xnm));
            documentTitle = !string.IsNullOrEmpty(cdaTitle) ? cdaTitle : documentTypeDisplayName;

            // Process document ID
            XdsMetadataHelper.IdType?idType;
            documentId          = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:id/@root", xnm));
            documentIdExtension = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:id/@extension", xnm));
            cdaDocumentIdOid    = XdsMetadataHelper.UuidToOid(documentId, out idType);
            if (idType == null)
            {
                throw new ArgumentException("The CDA document must contain an ID which is either a UUID or an OID.");
            }
            else if (idType == XdsMetadataHelper.IdType.Oid && !string.IsNullOrEmpty(documentIdExtension))
            {
                cdaDocumentIdOid = cdaDocumentIdOid + "^" + documentIdExtension;
            }

            XmlNode authorNode = null;

            if (cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor/cda:assignedPerson", xnm) != null)
            {
                authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor", xnm);
            }

            // If document author doesn't exist, and document is pathology
            if (authorNode == null && documentTypeCode == "100.32001")
            {
                authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:code/@code='101.20018']/cda:author/cda:assignedAuthor", xnm);
            }

            bool authorDevice = false;

            if (cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor/cda:assignedAuthoringDevice", xnm) != null)
            {
                authorNode   = authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor", xnm);
                authorDevice = true;
            }

            if (authorNode == null && authorDevice == false)
            {
                throw new ArgumentException("Unable to determine document author from CDA document");
            }

            // Current Author format
            string authorOrgIdCurr   = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEmployment/ext:employerOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameCurr = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEmployment/ext:employerOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/cda:name", xnm));

            // Check Custodian if Author has no Org [Dont need code - as it would fail PCEHR validation for the NCAP 5 documents]
            string authorOrgIdCust   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:custodian/cda:assignedCustodian/cda:representedCustodianOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameCust = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:custodian/cda:assignedCustodian/cda:representedCustodianOrganization/cda:name", xnm));

            // Check Health Care Facility in Component Of if Author has no Org
            string authorOrgIdHCF   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:location/cda:healthCareFacility/cda:serviceProviderOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameHCF = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:location/cda:healthCareFacility/cda:serviceProviderOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/cda:name", xnm));

            authorQualifiedOrgId = (authorOrgIdCurr != "" ? authorOrgIdCurr : (authorOrgIdCust != "" ? authorOrgIdCust : authorOrgIdHCF));
            if (String.IsNullOrEmpty(authorOrgName))
            {
                authorOrgName = (authorOrgNameCurr != "" ? authorOrgNameCurr : (authorOrgNameCust != "" ? authorOrgNameCust : authorOrgNameHCF));
            }

            // AUTHOR
            authorQualifiedId = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-I']/@root", xnm));
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId = CheckNullValue(authorNode.SelectSingleNode("cda:assignedAuthoringDevice/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='PAI-D']/@root", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId = CheckNullValue(cdaDocument.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='PAI-D']/@root", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId          = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id/@root", xnm));
                authorQualifiedIdExtension = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id/@extension", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                throw new ArgumentException("The CDA document must contain an author identifier.");
            }
            if (string.IsNullOrEmpty(authorFamily))
            {
                authorFamily = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:family", xnm));
            }
            authorGiven  = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:given", xnm));
            authorPrefix = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:prefix", xnm));
            authorSuffix = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:suffix", xnm));

            // Author specialty
            authorSpecialty = CheckNullValue(authorNode.SelectSingleNode("cda:code/@displayName", xnm));

            // HI numbers (for header)
            hpioNumber = authorQualifiedOrgId.Replace("1.2.36.1.2001.1003.0.", "");
            ihiNumber  = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='IHI']/@root", xnm)).Replace("1.2.36.1.2001.1003.0.", "");

            // Formatted Ids
            cxFormattedPatientId = ihiNumber + "^^^&1.2.36.1.2001.1003.0&ISO";

            // Correct format (3 hats before HPII)
            if (string.IsNullOrEmpty(authorQualifiedIdExtension))
            {
                xcnFormattedAuthor = string.Format("^{0}^{1}^^^{2}^^^&{3}&ISO", authorFamily, authorGiven, authorPrefix, authorQualifiedId);
            }
            else
            {
                xcnFormattedAuthor = string.Format("^{0}^{1}^^^{2}^^^{3}&{4}&ISO", authorFamily, authorGiven, authorPrefix, authorQualifiedId, authorQualifiedIdExtension);
            }
            xonFormattedOrganisation = string.Format("{0}^^^^^^^^^{1}", authorOrgName, authorQualifiedOrgId);
        }