/*
 * 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.Runtime.Serialization;
using System.Collections.Generic;
using JetBrains.Annotations;
using Nehta.VendorLibrary.CDA.Common;
using Nehta.VendorLibrary.CDA.Common.Enums;
using Nehta.VendorLibrary.Common;

namespace Nehta.VendorLibrary.CDA.SCSModel.Common
{
    [Serializable]
    [DataContract]
    [KnownType(typeof(Participation))]
    [KnownType(typeof(CodableText))]
    internal class Context : IPrescriptionRecordContext, IPrescriptionRequestContext, IDispenseRecordContext, ISharedHealthSummaryContext, 
        IEReferralContext, ISpecialistLetterContext, IEventSummaryContext, IAcdCustodianRecordContext, IConsumerEnteredHealthSummaryContext,
        IConsumerEnteredNotesContext, IConsolidatedViewContext, IMedicareInformationViewContext
    {
        #region Properties
        [CanBeNull]
        [DataMember]
        IParticipationConsumerAuthor IConsumerEnteredHealthSummaryContext.Author { get; set; }

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

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

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

        [CanBeNull]
        [DataMember]
        AuthorAuthoringDevice IMedicareInformationViewContext.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 IPrescriptionRecordContext.SubjectOfCare { get; set; }

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

        [CanBeNull]
        [DataMember]
        IParticipationSubjectOfCare IDispenseRecordContext.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 IMedicareInformationViewContext.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]
        public IParticipationUsualGP UsualGP { get; set; }

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

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

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

        [CanBeNull]
        [DataMember]
        public ISO8601DateTime DateTimePrescriptionRequestWritten { 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);

                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);

                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 IPrescriptionRecordContext.Validate(string path, List<ValidationMessage> messages)
        {
            var vb = new ValidationBuilder(path, messages);

            var subjectOfCare = ((IPrescriptionRecordContext)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.ValidateOptionalAddress(vb.Path + "SubjectOfCare", vb.Messages);
            }
        }

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

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

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

            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("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);
            }

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

        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.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 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.ValidateOptionalAddress(vb.Path + "SubjectOfCare", 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);
          }

          if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
          {
            if (castedParticipation.SubjectOfCare != null)
            {
              castedParticipation.SubjectOfCare.ValidateOptionalAddress(vb.Path + "SubjectOfCare", 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 IMedicareInformationViewContext.Validate(string path, List<ValidationMessage> messages)
        {
          var vb = new ValidationBuilder(path, messages);
          var castedParticipation = ((IMedicareInformationViewContext)this);

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

          if (vb.ArgumentRequiredCheck("SubjectOfCare", castedParticipation.SubjectOfCare))
          {
            if (castedParticipation.SubjectOfCare != null)
            {
              castedParticipation.SubjectOfCare.ValidateOptionalAddress(vb.Path + "SubjectOfCare", 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);
              }
            }
          }

        }

      #endregion
    }
}