context: // Copyright (c) The University of Dundee 2018-2019
  // This file is part of the Research Data Management Platform (RDMP).
  // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  // You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
  
  using System;
  using System.Collections.Generic;
  using System.Data.Common;
  using System.Linq;
  using MapsDirectlyToDatabaseTable;
  using MapsDirectlyToDatabaseTable.Attributes;
  using Rdmp.Core.Curation.Data.Aggregation;
  using Rdmp.Core.Curation.Data.Cohort.Joinables;
  using Rdmp.Core.Curation.Data.Defaults;
  using Rdmp.Core.Curation.FilterImporting;
  using Rdmp.Core.Curation.FilterImporting.Construction;
  using Rdmp.Core.Databases;
  using Rdmp.Core.QueryBuilding;
  using Rdmp.Core.Repositories;
  using Rdmp.Core.Ticketing;
  using ReusableLibraryCode;
  using ReusableLibraryCode.Annotations;
  using ReusableLibraryCode.Checks;
  
  namespace Rdmp.Core.Curation.Data.Cohort
  {
      /// <summary>
      /// Cohort identification is the job identifying which patients fit certain study criteria.  E.g. "I want all patients who have been prescribed Diazepam for the first time after 2000
      /// and who are still alive today".  Every time the data analyst has a new project/cohort to identify he should create a new CohortIdentificationConfiguration, it is the entry point
      /// for cohort generation and includes a high level description of what the cohort requirements are, an optional ticket and is the hanging off point for all the 
      /// RootCohortAggregateContainers (the bit that provides the actual filtering/technical data about how the cohort is identified).
      /// </summary>
      public class CohortIdentificationConfiguration : DatabaseEntity, ICollectSqlParameters,INamed, IHasDependencies,ICustomSearchString, IMightBeReadOnly
      {
          /// <summary>
          /// Characters that apear in front of any <see cref="AggregateConfiguration"/> which is acting as a cohort identification list or patient index table
          /// <seealso cref="AggregateConfiguration.IsCohortIdentificationAggregate"/>.
          /// </summary>
          public const string CICPrefix = "cic_";
  
          #region Database Properties
          private string _name;
          private string _description;
          private string _ticket;
          private int? _rootCohortAggregateContainerID;
          private int? _queryCachingServerID;
          private bool _frozen;
          private string _frozenBy;
          private DateTime? _frozenDate;
          private int? _clonedFrom_ID;
  
          /// <inheritdoc/>
          [Unique]
          [NotNull]
          public string Name
          {
              get { return _name; }
              set { SetField(ref  _name, value); }
          }
  
          /// <summary>
          /// User typed description of the cohort identification criteria in high level terms.  This should be a primer for looking at the contents (cohort sets, set operations
          /// etc).
          /// </summary>
          public string Description
          {
              get { return _description; }
              set { SetField(ref  _description, value); }
          }
  
          /// <summary>
          /// Name of a ticket in your company issue tracking application (if you have one) for logging time/issues with this <see cref="CohortIdentificationConfiguration"/>.
          /// Ties in with <see cref="ITicketingSystem"/> if a compatible plugin is installed.
          /// </summary>
          public string Ticket
          {
              get { return _ticket; }
              set { SetField(ref  _ticket, value); }
          }
  
          /// <summary>
          /// All <see cref="CohortIdentificationConfiguration"/> must have a single unique root <see cref="CohortAggregateContainer"/> in order to be run.  This is the ID of that
          /// container.
          /// <para>You should not share containers/cohort sets with any other <see cref="CohortIdentificationConfiguration"/></para>
          /// </summary>
          public int? RootCohortAggregateContainer_ID
          {
              get { return _rootCohortAggregateContainerID; }
              set { SetField(ref  _rootCohortAggregateContainerID, value); }
          }
  
          /// <summary>
          /// To assist with complex cohort identification queries over multiple datasets (and between servers / server types) you can configure a QueryCachingServer.  
          /// This is an <see cref="ExternalDatabaseServer"/> created by <see cref="QueryCachingPatcher"/>.
          /// 
          /// <para>Once setup, each <see cref="AggregateConfiguration"/> query in this <see cref="CohortIdentificationConfiguration"/> will be run independently and
          /// the resulting patient list committed ot the cache server (See QueryCaching.Aggregation.CachedAggregateConfigurationResultsManager).</para>
          /// 
          /// <para>This field holds the ID of the currently configured database (if any) which acts as a result cache</para>
          /// </summary>
          public int? QueryCachingServer_ID
          {
              get { return _queryCachingServerID; }
              set { SetField(ref  _queryCachingServerID, value); }
          }
  
          /// <summary>
          /// Indicates whether the <see cref="CohortIdentificationConfiguration"/> should be considered immutable.  Usually because it has been run and the results committed to
          /// a Project.
          /// <para>IMPORTANT:You should use <see cref="Freeze()"/> rather than just setting this manually so as to also populate <see cref="FrozenBy"/> and <see cref="FrozenDate"/></para>
          /// </summary>
          public bool Frozen
          {
              get { return _frozen; }
              set { SetField(ref  _frozen, value); }
          }
  
          /// <summary>
          /// Username of the user who ran <see cref="Freeze()"/>
          /// <seealso cref="Frozen"/>
          /// </summary>
          public string FrozenBy
          {
              get { return _frozenBy; }
              set { SetField(ref  _frozenBy, value); }
          }
  
          /// <summary>
          /// The date <see cref="Freeze()"/> was last ran
          /// <seealso cref="Frozen"/>
          /// </summary>
          public DateTime? FrozenDate
          {
              get { return _frozenDate; }
              set { SetField(ref  _frozenDate, value); }
          }
  
          /// <summary>
          /// The ID of the CohortIdentificationConfiguration that this one was cloned from (if any).
          /// 
          /// <para>It is possible to delete the parent, resulting in a ClonedFrom_ID which cannot be resolved to a parent object</para>
          /// <seealso cref="Frozen"/>
          /// </summary>
          public int? ClonedFrom_ID
          {
              get { return _clonedFrom_ID; }
              set { SetField(ref  _clonedFrom_ID, value); }
          }
  
          #endregion
  
          #region Relationships
          /// <inheritdoc cref="RootCohortAggregateContainer_ID"/>
          [NoMappingToDatabase]
          public CohortAggregateContainer RootCohortAggregateContainer {
              get { return 
                  RootCohortAggregateContainer_ID == null?  null:
                  Repository.GetObjectByID<CohortAggregateContainer>((int) RootCohortAggregateContainer_ID); }
          }
  
          /// <inheritdoc cref="QueryCachingServer_ID"/>
          [NoMappingToDatabase]
          public ExternalDatabaseServer QueryCachingServer {
              get
              {
                  return QueryCachingServer_ID == null
                      ? null
                      : Repository.GetObjectByID<ExternalDatabaseServer>(QueryCachingServer_ID.Value);
              }
          }
  
          
  
          #endregion
          
  
          /// <summary>
          /// Declares a new configuration for identifying patient lists matching a study requirements based on the results of cohort sets / patient index tables and set operations
          /// <see cref="CohortIdentificationConfiguration"/>
          /// </summary>
          /// <param name="repository"></param>
          /// <param name="name"></param>
          public CohortIdentificationConfiguration(ICatalogueRepository repository, string name)
          {
              var queryCache = repository.GetServerDefaults().GetDefaultFor(PermissableDefaults.CohortIdentificationQueryCachingServer_ID);
  
              repository.InsertAndHydrate(this,new Dictionary<string, object>
              {
                  {"Name", name},
                  {"QueryCachingServer_ID",queryCache == null ? (object) DBNull.Value:queryCache.ID}
              });
          }
  
          internal CohortIdentificationConfiguration(ICatalogueRepository repository, DbDataReader r)
              : base(repository, r)
          {
              Name = r["Name"].ToString();
              Description = r["Description"] as string;
              RootCohortAggregateContainer_ID = ObjectToNullableInt(r["RootCohortAggregateContainer_ID"]);
              Ticket = r["Ticket"] as string;
              QueryCachingServer_ID = ObjectToNullableInt(r["QueryCachingServer_ID"]);
  
              Frozen = Convert.ToBoolean(r["Frozen"]);
              FrozenBy = r["FrozenBy"] as string;
              FrozenDate = ObjectToNullableDateTime(r["FrozenDate"]);
              ClonedFrom_ID = ObjectToNullableInt(r["ClonedFrom_ID"]);
          }
          
          /// <summary>
          /// Deletes the root container and consequently the entire <see cref="CohortIdentificationConfiguration"/>
          /// </summary>
          public override void DeleteInDatabase()
          {
              //container is the parent class even though this is a 1 to 1 and there is a CASCADE which will actually nuke ourselves when we delete the root container!
              if (RootCohortAggregateContainer_ID != null)
                  RootCohortAggregateContainer.DeleteInDatabase();
  
              //shouldnt ever happen but double check anyway incase somebody removes the CASCADE
              if(Exists())
                  base.DeleteInDatabase();
              else
              {
                  //make sure to do the obscure cross server/database cascade activities too
  
                  //if the repository has obscure dependencies
                  if (CatalogueRepository.ObscureDependencyFinder != null)
                      CatalogueRepository.ObscureDependencyFinder.HandleCascadeDeletesForDeletedObject(this);
              }
          }
  
          /// <summary>
          /// Creates a new <see cref="CohortAggregateContainer"/> if there is no <see cref="RootCohortAggregateContainer_ID"/> yet
          /// </summary>
          public void CreateRootContainerIfNotExists()
          {
              //if it doesn't have one
  
gt:             if (RootCohortAggregateContainer_ID == null)