/*
 * Copyright 2011 NEHTA
 *
 * Licensed under the NEHTA Open Source (Apache) License; you may not use this
 * file except in compliance with the License. A copy of the License is in the
 * 'license.txt' file, which should be provided with this work.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml;
using CDA.Generator.Common.SCSModel.ConsolidatedView.Entities;
using JetBrains.Annotations;
using Nehta.VendorLibrary.CDA.Common.Enums;
using Nehta.VendorLibrary.CDA.Generator.Enums;
using Nehta.VendorLibrary.CDA.SCSModel;
using Nehta.VendorLibrary.CDA.SCSModel.Common;
using Nehta.VendorLibrary.CDA.SCSModel.Common.Entities;
using Nehta.VendorLibrary.CDA.SCSModel.DischargeSummary.Interfaces;
using Nehta.VendorLibrary.Common;
using Entitlement = Nehta.VendorLibrary.CDA.SCSModel.Common.Entitlement;
using IServiceProvider = Nehta.VendorLibrary.CDA.SCSModel.IServiceProvider;
using Participant = Nehta.VendorLibrary.CDA.SCSModel.Common.Participant;

namespace Nehta.VendorLibrary.CDA.Common
{
    /// <summary>
    /// This class encapsulates all the properties that are common between the various CDA documents
    /// </summary>
    [Serializable]
    [DataContract]
    public class BaseCDAModel
    {
        #region Constants
        private const String HEALTH_IDENTIFIER_QUALIFIER = "1.2.36.1.2001.1003.0.";
        private const String EXTERNAL_HEALTH_IDENTIFIER_QUALIFIER = "1.2.36.1.2001.1005.41.";



        #endregion

        #region Properties

        /// <summary>
        /// The status of this CDA document
        /// </summary>
        [CanBeNull]
        [DataMember]
        public ISO8601DateTime DocumentCreationTime { get; set; }

        /// <summary>
        /// The status of this CDA document
        /// </summary>
        [CanBeNull]
        [DataMember]
        public DocumentStatus DocumentStatus { get; set; }

        /// <summary>
        /// Indicates if the CDA document should include a logo
        /// </summary>
        [CanBeNull]
        [DataMember]
        public Boolean IncludeLogo { get; set; }

        /// <summary>
        /// This is optional field, this will copy the logo file to the output directory.
        /// If LogoPath is not populated and IncludeLogo is true the assumption is the user will 
        /// manually copy the image file to the output directory.
        /// </summary>
        [CanBeNull]
        [DataMember]
        public string LogoPath { get; set; }

        /// <summary>
        /// Show Administration Observations in the Narrative
        /// NOTE: This currently fails schematron V 3.0.4.0
        ///       This is a valid only for point to point usage
        /// </summary>
        [CanBeNull]
        [DataMember]
        public Boolean? ShowAdministrativeObservations { get; set; }
        #endregion

        #region static methods

        /// <summary>
        /// Creates a legal authenticator
        /// </summary>
        /// <returns>An Authenticator Object</returns>
        public static IParticipationLegalAuthenticator CreateLegalAuthenticator()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates an authenticator
        /// </summary>
        /// <returns>An Authenticator Object</returns>
        public static IParticipationLegalAuthenticator CreateAuthenticator()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates an information recipient
        /// </summary>
        /// <returns>Recipient</returns>
        public static IParticipationInformationRecipient CreateInformationRecipient()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates a person that is constrained down to an IPersonName
        /// </summary>
        /// <returns>(IPersonName) person</returns>
        public static IPersonName CreatePersonName()
        {
            return new PersonName();
        }

        /// <summary>
        /// Creates a person constrained for a Person With Organisation
        /// </summary>
        /// <returns>(IPerson) Person With Organisation</returns>
        public static IPersonWithOrganisation CreatePersonWithOrganisation()
        {
            return new Person();
        }

        /// <summary>
        /// Creates a person constrained for a Person Consumer
        /// </summary>
        /// <returns>(IPerson) Person With Organisation</returns>
        public static IPersonConsumer CreatePersonConsumer()
        {
            return new Person();
        }

        /// <summary>
        /// Creates a person constrained for a IPersonWithRelationship
        /// </summary>
        /// <returns>(IPersonWithRelationship) Person With Relationship</returns>
        public static IPersonWithRelationship CreatePersonWithRelationship()
        {
            return new Person();
        }
        
        /// <summary>
        /// Creates a address that is constrained down to an IAddress
        /// </summary>
        /// <returns>(IAddress) address</returns>
        public static IAddress CreateAddress()
        {
            return new Address();
        }

        /// <summary>
        /// Creates an international address
        /// </summary>
        /// <returns>InternationalAddress</returns>
        public static InternationalAddress CreateInternationalAddress()
        {
            return new InternationalAddress();
        }

        /// <summary>
        /// Creates a recipient
        /// </summary>
        /// <returns>A Recipient Object</returns>
        public static IInformationRecipient CreateRecipient()
        {
            return new Participant();
        }

        /// <summary>
        /// Creates an ElectronicCommunicationDetail Object
        /// </summary>
        /// <param name="address">Address</param>
        /// <param name="medium">Medium; E.g. Email</param>
        /// <param name="usage">Usage; E.g. Home</param>
        /// <returns>An ElectronicCommunicationDetail Object</returns>
        public static ElectronicCommunicationDetail CreateElectronicCommunicationDetail(string address, ElectronicCommunicationMedium medium, ElectronicCommunicationUsage usage)
        {
            return new ElectronicCommunicationDetail
            {
                Address = address,
                Medium = medium,
                Usage = new List<ElectronicCommunicationUsage> { usage }
            };
        }

        /// <summary>
        /// Creates an ElectronicCommunicationDetail Object
        /// </summary>
        /// <param name="address">Address</param>
        /// <param name="medium">Medium; E.g. Email</param>
        /// <param name="usage">Usage; E.g. Home</param>
        /// <returns>An ElectronicCommunicationDetail Object</returns>
        public static ElectronicCommunicationDetail CreateElectronicCommunicationDetail(string address, ElectronicCommunicationMedium medium, List<ElectronicCommunicationUsage> usage)
        {
            return new ElectronicCommunicationDetail
            {
                Address = address,
                Medium = medium,
                Usage = usage
            };
        }

        /// <summary>
        /// Creates an ElectronicCommunicationDetail Object
        /// </summary>
        /// <returns>An ElectronicCommunicationDetail Object</returns>
        public static ElectronicCommunicationDetail CreateElectronicCommunicationDetail()
        {
            return new ElectronicCommunicationDetail();
        }

        /// <summary>
        /// Creates an Australian Address
        /// </summary>
        /// <returns>(AustralianAddress) Australian Address</returns>
        public static AustralianAddress CreateAustralianAddress()
        {
            return new AustralianAddress();
        }

        
        /// <summary>
        /// Creates a custodian
        /// </summary>
        /// <returns>(IParticipationCustodian) Custodian</returns>
        public static IParticipationCustodian CreateCustodian()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates a custodian
        /// </summary>
        /// <returns>(Custodian) Custodian</returns>
        public static ICustodian CreateParticipantCustodian()
        {
            return new Participant();
        }

        /// <summary>
        /// Creates a participation constrained down to an IParticipationSubjectOfCare
        /// </summary>
        /// <returns>(IParticipationSubjectOfCare) Participation</returns>
        public static IParticipationSubjectOfCare CreateSubjectOfCare()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates a participation constrained down to an IParticipationDocumentAuthor
        /// </summary>
        /// <returns>(IParticipationDocumentAuthor) Participation</returns>
        public static IParticipationDocumentAuthor CreateAuthor()
        {
            return new Participation();
        }

        /// <summary>
        /// Creates a participation constrained down to an ISubjectOfCare
        /// </summary>
        /// <returns>(ISubjectOfCare) Participation</returns>
        public static ISubjectOfCare CreateParticipantForSubjectOfCare()
        {
            return new Participant();
        }

        
        /// <summary>
        /// Creates a participation constrained down to an ISubjectOfCare
        /// </summary>
        /// <returns>(ISubjectOfCare) Participation</returns>
        public static ILegalAuthenticator CreateParticipantForLegalAuthenticator()
        {
            return new Participant();
        }

       /// <summary>
        /// Creates a person constrained down to an IPersonSubjectOfCare
        /// </summary>
        /// <returns>(IPersonSubjectOfCare) Person</returns>
        public static IPersonSubjectOfCare CreatePersonForSubjectOfCare()
        {
            return new Person();
        }

        /// <summary>
        /// Creates a person constrained down to an IPerson
        /// </summary>
        /// <returns>(IPerson) Person</returns>
        public static IPerson CreatePerson()
        {
            return new Person();
        }

        /// <summary>
        /// Creates a person constrained down to an IInformationRecipient
        /// </summary>
        /// <returns>(IInformationRecipient) Participant</returns>
        public static  IInformationRecipient CreateParticipantForInformationRecipient()
        {
            return new Participant();
        }

        /// <summary>
        /// Create Participant for a IServiceRequester  
        /// </summary>
        /// <returns>(IServiceRequester) Participant</returns>
        public static IServiceRequester CreateParticipantForServiceRequester()
        {
          return new Participant();
        }

        /// <summary>
        /// Create Participation for a IParticipationServiceProvider
        /// </summary>
        /// <returns>(IParticipationServiceRequester) Participation</returns>
        public static IParticipationServiceRequester CreateServiceRequester()
        {
          return new Participation();
        }

        /// <summary>
        /// Create Participant for a ServiceProvider   
        /// </summary>
        /// <returns>(IServiceProvider) Participant</returns>
        public static IServiceProvider CreateParticipantForServiceProvider()
        {
          return new Participant();
        }

        /// <summary>
        /// Create Participation for a IParticipationServiceProvider
        /// </summary>
        /// <returns>(IParticipationServiceProvider) Participation</returns>
        public static IParticipationServiceProvider CreateServiceProvider()
        {
          return new Participation();
        }

        /// <summary>
        /// Creates an entitlement
        /// </summary>
        /// <returns>An Entitlement Object</returns>
        public static Entitlement CreateEntitlement()
        {
            return new Entitlement();
        }

        /// <summary>
        /// Create an MedicalRecordNumber object
        /// </summary>
        /// <param name="medicalRecordNumber">The Medical Record Number</param>
        /// <param name="root">The organisations HPIO</param>
        /// <param name="organisationName">The Organisatiosn Name</param>
        /// <returns>MRN</returns>
        public static Identifier CreateMedicalRecordNumber(String medicalRecordNumber, String root, String organisationName)
        {
            return  
            CreateIdentifier
            (
                    organisationName,
                    null,
                    medicalRecordNumber,
                    root,
                    CreateCodableText
                    (
                        "MR",
                        CodingSystem.HL7IdentifierType,
                        null,
                        null,
                        null
                    )
             );
        }

        /// <summary>
        /// Create a CreateMedicareNumber object
        /// </summary>
        /// <param name="medicalRecordNumber">MedicareNumberType</param>
        /// <param name="medicareNumber">medicalRecordNumber</param>
        /// <returns>MRN</returns>
        public static Identifier CreateMedicareNumber(MedicareNumberType medicalRecordNumber, String medicareNumber)
        {
            return 
            CreateIdentifier
            (
                    medicalRecordNumber.GetAttributeValue<NameAttribute, string>(x => x.Name),
                    null,
                    medicareNumber,
                    medicalRecordNumber.GetAttributeValue<NameAttribute, string>(x => x.Code),
                    CreateCodableText
                    (
                        "MC",
                        CodingSystem.HL7IdentifierType,
                        null,
                        null,
                        null
                     )
            );
        }


        /// <summary>
        /// Creates a DVA entitlement.
        /// </summary>
        /// <param name="dvaNumber">DVA number.</param>
        /// <param name="entitlementType">Entitlement type.</param>
        /// <param name="validityDuration">Entitlement validity duration.</param>
        /// <returns>Entitlement.</returns>
        public static Entitlement CreateDvaEntitlement(string dvaNumber, EntitlementType entitlementType, CdaInterval validityDuration)
        {
            Entitlement entitlement = new Entitlement();
            entitlement.Id = CreateDvaNumber(dvaNumber, entitlementType);
            entitlement.Type = entitlementType;
            entitlement.ValidityDuration = validityDuration;

            return entitlement;
        }

        /// <summary>
        /// Create a DVA entitlement with no validity duration.
        /// </summary>
        /// <param name="dvaNumber">DVA number.</param>
        /// <param name="entitlementType">Entitlement type.</param>
        /// <returns>Entitlement.</returns>
        public static Entitlement CreateDvaEntitlement(string dvaNumber, EntitlementType entitlementType)
        {
            return CreateDvaEntitlement(dvaNumber, entitlementType, null);
        }

        /// <summary>
        /// Create a DVA number object
        /// </summary>
        /// <param name="dvaNumber">DVA number</param>
        /// <param name="entitlementType">Entitlement type</param>
        /// <returns>DVA identifier</returns>
        public static Identifier CreateDvaNumber(string dvaNumber, EntitlementType entitlementType)
        {
            if (dvaNumber == null)
            {
                throw new ArgumentException("'dvaNumber' cannot be null");
            }

            if (!(entitlementType == EntitlementType.RepatriationHealthOrangeBenefits ||
                  entitlementType == EntitlementType.RepatriationHealthGoldBenefits || 
                  entitlementType == EntitlementType.RepatriationHealthWhiteBenefits))
            {
                throw new ArgumentException("Entitlement type must be either: RepatriationHealthOrangeBenefits, " +
                    "RepatriationHealthGoldBenefits or RepatriationHealthWhiteBenefits");                                
            }            

            return
            CreateIdentifier
            (
                    "Department of Veterans' Affairs",
                    null,
                    dvaNumber,
                    "2.16.840.1.113883.3.879.270091",
                    CreateCodableText
                    (
                        entitlementType.GetAttributeValue<NameAttribute, string>(x => x.Code),
                        CodingSystem.NCTISEntitlementTypeValues,
                        entitlementType.GetAttributeValue<NameAttribute, string>(x => x.Name),
                        null,
                        null
                     )
            );
        }

        /// <summary>
        /// Creates an identifier
        /// </summary>
        /// <param name="identifierType">An Identifier Type</param>
        /// <param name="value">A Value</param>
        /// <returns>An Identifier Object</returns>
        public static Identifier CreateHealthIdentifier(HealthIdentifierType identifierType, String value)
        {
            return new Identifier
            {
                AssigningAuthorityName = identifierType.GetAttributeValue<NameAttribute, String>(x => x.Code),
                AssigningGeographicArea = identifierType.GetAttributeValue<NameAttribute, String>(x => x.Name),
                Root = identifierType.GetAttributeValue<NameAttribute, String>(x => x.Extension) + value
            };
        }

        /// <summary>
        /// Creates an identifier
        /// </summary>
        /// <param name="assigningAuthorityName">Assigning Authority Name</param>
        /// <param name="assigningGeographicArea">Assigning Geographic Area</param>
        /// <param name="extension">extension</param>
        /// <param name="root">root</param>
        /// <param name="code">A Code</param>
        /// <returns>An Identifier Object</returns>
        public static Identifier CreateIdentifier(
            string assigningAuthorityName,
            HealthcareIdentifierGeographicArea? assigningGeographicArea, 
            string extension, 
            string root, 
            ICodableText code)
        {
            Identifier identifier = null;

            identifier = new Identifier
            {
                AssigningAuthorityName = assigningAuthorityName,
                AssigningGeographicArea = assigningGeographicArea.HasValue ? assigningGeographicArea.GetAttributeValue<NameAttribute, String>(x => x.Name) : null,
                Extension = extension,
                Root = root,
                Code = code
            };

          return identifier;
        }

        /// <summary>
        /// Creates an Employee Number Identifier
        /// </summary>
        /// <param name="organisationName">Organisation Name</param>
        /// <param name="extension">extension</param>
        /// <param name="hpio">hpio</param>
        /// <returns>An Identifier Object</returns>
        public static Identifier CreateEmployeeNumberIdentifier(
            string organisationName,
            string extension,
            string hpio)
        {
          Identifier identifier = null;

          identifier = new Identifier
          {
            AssigningAuthorityName = organisationName,
            Extension = extension,
            Root = EXTERNAL_HEALTH_IDENTIFIER_QUALIFIER + hpio,
            Code = CreateCodableText("EI", CodingSystem.HL7IdentifierType, null, null, null)
          };

          return identifier;
        }

        /// <summary>
        /// Creates an identifier
        /// </summary>
        /// <param name="extension">extension</param>
        /// <param name="root">root</param>
        /// <returns>An Identifier Object</returns>
        public static Identifier CreateIdentifier(string root, string extension)
        {
          Identifier identifier = null;

          identifier = new Identifier
          {
              Extension = extension,
              Root = root,
          };

          return identifier;
        }

        /// <summary>
        /// Creates an instance identifier  
        /// </summary>
        /// <param name="root">A UUID or OID</param>
        /// <param name="extension">Unique identifier within the scope of the identifier root</param>
        /// <returns></returns>
        public static InstanceIdentifier CreateInstanceIdentifier(
            string root,
            string extension)
        {
            return new InstanceIdentifier()
                       {
                           Root = root,
                           Extension = extension
                       };
        }

        /// <summary>
        /// Creates a person constrained down to an IAuthor
        /// </summary>
        /// <returns>(IAuthor) Person</returns>
        public static IAuthor CreateParticipantForAuthor()
        {
            return new Participant();
        }

        /// <summary>
        /// Creates an organisation constrained down to an IOrganisation
        /// </summary>
        /// <returns>(IOrganisationDischargeSummary) Organisation</returns>
        public static IOrganisation CreateOrganisation()
        {
            return new Organisation();
        }

        /// <summary>
        /// Creates an employment organisation.
        /// </summary>
        /// <returns>Employment organisation.</returns>
        public static IEmploymentOrganisation CreateEmploymentOrganisation()
        {
            return new EmploymentOrganisation();
        }

        /// <summary>
        /// Creates an Organisation constrained down to an IOrganisationName
        /// </summary>
        /// <returns>(IOrganisationName) Organisation</returns>
        public static IOrganisationName CreateOrganisationName()
        {
            return new Organisation();
        }
        /// <summary>
        /// Creates a Encapsulated Data Item
        /// </summary>
        /// <returns>An EncapsulatedData</returns>
        public static EncapsulatedData CreateEncapsulatedData()
        {
          return new EncapsulatedData();
        }
        /// <summary>
        /// Creates a ExternalData Attachment
        /// </summary>
        /// <returns>An ExternalData</returns>
        public static ExternalData CreateExternalData()
        {
            return new ExternalData();
        }
        
        /// <summary>
        /// Creates a quantity
        /// </summary>
        /// <returns>A Quantity Object</returns>
        public static Quantity CreateQuantity()
        {
            return new Quantity();
        }
        
        /// <summary>
        /// Creates a quantity range
        /// </summary>
        /// <returns>QuantityRange</returns>
        public static QuantityRange CreateQuantityRange()
        {
            return new QuantityRange();
        }

        /// <summary>
        /// Creats a role
        /// </summary>
        /// <returns>CodableText</returns>
        public static ICodableText CreateRole()
        {
            return new CodableText();
        }
        /// <summary>
        /// Creats a role
        /// </summary>
        /// <returns>CodableText</returns>
        public static ICodableText CreateRole(String code, CodingSystem? codeSystem, String displayName, String originalText, List<ICodableTranslation> translations)
        {
            return CreateCodableText(code, codeSystem, displayName, originalText, translations);
        }

        /// <summary>
        /// Creates a participation Role
        /// </summary>
        /// <param name="code">role participation</param>
        /// <param name="codeSystem">The code system associated with the code</param>
        /// <param name="displayName">The display name associated with the code</param>
        /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
        /// <param name="translations">Any translations that are associated with this participation role</param>
        /// <returns>CodableText defining a participation role</returns>
        public static ICodableText CreateParticipationRole(String code, CodingSystem? codeSystem, String displayName, String originalText, List<ICodableTranslation> translations)
        {
            return CreateCodableText(code, codeSystem, displayName, originalText, translations);
        }
        
        /// <summary>
        /// Creates a codableText object that contains and defines a role
        /// </summary>
        /// <param name="code">A code</param>
        /// <param name="codeSystem">The code system associated with the code</param>
        /// <param name="displayName">The display name associated with the code</param>
        /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
        /// <returns>A codable text representing the role</returns>
        public static ICodableText CreateRole(String code, CodingSystem? codeSystem, String displayName, String originalText)
        {
            var codableText = new CodableText();

            if (codeSystem.HasValue)
            {
                codableText.DisplayName = displayName;
                codableText.Code = code;
                codableText.CodeSystemCode = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Code);
                codableText.CodeSystemName = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Name);
                codableText.CodeSystemVersion = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Version);
            }

            if (!originalText.IsNullOrEmptyWhitespace())
                codableText.OriginalText = originalText;

            return codableText;
        }

        /// <summary>
        /// Creates a codableText object that contains and defines a role as defined by an occupation
        /// </summary>
        /// <param name="occupation">The occupation defining the role</param>
        /// <returns>A codable text representing the role</returns>
        public static ICodableText CreateRole(Occupation occupation)
        {
            return new CodableText
            {
                DisplayName = occupation != Occupation.Undefined ? occupation.GetAttributeValue<NameAttribute, String>(x => x.Name) : String.Empty,
                Code = occupation != Occupation.Undefined ? occupation.GetAttributeValue<NameAttribute, String>(x => x.Code) : String.Empty,
                CodeSystem = CodingSystem.ANZSCO
            };
        }

        /// <summary>
        /// Creates a codableText object that contains and defines a role as a nullFlavor
        /// </summary>
        /// <param name="nullFlavor"></param>
        /// <returns>CodableText</returns>
        public static ICodableText CreateRole(NullFlavour nullFlavor)
        {
            var codableText = new CodableText();
            codableText.NullFlavour = nullFlavor;
            return codableText;
        }

      /// <summary>
      /// Creates a result group name
      /// </summary>
      /// <param name="code">result group name code</param>
      /// <param name="codeSystem">The code system associated with the code</param>
      /// <param name="displayName">The display name associated with the code</param>
      /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
      /// <param name="translations">Any translations that are associated with this result group name</param>
      /// <returns>CodableText defining a result group name</returns>
      public static ICodableText CreateResultGroupName(String code, CodingSystem? codeSystem, String displayName, String originalText, List<ICodableTranslation> translations)
      {
        return CreateCodableText(code, codeSystem, displayName, originalText, translations);
      }

      /// <summary>
      /// Creates a result group name
      /// </summary>
      /// <param name="nullFlavor">nullFlavor</param>
      /// <returns>CodableText defining a result group name</returns>
      public static ICodableText CreateResultGroupName(NullFlavour nullFlavor)
      {
        return CreateCodableText(nullFlavor);
      }

      /// <summary>
      /// Creates a codableText object
      /// </summary>
      /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
      /// <returns>CodableText</returns>
      public static ICodableText CreateCodableText(String originalText)
      {
          return CreateCodableText(null, null, null, originalText, null);
      }

        /// <summary>
        /// Creates a codableText object
        /// </summary>
        /// <param name="code">A code</param>
        /// <param name="codeSystem">The code system associated with the code</param>
        /// <param name="displayName">The display name associated with the code</param>
        /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
        /// <param name="translations">Any translations that are associated with this codable text</param>
        /// <returns>CodableText</returns>
        public static ICodableText CreateCodableText(String code, CodingSystem? codeSystem, String displayName, String originalText, List<ICodableTranslation> translations)
        {
            var codableText = new CodableText();

            if (codeSystem.HasValue)
            {
                codableText.DisplayName = displayName;
                codableText.Code = code;
                codableText.CodeSystemCode = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Code);
                codableText.CodeSystemName = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Name);
                codableText.CodeSystemVersion = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Version);
            }

            if (!originalText.IsNullOrEmptyWhitespace())
                codableText.OriginalText = originalText;

            if (translations != null && translations.Any())
                codableText.Translations = translations;

            return codableText;
        }

        /// <summary>
        /// Creates a codableText object
        /// </summary>
        /// <param name="code">A code</param>
        /// <param name="codeSystemCode">The Code for the CodableText</param>
        /// <param name="codeSystemName">The CodeSystemName for the CodableText</param>
        /// <param name="codeSystemVersion">The CodeSystemVersion \ for the CodableText</param>
        /// <param name="displayName">The display name associated with the code</param>
        /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
        /// <param name="translations">Any translations that are associated with this codable text</param>
        /// <returns>CodableText</returns>
        public static ICodableText CreateCodableText(String code, string codeSystemCode, string codeSystemName, string codeSystemVersion,  String displayName, String originalText, List<ICodableTranslation> translations)
        {
          var codableText = new CodableText();

            codableText.DisplayName = displayName;
            codableText.Code = code;
            codableText.CodeSystemCode = codeSystemCode;
            codableText.CodeSystemName = codeSystemName;
            codableText.CodeSystemVersion = codeSystemVersion;

          if (!originalText.IsNullOrEmptyWhitespace())
            codableText.OriginalText = originalText;

          if (translations != null && translations.Any())
            codableText.Translations = translations;

          return codableText;
        }

        /// <summary>
        /// Creates a codable text nullable object
        /// </summary>
        /// <param name="nullFlavor"></param>
        /// <returns>CodableText</returns>
        public static ICodableText CreateCodableText(NullFlavour nullFlavor)
        {
           var codableText = new CodableText();
           codableText.NullFlavour = nullFlavor;
           return codableText;
        }


        /// <summary>
        /// Creates a codable text object
        /// </summary>
        /// <param name="code">code</param>
        /// <param name="codeSystem">The code system associated with the code</param>
        /// <param name="displayName">The display name associated with the code</param>
        /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
        /// <returns>CodableText</returns>
        public static ICodableTranslation CreateCodableTranslation(String code, CodingSystem? codeSystem, String displayName, String originalText)
        {
            var codableText = new CodableText();

            if (codeSystem.HasValue)
            {
                codableText.DisplayName = displayName;
                codableText.Code = code;
                codableText.CodeSystemCode = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Code);
                codableText.CodeSystemName = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Name);
                codableText.CodeSystemVersion = codeSystem.Value.GetAttributeValue<NameAttribute, string>(a => a.Version);
            }

            if (!originalText.IsNullOrEmptyWhitespace())
                codableText.OriginalText = originalText;

            return codableText;
        }

        /// <summary>
        /// Creates an electronic communication detail
        /// </summary>
        /// <param name="address">Address, E.g.. Phone number, Email address etc</param>
        /// <param name="medium">Medium, E.g. Telephone</param>
        /// <param name="usage">Usage, E.g. Business</param>
        /// <returns>ElectronicCommunicationDetail</returns>
        public static ElectronicCommunicationDetail CreateElectronicCommunicationDetail(string address, ElectronicCommunicationMedium? medium, ElectronicCommunicationUsage? usage)
        {
            var electronicCommunicationDetail = new ElectronicCommunicationDetail
            {
                Address = address
            };

            if (medium.HasValue)
            {
                electronicCommunicationDetail.Medium = medium.Value;
            }

            if (usage.HasValue)
            {
                electronicCommunicationDetail.Usage = new List<ElectronicCommunicationUsage> {usage.Value};
            }

            return electronicCommunicationDetail;
        }

        /// <summary>
        /// Creates an electronic communication detail
        /// </summary>
        /// <param name="address">Address, E.g.. Phone number, Email address etc</param>
        /// <param name="medium">Medium, E.g. Telephone</param>
        /// <param name="usage">Usage, E.g. Business</param>
        /// <returns>ElectronicCommunicationDetail</returns>
        public static ElectronicCommunicationDetail CreateElectronicCommunicationDetail(string address, ElectronicCommunicationMedium? medium, List<ElectronicCommunicationUsage> usage)
        {
            var electronicCommunicationDetail = new ElectronicCommunicationDetail
            {
                Address = address
            };

            if (medium.HasValue)
            {
                electronicCommunicationDetail.Medium = medium.Value;
            }

            if (usage != null)
            {
                electronicCommunicationDetail.Usage = usage;
            }

            return electronicCommunicationDetail;
        }


        /// <summary>
        /// Create a valid Guid
        /// </summary>
        /// <returns></returns>
        public static string CreateGuid()
        {
            return
                (
                    new UniqueId().ToString().Replace("urn:uuid:", "")
                );
        }

        /// <summary>
        /// Creates an interval with a start and end date/time.
        /// </summary>
        /// <param name="start">Start date/time.</param>
        /// <param name="end">End date/time.</param>
        /// <returns>Interval.</returns>
        public static CdaInterval CreateInterval(ISO8601DateTime start, ISO8601DateTime end)
        {
            return CdaInterval.CreateLowHigh(start, end);
        }

        /// <summary>
        /// Creates an interval from a width.
        /// </summary>
        /// <param name="widthValue">Interval width.</param>
        /// <param name="unit">Unit.</param>
        /// <returns>Interval.</returns>
        public static CdaInterval CreateInterval(string widthValue, TimeUnitOfMeasure unit)
        {
            return CdaInterval.CreateWidth(widthValue, unit);
        }

        /// <summary>
        /// Creates an interval for a high value.
        /// </summary>
        /// <param name="high">Interval high.</param>
        /// <returns>Interval.</returns>
        public static CdaInterval CreateInterval(ISO8601DateTime high)
        {
            return CdaInterval.CreateHigh(high);
        }

        /// <summary>
        /// Creates a CreateStructuredBodyFile (External Data)
        /// </summary>
        /// <returns>ExternalData</returns>
        public static ExternalData CreateStructuredBodyFile()
        {
            return new ExternalData();
        }

        /// <summary>
        /// Creates and Hydrates a PhysicalDetails object
        /// </summary>
        /// <returns>An Empty PhysicalDetails object</returns>
        public static PhysicalDetails CreatePhysicalDetails()
        {
            return new PhysicalDetails();
        }

        /// <summary>
        /// Creates an Imaging Examination Request
        /// </summary>
        /// <returns>(IImagingExaminationRequest) Request</returns>
        public static IImagingExaminationRequest CreateImagingExaminationRequest()
        {
            return new Request();
        }

        /// <summary>
        /// Creates a pathology test result
        /// </summary>
        /// <returns>PathologyTestResult</returns>
        public static PathologyTestResult CreatePathologyTestResult()
        {
            return new PathologyTestResult();
        }

        /// <summary>
        /// Creates a specimen detail
        /// </summary>
        /// <returns>SpecimenDetail</returns>
        public static SpecimenDetail CreateSpecimenDetail()
        {
            return new SpecimenDetail();
        }

        /// <summary>
        /// Creates an anatomical site
        /// </summary>
        /// <returns>AnatomicalSite</returns>
        public static AnatomicalSite CreateAnatomicalSite()
        {
            return new AnatomicalSite();
        }

        /// <summary>
        /// Creates a Link Object
        /// </summary>
        /// <returns>Link</returns>
        public static Link CreateLink()
        {
          return new Link();
        }

      /// <summary>
        /// Creates and Hydrates a PhysicalDetails object
        /// </summary>
        /// <param name="value">The Value</param>
        /// <param name="units">The Unit</param>
        /// <param name="image">The ExternalData</param>
        /// <returns>A Hydrated PhysicalDetails object</returns>
        public static PhysicalDetails CreatePhysicalDetails(String value, String units, ExternalData image)
        {
            PhysicalDetails physicalDetails = null;

            if (!value.IsNullOrEmptyWhitespace() && !units.IsNullOrEmptyWhitespace())
            {
                physicalDetails = CreatePhysicalDetails();

                physicalDetails.Volume = CreateQuantity();
                physicalDetails.Volume.Value = value;
                physicalDetails.Volume.Units = units;
            }

            if (image != null)
            {
                physicalDetails.Image = image;
            }

            return physicalDetails;
        }

      /// <summary>
      /// Creates a OtherTestResult
      /// </summary>
      /// <returns>OtherTestResult</returns>
      public static OtherTestResult CreateOtherTestResult()
      {
        return new OtherTestResult();
      }

      /// <summary>
      /// Creates a External Concept Identifier
      /// </summary>
      /// <returns>Identifier</returns>
      public static Identifier CreateExternalConceptIdentifier(ExternalConcepts externalConcepts, string extention)
      {
        return CreateIdentifier(
          CodingSystem.AustralianPBSManufacturerCode.GetAttributeValue<NameAttribute, String>(x => x.Name),
          null,
          extention,
          String.Format("{0}.{1}", CodingSystem.NCTISExternalConcepts.GetAttributeValue<NameAttribute, String>(x => x.Code), externalConcepts.GetAttributeValue<NameAttribute, String>(x => x.Code)),
          null
          );
      }

      #region 

      /// <summary>
      /// Creates a Prescriber
      /// </summary>
      /// <returns></returns>
      public static IParticipationPrescriber CreatePrescriber()
      {
        return new Participation();
      }

      /// <summary>
      /// Creates a participant for a prescriber
      /// </summary>
      /// <returns></returns>
      public static IPrescriber CreateParticipantForPrescriber()
      {
        return new Participant();
      }

      /// <summary>
      /// Creates a person
      /// </summary>
      /// <returns>(IPersonPrescriber) Person</returns>
      public static IPersonPrescriber CreatePersonForPrescriber()
      {
        return new Person();
      }

      /// <summary>
      /// Creates a Prescriber organisation
      /// </summary>
      /// <returns>(IParticipationPrescriberOrganisation) Participation</returns>
      public static IParticipationPrescriberOrganisation CreatePrescriberOrganisation()
      {
        return new Participation();
      }

      /// <summary>
      /// Creates a participant for a prescriber organisation
      /// </summary>
      /// <returns>(IPrescriberOrganisation) Participant</returns>
      public static IPrescriberOrganisation CreateParticipantForPrescriberOrganisation()
      {
        return new Participant();
      }

      /// <summary>
      /// Creates a dispenser
      /// </summary>
      /// <returns></returns>
      public static IParticipationDispenser CreateDispenser()
      {
        return new Participation();
      }

      /// <summary>
      /// Creates a participant for a dispenser
      /// </summary>
      /// <returns></returns>
      public static IDispenser CreateParticipantForDispenser()
      {
        return new Participant();
      }

      /// <summary>
      /// Creates a person
      /// </summary>
      /// <returns>(IPersonDispenser) Person</returns>
      public static IPersonDispenser CreatePersonForDispenser()
      {
        return new Person();
      }

      /// <summary>
      /// Creates a Dispenser organisation
      /// </summary>
      /// <returns>(IParticipationDispenserOrganisation) Participation</returns>
      public static IParticipationDispenserOrganisation CreateDispenserOrganisation()
      {
        return new Participation();
      }

      /// <summary>
      /// Creates a participant for a dispenser organisation
      /// </summary>
      /// <returns>(IDispenserOrganisation) Participant</returns>
      public static IDispenserOrganisation CreateParticipantForDispenserOrganisation()
      {
        return new Participant();
      }

      #endregion

      /// <summary>
      /// Creates a medicine
      /// </summary>
      /// <param name="code">role medicine</param>
      /// <param name="codeSystem">The code system associated with the code</param>
      /// <param name="codeSystemVersion">The current CodeSystemVersion for the AMT SnomedCode</param>
      /// <param name="displayName">The display name associated with the code</param>
      /// <param name="originalText">Original text, usually applicable in the absence of a code and display name</param>
      /// <param name="translations">Any translations that are associated with this medicine</param>
      /// <returns>CodableText defining a medicine</returns>
      public static ICodableText CreateMedicine(String code, CodingSystem codeSystem, String codeSystemVersion, String displayName, String originalText, List<ICodableTranslation> translations)
      {
        return CreateCodableText(code,
                                 codeSystem.GetAttributeValue<NameAttribute, string>(a => a.Code),
                                 codeSystem.GetAttributeValue<NameAttribute, string>(a => a.Name),
                                 codeSystemVersion,
                                 displayName,
                                 originalText,
                                 translations);
      }

      #endregion

        #region Constructors
        /// <summary>
        /// Default Constructor
        /// </summary>
        public BaseCDAModel()
        {
            ShowAdministrativeObservations = true;
        }
        #endregion
    }
}
