using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using IdeaBlade.Aop;
using IdeaBlade.Core.Composition;
using IdeaBlade.EntityModel;
using IbEm = IdeaBlade.EntityModel;

namespace ConsoleApplication3 {
  class Program {
    static void Main(string[] args) {
      Console.WriteLine("without Attaching");
      Test(false);
      Console.WriteLine("Done");

      Console.WriteLine("Attaching");
      Test(true);
      Console.WriteLine("Done");

      Console.WriteLine("Attaching with workaround");
      Test2();
      Console.WriteLine("Done");

      Console.ReadLine();
    }

    private static void Test2() {
      var manager = new TestEntities();

      var profession = new Profession() {
        Id = 1,
        Title = "Boss",
      };

      var user = new User {
        Id = 2,
        Name = "User test with Profession",
        Profession = profession,
      };
      
      //Another workaround is to relate User and Profession first then Attach.
      manager.AttachEntity(profession);

      Debug.Assert(profession.Id == 1);
      Debug.Assert(EntityAspect.Wrap(profession).EntityState.IsUnchanged());
      Debug.Assert(user.Id == 2);
      Debug.Assert(EntityAspect.Wrap(user).EntityState.IsUnchanged());

      Console.WriteLine("Profession Id expected = 1 - Id current value = {0} ", profession.Id);
      Console.WriteLine("User Id expected = 2 - Id current value = {0} ", user.Id);
    }

    private static void Test(bool attachProfession) {
      var manager = new TestEntities();

      var profession = new Profession() {
        Id = 1,
        Title = "Boss",
      };
      if (attachProfession)
        manager.AttachEntity(profession);

      Debug.Assert(profession.Id == 1);

      var user = new User {
        Id = 2,
        Name = "User test with Profession",

        //When we assign Profession here, the operation that occurs is similar to that of AddEntity.
        //Notice that user.EntityAspect.EntityState changes from Detached to Added.
        //The AttachEntity below doesn't do anything.
        //Profession = profession,
      };

      //To achieve what you want, attach the entity first then assign its Profession.
      manager.AttachEntity(user);
      user.Profession = profession;

      Debug.Assert(user.Id == 2, string.Format("Id expected = 2 - Id current value = {0} ", user.Id));
      Console.WriteLine("Id expected = 2 - Id current value = {0} ", user.Id);
    }
  }


  [DataContract(IsReference = true)]
  [ProvideEntityAspect]
  public abstract class BaseEntity : IEntity {
    public EntityAspect EntityAspect {
      get { return null; }
    }
  }

  public class User : BaseEntity {
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    [ForeignKey("Profession")]
    public int ProfessionId { get; set; }
    public Profession Profession { get; set; }
  }

  public class Profession : BaseEntity {
    [Key]
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }
  }

  [DataSourceKeyName("TestNavigation")]
  public class TestEntities : IbEm.EntityManager {
    #region Constructors
    public TestEntities(IbEm.EntityManagerContext context)
      : base(context) {
    }

    public TestEntities(IbEm.EntityManager entityManager, IbEm.EntityManagerContext entityManagerContext = null)
      : base(entityManager, entityManagerContext) {
    }


    public TestEntities(bool shouldConnect = true, string dataSourceExtension = null, IbEm.EntityServiceOption entityServiceOption = IbEm.EntityServiceOption.UseDefaultService, string compositionContextName = null)
      : base(shouldConnect, dataSourceExtension, entityServiceOption, compositionContextName) {
    }

    public TestEntities(IbEm.EntityManager entityManager, bool shouldConnect, string dataSourceExtension = null, IbEm.EntityServiceOption entityServiceOption = IbEm.EntityServiceOption.UseDefaultService, string compositionContextName = null)
      : base(entityManager, shouldConnect, dataSourceExtension, entityServiceOption, compositionContextName) {
    }

    #endregion

    #region EntityQueries

    public EntityQuery<User> Users { get; set; }
    public EntityQuery<Profession> Professions { get; set; }
    #endregion
  }

  [DataSourceKeyName("TestNavigation")]
  public class TestDbContex : DbContext {
    #region Constructors
    public TestDbContex() {
      Database.SetInitializer(new DropCreateDatabaseIfModelChanges<TestDbContex>());
    }

    public TestDbContex(string connexion)
      : base(connexion) {
      Database.SetInitializer(new DropCreateDatabaseIfModelChanges<TestDbContex>());
    }

    #endregion

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Ignore<EntityAspect>();

      modelBuilder.Entity<User>().HasRequired(p => p.Profession);
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Profession> Professions { get; set; }

  }


}
