<commit_before>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Reflection;

using Clifton.Core.Assertions;
using Clifton.Core.ExtensionMethods;
using Clifton.Core.ServiceInterfaces;

namespace Clifton.Core.ModelTableManagement
{
	public class ExtDataColumn : DataColumn
	{
		public bool Visible { get; set; }

		public ExtDataColumn(string colName, Type colType, bool visible)
			: base(colName, colType)
		{
			Visible = visible;
		}
	}

	public class Field
	{
		public string Name { get; set; }
		public string DisplayName { get; set; }
		public Type Type { get; set; }
		public bool ReadOnly { get; set; }
		public bool Visible { get; set; }
		public bool IsColumn { get; set; }
		public bool IsDisplayField { get; set; }

		public bool IsTableField { get { return IsColumn || IsDisplayField; } }
	}

	public class ModelPropertyChangedEventArgs : EventArgs
	{
		public string FieldName { get; set; }
		public object Value { get; set; }
	}

	/// <summary>
	/// Provides services to populate a model collection and to track model collections by their model type.
	/// </summary>
	public class ModelMgr
	{
		/// <summary>
		/// Fires when a model field has been updated with a programmatic call to UpdateRecordField or when the UI changes the associated field in the DataTable.
		/// </summary>
		public event EventHandler<ModelPropertyChangedEventArgs> PropertyChanged;

		public IDbContextService DbContextService { get { return db; } }

		protected Dictionary<Type, List<IEntity>> mappedRecords;
		protected IDbContextService db;

		public ModelMgr(IDbContextService db)
		{
			this.db = db;
			mappedRecords = new Dictionary<Type, List<IEntity>>();
		}

		public void Register<T>() where T : MappedRecord, IEntity
		{
			Type recType = typeof(T);
			mappedRecords[recType] = new List<IEntity>();
		}

		/// <summary>
		/// Loads all the records for the model type into the DataView and our underlying model collection for that model type.
		/// </summary>
		public List<IEntity> LoadRecords<T>(DataView dv) where T : MappedRecord, IEntity
		{
			Type recType = typeof(T);
			// if (!mappedRecords.ContainsKey(recType)) mappedRecords[recType] = new List<IEntity>();
			// Create or clear anything that already exists.
			mappedRecords[recType] = new List<IEntity>();
			(from rec in db.Context.GetTable<T>() select rec).ForEach(m => AddRow(dv, (T)m));			// The cast to (T) is critical here so that the type is T rather than MappedRecord.

			return mappedRecords[recType];
		}

		/// <summary>
		/// Adds all the records for the model type into the DataView and our underlying EXISTING model collection for that model type.
		/// </summary>
		public void AddRecords<T>(DataView dv) where T : MappedRecord, IEntity
		{
			Assert.That(mappedRecords.ContainsKey(typeof(T)), "Model Manager does not know about " + typeof(T).Name + ".\r\nCreate an instance of ModuleMgr with this record collection.");

			Type recType = typeof(T);
			mappedRecords[recType].ForEach(m =>
				{
					DataRow row = NewRow(dv, (T)m);		// The cast to (T) is critical here so that the type is T rather than MappedRecord.
					dv.Table.Rows.Add(row);
				});
		}

		/// <summary>
		/// Returns a collection of type IEntity.  This method is used to initialize a ModelTable instance.
		/// </summary>
		public List<IEntity> GetEntityRecordCollection<T>() where T : MappedRecord, IEntity, new()
		{
			Type recType = typeof(T);

			return mappedRecords[recType];
		}

		/// <summary>
		/// Returns a collection cast to the specified model type, which can by used by the application to get a concrete collection of the model type, as opposed
		/// the the GetEntityRecordCollection, which returns a collection of IEntity.
		/// </summary>
		public List<T> GetRecordCollection<T>() where T : MappedRecord, IEntity, new()
		{
			Type recType = typeof(T);

			return mappedRecords[recType].Cast<T>().ToList();
		}

		/// <summary>
		/// Returns a view by inspecting, via reflection, the model's properties.
		/// Those properties decorated with the DisplayField attribute are added, in order, to the underlying DataTable column collection.
		/// Optionally, properties with the DisplayName attribute set the caption for the column.
		/// </summary>
		public DataView CreateView<T>()
		{
			DataTable dt = new DataTable();
			List<Field> fields = GetFields<T>();

			foreach (Field field in fields.Where(f=>f.IsTableField))
			{
				// Only create columns in the underlying data table for those properties in the model that have Column or DisplayField attributes.
				// Otherwise, we start tracking things like EntityRef properties, etc, that we don't want to track!
				ExtDataColumn dc;

				// Handle nullable types by creating the column type as the underlying, non-nullable, type.
				if (field.Type.Name == "Nullable`1")
				{
					dc = new ExtDataColumn(field.Name, field.Type.UnderlyingSystemType.GenericTypeArguments[0], field.Visible);
				}
				else
				{
					dc = new ExtDataColumn(field.Name, field.Type, field.Visible);
				}

				dc.ReadOnly = field.ReadOnly;
				dc.Caption = field.DisplayName;
				dt.Columns.Add(dc);
			}

			return new DataView(dt);
		}

		/// <summary>
		/// Appends a DataRow from the fields in the model and adds the row to the underlying model collection.
		/// </summary>
		public void AddRow<T>(DataView view, T model) where T : MappedRecord
		{
			DataRow row = NewRow(view, model);
			view.Table.Rows.Add(row);
			AddRecordToCollection(model);
		}

		/// <summary>
		/// Inserts a DataRow from the fields in the model and adds the row to the underlying model collection.
		/// </summary>
		public void InsertRow<T>(DataView view, T model) where T : MappedRecord
		{
			DataRow row = NewRow(view, model);
			view.Table.Rows.InsertAt(row, 0);
			AddRecordToCollection(model);
		}<commit_msg>Added IsDbColumn so we don't update records when a non-DB column is changed.<commit_after>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Reflection;

using Clifton.Core.Assertions;
using Clifton.Core.ExtensionMethods;
using Clifton.Core.ServiceInterfaces;

namespace Clifton.Core.ModelTableManagement
{
	public class ExtDataColumn : DataColumn
	{
		public bool Visible { get; set; }
		public bool IsDbColumn { get; set; }

		public ExtDataColumn(string colName, Type colType, bool visible, bool isDbColumn)
			: base(colName, colType)
		{
			Visible = visible;
			IsDbColumn = isDbColumn;
		}
	}

	public class Field
	{
		public string Name { get; set; }
		public string DisplayName { get; set; }
		public Type Type { get; set; }
		public bool ReadOnly { get; set; }
		public bool Visible { get; set; }
		public bool IsColumn { get; set; }
		public bool IsDisplayField { get; set; }

		public bool IsTableField { get { return IsColumn || IsDisplayField; } }
	}

	public class ModelPropertyChangedEventArgs : EventArgs
	{
		public string FieldName { get; set; }
		public object Value { get; set; }
	}

	/// <summary>
	/// Provides services to populate a model collection and to track model collections by their model type.
	/// </summary>
	public class ModelMgr
	{
		/// <summary>
		/// Fires when a model field has been updated with a programmatic call to UpdateRecordField or when the UI changes the associated field in the DataTable.
		/// </summary>
		public event EventHandler<ModelPropertyChangedEventArgs> PropertyChanged;

		public IDbContextService DbContextService { get { return db; } }

		protected Dictionary<Type, List<IEntity>> mappedRecords;
		protected IDbContextService db;

		public ModelMgr(IDbContextService db)
		{
			this.db = db;
			mappedRecords = new Dictionary<Type, List<IEntity>>();
		}

		public void Register<T>() where T : MappedRecord, IEntity
		{
			Type recType = typeof(T);
			mappedRecords[recType] = new List<IEntity>();
		}

		/// <summary>
		/// Loads all the records for the model type into the DataView and our underlying model collection for that model type.
		/// </summary>
		public List<IEntity> LoadRecords<T>(DataView dv) where T : MappedRecord, IEntity
		{
			Type recType = typeof(T);
			// if (!mappedRecords.ContainsKey(recType)) mappedRecords[recType] = new List<IEntity>();
			// Create or clear anything that already exists.
			mappedRecords[recType] = new List<IEntity>();
			(from rec in db.Context.GetTable<T>() select rec).ForEach(m => AddRow(dv, (T)m));			// The cast to (T) is critical here so that the type is T rather than MappedRecord.

			return mappedRecords[recType];
		}

		/// <summary>
		/// Adds all the records for the model type into the DataView and our underlying EXISTING model collection for that model type.
		/// </summary>
		public void AddRecords<T>(DataView dv) where T : MappedRecord, IEntity
		{
			Assert.That(mappedRecords.ContainsKey(typeof(T)), "Model Manager does not know about " + typeof(T).Name + ".\r\nCreate an instance of ModuleMgr with this record collection.");

			Type recType = typeof(T);
			mappedRecords[recType].ForEach(m =>
				{
					DataRow row = NewRow(dv, (T)m);		// The cast to (T) is critical here so that the type is T rather than MappedRecord.
					dv.Table.Rows.Add(row);
				});
		}

		/// <summary>
		/// Returns a collection of type IEntity.  This method is used to initialize a ModelTable instance.
		/// </summary>
		public List<IEntity> GetEntityRecordCollection<T>() where T : MappedRecord, IEntity, new()
		{
			Type recType = typeof(T);

			return mappedRecords[recType];
		}

		/// <summary>
		/// Returns a collection cast to the specified model type, which can by used by the application to get a concrete collection of the model type, as opposed
		/// the the GetEntityRecordCollection, which returns a collection of IEntity.
		/// </summary>
		public List<T> GetRecordCollection<T>() where T : MappedRecord, IEntity, new()
		{
			Type recType = typeof(T);

			return mappedRecords[recType].Cast<T>().ToList();
		}

		/// <summary>
		/// Returns a view by inspecting, via reflection, the model's properties.
		/// Those properties decorated with the DisplayField attribute are added, in order, to the underlying DataTable column collection.
		/// Optionally, properties with the DisplayName attribute set the caption for the column.
		/// </summary>
		public DataView CreateView<T>()
		{
			DataTable dt = new DataTable();
			List<Field> fields = GetFields<T>();

			foreach (Field field in fields.Where(f=>f.IsTableField))
			{
				// Only create columns in the underlying data table for those properties in the model that have Column or DisplayField attributes.
				// Otherwise, we start tracking things like EntityRef properties, etc, that we don't want to track!
				ExtDataColumn dc;

				// Handle nullable types by creating the column type as the underlying, non-nullable, type.
				if (field.Type.Name == "Nullable`1")
				{
					dc = new ExtDataColumn(field.Name, field.Type.UnderlyingSystemType.GenericTypeArguments[0], field.Visible, field.IsColumn);
				}
				else
				{
					dc = new ExtDataColumn(field.Name, field.Type, field.Visible, field.IsColumn);
				}

				dc.ReadOnly = field.ReadOnly;
				dc.Caption = field.DisplayName;
				dt.Columns.Add(dc);
			}

			return new DataView(dt);
		}

		/// <summary>
		/// Appends a DataRow from the fields in the model and adds the row to the underlying model collection.
		/// </summary>
		public void AddRow<T>(DataView view, T model) where T : MappedRecord
		{
			DataRow row = NewRow(view, model);
			view.Table.Rows.Add(row);
			AddRecordToCollection(model);
		}

		/// <summary>
		/// Inserts a DataRow from the fields in the model and adds the row to the underlying model collection.
		/// </summary>
		public void InsertRow<T>(DataView view, T model) where T : MappedRecord
		{
			DataRow row = NewRow(view, model);
			view.Table.Rows.InsertAt(row, 0);
			AddRecordToCollection(model);
		}
{'fraction_non_alphanumeric': 0.10566475906112632, 'fraction_numerical': 0.0004976362279173924, 'mean_word_length': 3.550188679245283, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1084768', 'n_tokens_mistral': 3946, 'n_tokens_neox': 3508, 'n_words': 1257}