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 System.Reflection;
  using System.Text;
  using MapsDirectlyToDatabaseTable;
  using MapsDirectlyToDatabaseTable.Attributes;
  using MapsDirectlyToDatabaseTable.Injection;
  using MapsDirectlyToDatabaseTable.Revertable;
  using Rdmp.Core.Curation.Data.ImportExport;
  using Rdmp.Core.Curation.Data.Serialization;
  using Rdmp.Core.QueryBuilding;
  using Rdmp.Core.Repositories;
  using ReusableLibraryCode;
  using ReusableLibraryCode.Annotations;
  
  namespace Rdmp.Core.Curation.Data
  {
      /// <summary>
      /// A virtual column that is made available to researchers.  Each Catalogue has 1 or more CatalogueItems, these contain the descriptions of what is contained
      /// in the column as well as any outstanding/resolved issues.
      /// 
      /// <para>It is important to note that <see cref="CatalogueItem"/> are tied to underlying database via <see cref="ExtractionInformation"/>.  This means
      /// that you can for example have multiple different versions of the same underlying <see cref="ColumnInfo"/> </para>
      /// 
      /// <para>e.g.
      /// CatalogueItem: PatientDateOfBirth (ExtractionInformation verbatim but 'Special Approval Required')
      /// CatalogueItem: PatientDateOfBirthApprox (ExtractionInformation rounds to nearest quarter but governance is 'Core')</para>
      /// 
      /// <para>Both the above would extract from the same ColumnInfo DateOfBirth</para>
      /// </summary>
      public class CatalogueItem : DatabaseEntity, IDeleteable, IComparable, IHasDependencies, IRevertable, INamed, IInjectKnown<ExtractionInformation>,IInjectKnown<ColumnInfo>, IInjectKnown<Catalogue>
      {
          #region Database Properties
          
          private string _Name;
          private string _Statistical_cons;
          private string _Research_relevance;
          private string _Description;
          private string _Topic;
          private string _Agg_method;
          private string _Limitations;
          private string _Comments;
          private int _catalogueID;
          private int? _columnInfoID;
          private Catalogue.CataloguePeriodicity _periodicity;
  
          private Lazy<ExtractionInformation> _knownExtractionInformation;
          private Lazy<ColumnInfo> _knownColumnInfo;
          private Lazy<Catalogue> _knownCatalogue;
  
          /// <summary>
          /// The ID of the parent <see cref="Catalogue"/> (dataset) to which this is a virtual column/column description
          /// </summary>
          [Relationship(typeof(Catalogue),RelationshipType.SharedObject)]
          [DoNotExtractProperty]
          public int Catalogue_ID
          {
              get { return _catalogueID; }
              set
              {
                  SetField(ref _catalogueID, value);
                  ClearAllInjections();
              }
          }
          /// <inheritdoc/>
          [NotNull]
          [DoNotImportDescriptions]
          public string Name {
              get { return _Name;}
              set {SetField(ref _Name,value);} 
          }
  
          /// <summary>
          /// User supplied field meant to identify any statistical anomalies with the data in the column described.  Not used for anything by RDMP.
          /// </summary>
          public string Statistical_cons {
              get { return _Statistical_cons; }
              set
              {
                  SetField(ref _Statistical_cons, value);
              } 
          }
  
          /// <summary>
          /// User supplied field meant for describing research applicability/field of the data in the column.  Not used for anything by RDMP.
          /// </summary>
          public string Research_relevance
          {
              get { return _Research_relevance; }
              set { SetField(ref _Research_relevance , value);}
          }
  
          /// <summary>
          /// User supplied description of what is in the column
          /// </summary>
          public string Description
          {
              get { return _Description; }
              set { SetField(ref _Description , value);}
          }
  
          /// <summary>
          /// User supplied heading or keywords of what is in the column relates to.  Not used for anything by RDMP.
          /// </summary>
          public string Topic
          {
              get { return _Topic; }
              set { SetField(ref _Topic , value);}
          }
  
          /// <summary>
          /// User specified free text field.  Not used for anything by RDMP.
          /// </summary>
          public string Agg_method
          {
              get { return _Agg_method; }
              set { SetField(ref _Agg_method , value);}
          }
  
          /// <summary>
          /// User specified free text field.  Not used for anything by RDMP.
          /// </summary>
          public string Limitations
          {
              get { return _Limitations; }
              set{ SetField(ref _Limitations , value);}
          }
  
          /// <summary>
          /// User specified free text field.  Not used for anything by RDMP.
          /// </summary>
          public string Comments
          {
              get { return _Comments; }
              set { SetField( ref _Comments , value);}
          }
  
          /// <summary>
          /// The ID of the underlying <see cref="ColumnInfo"/> to which this CatalogueItem describes.  This can be null if the underlying column has been deleted / removed.
          /// You can have multiple <see cref="CatalogueItem"/>s in a <see cref="Catalogue"/> that share the same underlying <see cref="ColumnInfo"/> if one of them is a transform
          /// e.g. you might release the first 3 digits of a postcode to anyone (<see cref="ExtractionCategory.Core"/>) but only release the full postcode with 
          /// <see cref="ExtractionCategory.SpecialApprovalRequired"/>.
          /// </summary>
          [Relationship(typeof(ColumnInfo), RelationshipType.IgnoreableLocalReference)]  //will appear as empty, then the user can guess from a table
          public int? ColumnInfo_ID
          {
              get { return _columnInfoID; }
              set
              {
                  //don't change it to the same value it already has
                  if(value == ColumnInfo_ID )
                      return;
  
                  SetField(ref _columnInfoID , value);
                  ClearAllInjections();
              }
          }
  
          /// <summary>
          /// How frequently this column is updated... why this would be different from <see cref="Data.Catalogue.Periodicity"/>?
          /// </summary>
          public Catalogue.CataloguePeriodicity Periodicity
          {
              get { return _periodicity; }
              set { SetField(ref _periodicity , value); }
          }
  
          #endregion
  
  
          #region Relationships
          /// <inheritdoc cref="Catalogue_ID"/>
          [NoMappingToDatabase]
          public Catalogue Catalogue {
              get{return _knownCatalogue.Value;}
          }
  
          /// <summary>
          /// Fetches the <see cref="ExtractionInformation"/> (if any) that specifies how to extract this column.  This can be the underlying column name (fully specified) or a transform.
          /// <para>This will be null if the <see cref="CatalogueItem"/> is not extractable</para>
          /// </summary>
          [NoMappingToDatabase]
          public ExtractionInformation ExtractionInformation
          {
              get
              {
                  return _knownExtractionInformation.Value;
              }
          }
  
          /// <inheritdoc cref="ColumnInfo_ID"/>
          [NoMappingToDatabase]
          public ColumnInfo ColumnInfo
          {
              get
              {
                  return _knownColumnInfo.Value;
              }
          }
          
          internal bool IsColumnInfoCached()
          {
              return _knownColumnInfo.IsValueCreated;
          }
  
          #endregion
  
          /// <summary>
          /// Name of the parent <see cref="Catalogue"/>.  This property value will be cached once fetched for a given object thanks to Lazy/IInjectKnown&lt;Catalogue&gt;"/>.
          /// </summary>
          [UsefulProperty]
          [NoMappingToDatabase]
          [DoNotExtractProperty]
          public string CatalogueName { get { return Catalogue.Name; }}
  
          /// <summary>
          /// Creates a new virtual column description for for a column in the dataset (<paramref name="parent"/>) supplied with the given Name.
          /// <para><remarks>You should next choose which <see cref="ColumnInfo"/> powers it and optionally create an <see cref="ExtractionInformation"/> to
          /// make the column extractable</remarks></para>
          /// </summary>
          public CatalogueItem(ICatalogueRepository repository, ICatalogue parent, string name)
          {
              repository.InsertAndHydrate(this,new Dictionary<string, object>
              {
                  {"Name", name},
                  {"Catalogue_ID", parent.ID}
              });
              
              ClearAllInjections();
              parent.ClearAllInjections();
          }
  
          internal CatalogueItem(ICatalogueRepository repository, DbDataReader r)
              : base(repository, r)
          {
              Catalogue_ID = int.Parse(r["Catalogue_ID"].ToString()); //gets around decimals and other random crud number field types that sql returns
              Name = (string)r["Name"];
              Statistical_cons = r["Statistical_cons"].ToString();
              Research_relevance = r["Research_relevance"].ToString();
              Description = r["Description"].ToString();
  
gt:             Topic = r["Topic"].ToString();