/*
 * Copyright 2013 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.Linq;
using System.Runtime.Serialization;
using System.Collections.Generic;
using CDA.Generator.Common.SCSModel.ATS.ETP.Interfaces;
using CDA.Generator.Common.SCSModel.Interfaces;
using JetBrains.Annotations;
using Nehta.VendorLibrary.CDA.Common;
using Nehta.VendorLibrary.CDA.Common.Enums;
using Nehta.VendorLibrary.CDA.Generator.Enums;
using Nehta.VendorLibrary.Common;

namespace Nehta.VendorLibrary.CDA.SCSModel.Common
{
    [Serializable]
    [DataContract]
    [KnownType(typeof(Participation))]
    [KnownType(typeof(CodableText))]
  internal class Context : IPCEHRPrescriptionRecordContext, IPrescriptionRequestContext, IPCEHRDispenseRecordContext, ISharedHealthSummaryContext, 
        IEReferralContext, ISpecialistLetterContext, IEventSummaryContext, IAcdCustodianRecordContext, IConsumerEnteredHealthSummaryContext,
        IConsumerEnteredNotesContext, IConsolidatedViewContext, IMedicareOverviewContext, IPrescriptionAndDispenseViewContext,
        IConsumerEnteredAchievementsContext, IEPrescriptionContext, IPhysicalMeasurementsContext, IDispenseRecordContext, INSWHealthCheckAssessmentContext,
        IPersonalHealthObservationContext, IConsumerQuestionnaireContext, IBirthDetailsRecordContext
    {
        #region Properties
        [CanBeNull]
        [DataMember]
        IParticipationConsumerAuthor IConsumerEnteredHealthSummaryContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        IAuthorCollection IPhysicalMeasurementsContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        IAuthorCollection IBirthDetailsRecordContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationConsumerAuthor IConsumerEnteredNotesContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationDocumentAuthor Author { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationConsumerAuthor IConsumerEnteredAchievementsContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        AuthorAuthoringDevice IConsolidatedViewContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        AuthorAuthoringDevice IPrescriptionAndDispenseViewContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        AuthorAuthoringDevice IMedicareOverviewContext.Author { get; set; }

        [CanBeNull]
        [DataMember]
        public ISO8601DateTime Attested { get; set; }

        [CanBeNull]
        [DataMember]
        public CdaInterval EncounterPeriod { get; set; }

        [CanBeNull]
        [DataMember]
        public NullFlavour? EncounterPeriodNullFlavor { get; set; } 

        [DataMember]
        public IParticipationPrescriber Prescriber { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationPrescriberOrganisation PrescriberOrganisation { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationDispenser Dispenser { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationDispenserOrganisation DispenserOrganisation { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPCEHRPrescriptionRecordContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IDispenseRecordContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPrescriptionRequestContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPrescriptionAndDispenseViewContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPCEHRDispenseRecordContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare ISharedHealthSummaryContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IEReferralContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IAcdCustodianRecordContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IConsumerEnteredNotesContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IConsumerEnteredHealthSummaryContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IConsolidatedViewContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IMedicareOverviewContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IConsumerEnteredAchievementsContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IEPrescriptionContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IList<IParticipationPatientNominatedContact> IEReferralContext.PatientNominatedContacts { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare ISpecialistLetterContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IEventSummaryContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPhysicalMeasurementsContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare INSWHealthCheckAssessmentContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IPersonalHealthObservationContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IConsumerQuestionnaireContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IBirthDetailsRecordContext.SubjectOfCare { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationUsualGP UsualGP { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationReferrer Referrer { get; set; }

        [CanBeNull]
        [DataMember]
        public IParticipationHealthcareFacility HealthcareFacility { get; set; }

        [CanBeNull]
        [DataMember]
        public ISO8601DateTime DateTimeSubjectSeen { get; set; }

        [CanBeNull]
        [DataMember]
        public String PrescriptionRequestIdentifier { get; set; }

        [CanBeNull]
        [DataMember]
        public ISO8601DateTime DateTimePrescriptionRequestWritten { get; set; }

        /// <summary>
        /// Earliest Date for Filtering (DateTime Health Event Started)
        /// </summary>
        [CanBeNull]
        public ISO8601DateTime EarliestDateForFiltering { get; set; }

        /// <summary>
        /// Latest Date for Filtering (DateTime Health Event Ended)
        /// </summary>
        [CanBeNull]
        public ISO8601DateTime LatestDateForFiltering { get; set; }

        /// <summary>
        /// DateTime Health Event Started
        /// </summary>
        [CanBeNull]
        public ISO8601DateTime DateTimeHealthEventStarted { get; set; }

        /// <summary>
        /// DateTime Health Event Ended
        /// </summary>
        [CanBeNull]
        public ISO8601DateTime DateTimeHealthEventEnded { get; set; }

       #endregion

        #region Constructors
        internal Context()
        {
        }
        #endregion

        #region Validation

        void IEventSummaryContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((IEventSummaryContext)this).SubjectOfCare;

            // Encounter Period can only contain a Encounter Period Null Flavor or a Encounter Period
            var encounterPeriod = new Dictionary<string, object>()
            {
                { "EncounterPeriod", EncounterPeriod },
                { "EncounterPeriodNullFlavor", EncounterPeriodNullFlavor }
            };
            vb.ChoiceCheck(encounterPeriod);

            // Validate author at this level, and not at Participation / Participant level because it is different from the other
            // documents.
            if (vb.ArgumentRequiredCheck("Author", Author))
            {
                Author.Validate(vb.Path + "Author", vb.Messages);

                if (Author.Participant != null)
                {
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.ElectronicCommunicationDetails", Author.Participant.ElectronicCommunicationDetails);
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.Addresses", Author.Participant.Addresses);

                    if (Author.Participant.Person != null)
                        vb.ArgumentRequiredCheck("Author.Participant.Person.Organisation", Author.Participant.Person.Organisation);
                }
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                    subjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);

                    if (subjectOfCare.Participant != null && subjectOfCare.Participant.Person != null)
                    {
                       vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", subjectOfCare.Participant.Person.IndigenousStatus);

                        if (subjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                        {
                            vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.MothersOriginalFamilyName can not be included for this CDA document type");
                        }

                        if (subjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                        {
                            vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.SourceOfDeathNotification can not be included for this CDA document type");
                        }
                    }
                }
            }
        }

        void ISharedHealthSummaryContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((ISharedHealthSummaryContext)this).SubjectOfCare;

            // Validate author at this level, and not at Participation / Participant level because it is different from the other
            // documents.
            if (vb.ArgumentRequiredCheck("Author", Author))
            {
                Author.Validate(vb.Path + "Author", vb.Messages);

                if (Author.Participant != null)
                {
                    vb.ArgumentRequiredCheck("Author.Participant.ElectronicCommunicationDetails", Author.Participant.ElectronicCommunicationDetails);
                    vb.ArgumentRequiredCheck("Author.Participant.Addresses", Author.Participant.Addresses);

                    if (Author.Participant.Person != null)
                        vb.ArgumentRequiredCheck("Author.Participant.Person.Organisation", Author.Participant.Person.Organisation);
                }
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                        subjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);

                        if (subjectOfCare.Participant != null && subjectOfCare.Participant.Person != null)
                        {
                            vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", subjectOfCare.Participant.Person.IndigenousStatus);

                            if (subjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                            {
                                vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.MothersOriginalFamilyName can not be included for this CDA document type");
                            }

                            if (subjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                            {
                                vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.SourceOfDeathNotification can not be included for this CDA document type");
                            }
                        }
                }
            }
        }

        void IAcdCustodianRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var castedParticipation = ((IAcdCustodianRecordContext)this);

            if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
            {
               castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages);
            }

            var subjectOfCare = ((IAcdCustodianRecordContext)this).SubjectOfCare;

            // Validate author at this level, and not at Participation / Participant level because it is different from the other
            // documents.
            if (vb.ArgumentRequiredCheck("Author", Author))
            {
                Author.Validate(vb.Path + "Author", vb.Messages);

                if (Author.Participant != null)
                {
                    if (Author.Participant.Person != null)
                    {
                        vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.Identifiers", Author.Participant.Person.Identifiers);
                    }
                }
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                    subjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);

                    // IndigenousStatus
                    if (subjectOfCare.Participant != null)
                        if (subjectOfCare.Participant.Person != null)
                            vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", subjectOfCare.Participant.Person.IndigenousStatus);
                }


                if (subjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                {
                    vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.MothersOriginalFamilyName can not be included for this CDA document type");
                }

                if (subjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                {
                    vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.SourceOfDeathNotification can not be included for this CDA document type");
                }

            }
        }

        void IEReferralContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((IEReferralContext)this).SubjectOfCare;

            // Validate author at this level, and not at Participation / Participant level because it is different from the other
            // documents.
            if (vb.ArgumentRequiredCheck("Author", Author))
            {
                Author.Validate(vb.Path + "Author", vb.Messages);

                vb.ArgumentRequiredCheck("Author.AuthorParticipationPeriodOrDateTimeAuthored", Author.AuthorParticipationPeriodOrDateTimeAuthored);

                if (Author.Participant != null)
                {
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.ElectronicCommunicationDetails", Author.Participant.ElectronicCommunicationDetails);
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.Addresses", Author.Participant.Addresses);

                    if (Author.Participant.Person != null)
                        vb.ArgumentRequiredCheck("Author.Participant.Person.Organisation", Author.Participant.Person.Organisation);
                }
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                    subjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);

                    // Check electronic communication details here because it's only needed
                    // in eReferral
                    if (subjectOfCare.Participant != null)
                        vb.ArgumentRequiredCheck("SubjectOfCare.Participant.ElectronicCommunicationDetails", subjectOfCare.Participant.ElectronicCommunicationDetails);

                    if (subjectOfCare.Participant != null && subjectOfCare.Participant.Person != null)
                    {
                        vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", subjectOfCare.Participant.Person.IndigenousStatus);

                        if (subjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                        {
                            vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.MothersOriginalFamilyName can not be included for this CDA document type");
                        }

                        if (subjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                        {
                            vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.SourceOfDeathNotification can not be included for this CDA document type");
                        }
                    }
                }
            }

            // Patient nominated contacts
            if (((IEReferralContext) this).PatientNominatedContacts != null)
            {
                foreach (IParticipationPatientNominatedContact contact in ((IEReferralContext)this).PatientNominatedContacts)
                {
                    contact.Validate(vb.Path + "PatientNominatedContact", vb.Messages);
                }                
            }
        }

        void ISpecialistLetterContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((ISpecialistLetterContext)this).SubjectOfCare;

            vb.ArgumentRequiredCheck("DateTimeSubjectSeen", DateTimeSubjectSeen);

            // Validate author at this level, and not at Participation / Participant level because it is different from the other
            // documents.
            if (vb.ArgumentRequiredCheck("Author", Author))
            {
                Author.Validate(vb.Path + "Author", vb.Messages);

                vb.ArgumentRequiredCheck("Author.AuthorParticipationPeriodOrDateTimeAuthored", Author.AuthorParticipationPeriodOrDateTimeAuthored);

                if (Author.Participant != null)
                {
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.ElectronicCommunicationDetails", Author.Participant.ElectronicCommunicationDetails);
                    vb.ArgumentRequiredCheck(vb.Path + "Author.Participant.Addresses", Author.Participant.Addresses);

                    if (Author.Participant.Person != null)
                        vb.ArgumentRequiredCheck("Author.Participant.Person.Organisation", Author.Participant.Person.Organisation);
                }
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null && subjectOfCare.Participant != null && subjectOfCare.Participant.Person != null)
                {
                    if (subjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                    {
                        vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.MothersOriginalFamilyName can not be included for this CDA document type");
                    }

                    if (subjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                    {
                        vb.AddValidationMessage(vb.PathName, null, "SubjectOfCare.SourceOfDeathNotification can not be included for this CDA document type");
                    }
                }
            }
            
            if (vb.ArgumentRequiredCheck("Referrer", Referrer))
            {
                if (Referrer != null) Referrer.Validate(vb.Path + "Referrer", vb.Messages);
            }

            if (UsualGP != null)
            {
                if (UsualGP != null) UsualGP.Validate(vb.Path + "UsualGP", vb.Messages);
            }

            if (Referrer != null)
            {
                if (Referrer != null) Referrer.Validate(vb.Path + "Referrer", vb.Messages);
            }
        }

        void IPCEHRDispenseRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((IPCEHRDispenseRecordContext)this).SubjectOfCare;

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null) subjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);
            }

            if (vb.ArgumentRequiredCheck("Dispenser", Dispenser))
            {
                if (Dispenser != null) Dispenser.Validate(vb.Path + "Dispenser", vb.Messages);
            }

            if (vb.ArgumentRequiredCheck("DispenserOrganisation", DispenserOrganisation))
            {
                if (DispenserOrganisation != null) DispenserOrganisation.Validate(vb.Path + "DispenserOrganisation", vb.Messages);
            }
        }

        void IPCEHRPrescriptionRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var subjectOfCare = ((IPCEHRPrescriptionRecordContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("Prescriber", Prescriber))
          {
            if (Prescriber != null) Prescriber.Validate(vb.Path + "Prescriber", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("PrescriberOrganisation", PrescriberOrganisation))
          {
            if (PrescriberOrganisation != null) PrescriberOrganisation.Validate(vb.Path + "PrescriberOrganisation", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null) subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);
          }
        }


        void IConsumerEnteredNotesContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var castedParticipation = ((IConsumerEnteredNotesContext)this);

            if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
            {
              castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages);
            }

            var subjectOfCare = ((IConsumerEnteredNotesContext)this).SubjectOfCare;

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                  subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, true, true, vb.Messages);
                }
            }
        }


        void IConsumerEnteredHealthSummaryContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var castedParticipation = ((IConsumerEnteredHealthSummaryContext)this);

            if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
            {
               castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages);
            }

            if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
            {
              if (castedParticipation.SubjectOfCare != null)
                {
                  castedParticipation.SubjectOfCare.Validate(vb.Path + "SubjectOfCare", vb.Messages);

                    // IndigenousStatus
                  if (castedParticipation.SubjectOfCare.Participant != null)
                    if (castedParticipation.SubjectOfCare.Participant.Person != null)
                      vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", castedParticipation.SubjectOfCare.Participant.Person.IndigenousStatus);
                }
            }
        }

        void IConsolidatedViewContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var castedParticipation = ((IConsolidatedViewContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
              castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages, false, true);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
          {
            if (castedParticipation.SubjectOfCare != null)
            {
              castedParticipation.SubjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);

              if (castedParticipation.SubjectOfCare.Participant != null && castedParticipation.SubjectOfCare.Participant.Person != null)
              {
                 vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", castedParticipation.SubjectOfCare.Participant.Person.IndigenousStatus);
                 vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.Age", castedParticipation.SubjectOfCare.Participant.Person.Age);
              }
            }
          }
        }

        void IPrescriptionAndDispenseViewContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);
          var castedParticipation = ((IPrescriptionAndDispenseViewContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
            vb.ArgumentRequiredCheck("Author.Identifiers", castedParticipation.Author.Identifiers);
            castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages, true, true);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
          {
            if (castedParticipation.SubjectOfCare != null)
            {
              castedParticipation.SubjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);

              if (castedParticipation.SubjectOfCare.Participant != null && castedParticipation.SubjectOfCare.Participant.Addresses != null)
              {
                var addresses = castedParticipation.SubjectOfCare.Participant.Addresses;

                for (var x = 0; x < addresses.Count; x++)
                {
                  if (addresses[x].InternationalAddress != null || addresses[x].AustralianAddress == null)
                    vb.AddValidationMessage(vb.Path + string.Format("Addresses[{0}]", x), null, "Australian address required.");
                }
              }

              if (castedParticipation.SubjectOfCare.Participant.Person.DateOfDeath != null)
                vb.AddValidationMessage(vb.Path + "DateOfDeath", null, "DateOfDeath is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant.Person.DateOfDeathAccuracyIndicator != null)
                vb.AddValidationMessage(vb.Path + "DateOfDeathAccuracyIndicator", null, "DateOfDeathAccuracyIndicator is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant.Person.SourceOfDeathNotification != null)
                vb.AddValidationMessage(vb.Path + "SourceOfDeathNotification", null, "SourceOfDeathNotification is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant.Person.MothersOriginalFamilyName != null)
                vb.AddValidationMessage(vb.Path + "MothersOriginalFamilyName", null, "MothersOriginalFamilyName is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant.Person.CountryOfBirth != Country.Undefined)
                vb.AddValidationMessage(vb.Path + "CountryOfBirth", null, "CountryOfBirth is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant.Person.StateOfBirth != AustralianState.Undefined)
                vb.AddValidationMessage(vb.Path + "StateOfBirth", null, "StateOfBirth is not a valid field for PCEHR Prescription and Dispense View");

              if (castedParticipation.SubjectOfCare.Participant != null && castedParticipation.SubjectOfCare.Participant.Person != null)
              {
                vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.Age", castedParticipation.SubjectOfCare.Participant.Person.Age);
              }
            }
          }

        }

        void IMedicareOverviewContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);
          var castedParticipation = ((IMedicareOverviewContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
              vb.ArgumentRequiredCheck("Author.Identifiers", castedParticipation.Author.Identifiers);
              castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages, true, true);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
          {
            if (castedParticipation.SubjectOfCare != null)
            {
              castedParticipation.SubjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);

              if (castedParticipation.SubjectOfCare.Participant != null && castedParticipation.SubjectOfCare.Participant.Addresses != null)
              {
                var addresses = castedParticipation.SubjectOfCare.Participant.Addresses;

                for (var x = 0; x < addresses.Count; x++)
                {
                  if (addresses[x].InternationalAddress != null || addresses[x].AustralianAddress == null)
                    vb.AddValidationMessage(vb.Path + string.Format("Addresses[{0}]", x), null, "Australian address required.");
                }
              }

              if (castedParticipation.SubjectOfCare.Participant != null && castedParticipation.SubjectOfCare.Participant.Person != null)
              {
                vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.IndigenousStatus", castedParticipation.SubjectOfCare.Participant.Person.IndigenousStatus);
                vb.ArgumentRequiredCheck("SubjectOfCare.Participant.Person.Age", castedParticipation.SubjectOfCare.Participant.Person.Age);
              }
            }
          }
        }

        void IConsumerEnteredAchievementsContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var castedParticipation = ((IConsumerEnteredAchievementsContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
            castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages);

            if (castedParticipation.Author.Participant != null && castedParticipation.Author.Participant.RelationshipToSubjectOfCare != null)
            {
              vb.AddValidationMessage(vb.Path + "SubjectOfCare.Participant.Person.RelationshipToSubjectOfCare", null, "RelationshipToSubjectOfCare is not valid for ConsumerEnteredAchievements");
            }
          }

          var subjectOfCare = castedParticipation.SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);
          }
        }


        void IEPrescriptionContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var subjectOfCare = ((IEPrescriptionContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("Prescriber", Prescriber))
          {
            if (Prescriber != null) Prescriber.ValidateATS(vb.Path + "Prescriber", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null) subjectOfCare.ValidateATS(vb.Path + "SubjectOfCare", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("PrescriberOrganisation", PrescriberOrganisation))
          {
            if (PrescriberOrganisation != null) PrescriberOrganisation.ValidateATS(vb.Path + "PrescriberOrganisation", vb.Messages);
          }
        }

        void IDispenseRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var subjectOfCare = ((IDispenseRecordContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null) subjectOfCare.ValidateATS(vb.Path + "SubjectOfCare", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("Dispenser", Dispenser))
          {
            if (Dispenser != null) Dispenser.ValidateATS(vb.Path + "Dispenser", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("DispenserOrganisation", DispenserOrganisation))
          {
            if (DispenserOrganisation != null) DispenserOrganisation.ValidateATS(vb.Path + "DispenserOrganisation", vb.Messages);
          }
        }

        void IPrescriptionRequestContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var prescriptionRequest = ((IPrescriptionRequestContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", prescriptionRequest))
          {
            if (prescriptionRequest != null) prescriptionRequest.ValidateATS(vb.Path + "SubjectOfCare", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("Prescriber", Prescriber))
          {
            if (Prescriber != null) Prescriber.ValidateATS(vb.Path + "Prescriber", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("PrescriberOrganisation", PrescriberOrganisation))
          {
            if (PrescriberOrganisation != null) PrescriberOrganisation.ValidateATS(vb.Path + "PrescriberOrganisation", vb.Messages);
          }

          if (vb.ArgumentRequiredCheck("Dispenser", Dispenser))
          {
            if (Dispenser != null)
            {
              Dispenser.Validate(vb.Path + "Dispenser", vb.Messages);

              if (prescriptionRequest != null && prescriptionRequest.Participant != null)
                  vb.RangeCheck("Addresses", prescriptionRequest.Participant.Addresses, 0, 1);
            }
          }

          if (vb.ArgumentRequiredCheck("DispenserOrganisation", DispenserOrganisation))
          {
            if (DispenserOrganisation != null) DispenserOrganisation.ValidateATS(vb.Path + "DispenserOrganisation", vb.Messages);
          }

          vb.ArgumentRequiredCheck("DateTimePrescriptionRequestWritten", DateTimePrescriptionRequestWritten);
          vb.ArgumentRequiredCheck("PrescriptionRequestIdentifier", PrescriptionRequestIdentifier);
        }

        void IPhysicalMeasurementsContext.Validate(string path, PhysicalMeasurementsDocumentType physicalMeasurementsDocumentType, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var castedParticipation = ((IPhysicalMeasurementsContext)this);

            if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
            {
              if (castedParticipation.Author is AuthorAuthoringDevice)
              {
                  ((AuthorAuthoringDevice)castedParticipation.Author).Validate(vb.Path + "IParticipationAuthorHealthcareProvider", vb.Messages, false, true);

                  if (physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.ConsumerEnteredMeasurements || physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.HealthcareProviderEnteredMeasurements)
                  {
                    vb.AddValidationMessage(path + "Author", null, "AuthorAuthoringDevice can only be used for the PhysicalMeasurementsView document type"); 
                  }
              }

              // Both types are of type Participation so use the Participant to determin the type 
              if (castedParticipation.Author is Participation)
              {
                var authorNonHealthcareProvider = castedParticipation.Author as IParticipationAuthorNonHealthcareProvider;

                if (authorNonHealthcareProvider.Participant != null)
                {
                  authorNonHealthcareProvider.Validate(vb.Path + "IParticipationAuthorNonHealthcareProvider", vb.Messages);

                  // Mandatory Identifier
                  if (authorNonHealthcareProvider.Participant != null && authorNonHealthcareProvider.Participant.Person != null)
                  {
                      vb.ArgumentRequiredCheck("Person.Identifiers", authorNonHealthcareProvider.Participant.Person.Identifiers);
                  }

                  if (physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.PhysicalMeasurementsView || physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.HealthcareProviderEnteredMeasurements)
                  {
                    vb.AddValidationMessage(path + "Author", null, "IParticipationAuthorNonHealthcareProvider can only be used for the ConsumerEnteredMeasurements document type");
                  }
                }

                var authorHealthcareProvider = castedParticipation.Author as IParticipationAuthorHealthcareProvider;

                if (authorHealthcareProvider.Participant != null)
                {
                  authorHealthcareProvider.Validate(vb.Path + "IParticipationAuthorHealthcareProvider", vb.Messages);

                  if (physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.ConsumerEnteredMeasurements || physicalMeasurementsDocumentType == PhysicalMeasurementsDocumentType.PhysicalMeasurementsView)
                  {
                    vb.AddValidationMessage(path + "Author", null, "IParticipationAuthorHealthcareProvider can only be used for the HealthcareProviderEnteredMeasurements document type");
                  }
                }
              }
            }

            var subjectOfCare = ((IPhysicalMeasurementsContext)this).SubjectOfCare;

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
              if (subjectOfCare != null)
              {
                subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, true, vb.Messages);
              }
            }

            if (HealthcareFacility != null)
            {
                HealthcareFacility.Validate(vb.Path + "HealthcareFacility", false, false, vb.Messages);    
            }
        }

        void IBirthDetailsRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var castedParticipation = ((IBirthDetailsRecordContext)this);

            if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
            {
                if (castedParticipation.Author is AuthorAuthoringDevice)
                {
                    ((AuthorAuthoringDevice)castedParticipation.Author).Validate(vb.Path + "IParticipationAuthorHealthcareProvider", vb.Messages, false, true);
                }

                // Both types are of type Participation so use the Participant to determine the type 
                if (castedParticipation.Author is Participation)
                {
                    var authorNonHealthcareProvider = castedParticipation.Author as IParticipationAuthorNonHealthcareProvider;

                    if (authorNonHealthcareProvider.Participant != null)
                    {
                        authorNonHealthcareProvider.Validate(vb.Path + "IParticipationAuthorNonHealthcareProvider", vb.Messages);
                    }

                    var authorHealthcareProvider = castedParticipation.Author as IParticipationAuthorHealthcareProvider;

                    if (authorHealthcareProvider.Participant != null)
                    {
                        authorHealthcareProvider.Validate(vb.Path + "IParticipationAuthorHealthcareProvider", vb.Messages);
                    }
                }
            }

            var subjectOfCare = castedParticipation.SubjectOfCare;

            if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
            {
                if (subjectOfCare != null)
                {
                    subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, false, false, vb.Messages);
                }
            }

            var healthcareFacility = castedParticipation.HealthcareFacility;

            if (healthcareFacility != null)
            {
               healthcareFacility.Validate(vb.Path + "HealthcareFacility", true, true, vb.Messages);
            }
        }

      #region CeHR

        void INSWHealthCheckAssessmentContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var castedParticipation = ((INSWHealthCheckAssessmentContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
            castedParticipation.Author.ValidateOptional(vb.Path + "Author", true, vb.Messages);
          }

          var subjectOfCare = ((INSWHealthCheckAssessmentContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null)
            {
              subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, true, true, vb.Messages);
            }
          }
        }

        void IPersonalHealthObservationContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var castedParticipation = ((IPersonalHealthObservationContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
            castedParticipation.Author.Validate(vb.Path + "Author", vb.Messages);
          }

          var subjectOfCare = ((IPersonalHealthObservationContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null)
            {
              subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, true, true, vb.Messages);
            }
          }
        }

        void IConsumerQuestionnaireContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);

          var castedParticipation = ((IConsumerQuestionnaireContext)this);

          if (vb.ArgumentRequiredCheck("Author", castedParticipation.Author))
          {
            castedParticipation.Author.ValidateOptional(vb.Path + "Author", true, vb.Messages);
          }

          var subjectOfCare = ((IConsumerQuestionnaireContext)this).SubjectOfCare;

          if (vb.ArgumentRequiredCheck("SubjectOfCare", subjectOfCare))
          {
            if (subjectOfCare != null)
            {
              subjectOfCare.ValidateOptional(vb.Path + "SubjectOfCare", true, true, true, vb.Messages);
            }
          }
        }

  
      #endregion  

      #endregion
    }
}