Exercise 2 Solution

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Plato.Core
{
    /// <summary>
    /// The set of entities.
    /// </summary>
    public class Brain
    {
        // The set of entities.
        private Dictionary<string, Entity> entities;

        public Brain()
        {
            entities = new Dictionary<string, Entity>();
        }

        /// <summary>
        /// Create/add a new entity.
        /// </summary>
        /// <param name="id">The entity's internal ID.</param>
        /// <returns>The new entity.</returns>
        public IEntity CreateEntity(string id)
        {
            if (entities.ContainsKey(id))
            {
                throw new Exception(String.Format("Entity [{0}] already exists.", id));
            }

            Entity newEntity = new Entity(id);
            entities.Add(id, newEntity);

            return newEntity;
        }

        /// <summary>
        /// Returns an entity given its ID.
        /// </summary>
        /// <param name="id">The entity's internal ID.</param>
        /// <returns>The entity.</returns>
        public IEntity Get(string id)
        {
            if (!entities.ContainsKey(id))
            {
                throw new Exception(String.Format("Entity [{0}] doesn't exist.", id));
            }

            return entities[id];
        }

        /// <summary>
        /// Create/add a new relationship.
        /// </summary>
        /// <param name="entity1">The first entity.</param>
        /// <param name="entity2">The second entity.</param>
        /// <param name="relation">The relation that defines how the entities are related.</param>
        /// <param name="args">Optional argument entities.</param>
        /// <param name="directed">Whether or not the relationship is directed.</param>
        /// <returns>The new relationship.</returns>
        public IRelationship CreateRelationship(IEntity entity1, IEntity relation, IEntity entity2, IEntity[] args, bool directed)
        {
            Relationship newRelationship = new Relationship(entity1, relation, entity2, args, directed);
            entity1.AddRelationship(newRelationship);
            return newRelationship;
        }

        public IRelationship CreateRelationship(IEntity entity1, IEntity relation, IEntity entity2, bool directed)
        {
            return CreateRelationship(entity1, relation, entity2, null, directed);
        }

        public IRelationship CreateRelationship(IEntity entity1, IEntity relation, IEntity entity2)
        {
            return CreateRelationship(entity1, relation, entity2, null, true);
        }

        public IRelationship CreateRelationship(IEntity entity1, IEntity relation, IEntity entity2, IEntity[] args)
        {
            return CreateRelationship(entity1, relation, entity2, args, true);
        }
    }

    /// <summary>
    /// An entity.
    /// </summary>
    internal class Entity : IEntity
    {
        private string id;

        // Relationships, organized by target entity.
        private Dictionary<string, List<IRelationship>> relationships;

        // Values which have been assigned to properties
        private Dictionary<string, List<IEntity>> values;

        public Entity(string id)
        {
            this.id = id;
            this.values = new Dictionary<string, List<IEntity>>();
            this.relationships = new Dictionary<string, List<IRelationship>>();
        }

        /// <summary>
        /// The ID by which the entity is internally referenced.
        /// </summary>
        public string Id
        {
            get { return id; }
            set { id = value; }
        }

        /// <summary>
        /// Add a relationship between this entity and another entity.
        /// </summary>
        /// <param name="relationship">The relationship.</param>
        public void AddRelationship(IRelationship relationship)
        {
            if (relationship.Entity1.Id != this.id)
            {
                throw new ArgumentException("The relationship's source entity doesn't match this entity.");
            }

            List<IRelationship> list;
            string entity2 = relationship.Entity2.Id;

            if (relationships.ContainsKey(entity2))
            {
                // One or more relationships already exist with this
                // entity. Add the new relationship.
                relationships[entity2].Add(relationship);
            }
            else
            {
                // No relationships yet exist with this entity.
                // Add this first relationship.
                list = new List<IRelationship>();
                list.Add(relationship);
                relationships[relationship.Entity2.Id] = list;
            }
        }

        /// <summary>
        /// Assign a value to a property.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <param name="value">The value.</param>
        public void Set(IEntity property, IEntity value)
        {
            // TODO: Verify 'has' relationship exists

            List<IEntity> list = new List<IEntity>();
            list.Add(value);

            // Overwrite any existing value
            values[property.Id] = list;
        }

        /// <summary>
        /// Get a property's value.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <returns>The value if set, or null otherwise.</returns>
        public IEntity Get(IEntity property)
        {
            if (values.ContainsKey(property.Id))
            {
                return values[property.Id][0];
            }
            else
            {
                return null;
            }
        }
    }

    /// <summary>
    /// A relationship.
    /// </summary>
    internal class Relationship : Entity, IRelationship
    {
        // The 'source' entity
        private IEntity entity1;

        // The target entity
        private IEntity entity2;

        private IEntity relation;
        private IEntity[] args;
        private bool directed;

        public Relationship(IEntity entity1, IEntity relation, IEntity entity2, IEntity[] args)
            : base(null)
        {
            if (entity1 == null)
            {
                throw new ArgumentNullException("entity1");
            }
            if (entity2 == null)
            {
                throw new ArgumentNullException("entity2");
            }
            if (relation == null)
            {
                throw new ArgumentNullException("relation");
            }

            this.entity1 = entity1;
            this.entity2 = entity2;
            this.relation = relation;
            this.args = args;
            directed = true;

            // Create a reference from the first entity
            // to this relationship.
            entity1.AddRelationship(this);

        }

        public Relationship(IEntity entity1, IEntity relation, IEntity entity2, IEntity[] args, bool directed)
            : this(entity1, relation, entity2, args)
        {
            this.directed = directed;
        }

        /// <summary>
        /// The first entity of the relationship.
        /// </summary>
        public IEntity Entity1
        {
            get { return entity1; }
        }

        /// <summary>
        /// The second Entity of the relationship.
        /// </summary>
        public IEntity Entity2
        {
            get { return entity2; }
        }

        /// <summary>
        /// The relation defines how the two entities relate.
        /// </summary>
        public IEntity Relation
        {
            get { return relation; }
        }

        /// <summary>
        /// Optional entity arguments.
        /// </summary>
        public IEntity[] Args
        {
            get { return args; }
        }

        /// <summary>
        /// Is the relationship directed?
        /// </summary>
        public bool Directed
        {
            get { return directed; }
        }
    }
}