// Copyright (C) 2003-2010 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Alex Ustinov
// Created:    2007.07.11

using System;
using System.Collections.Generic;
using Xtensive.Collections;
using Xtensive.Core;
using Xtensive.Reflection;
using Xtensive.Orm.Building.Builders;
using Xtensive.Orm.Model;

using System.Linq;

namespace Xtensive.Orm.Building.Definitions
{
  /// <summary>
  /// Defines the whole <see cref="Domain"/> model.
  /// The root definition.
  /// </summary>
  [Serializable]
  public sealed class DomainModelDef : Node
  {
    private readonly ModelDefBuilder builder;
    private readonly TypeDefCollection types;
    private readonly FullTextIndexDefCollection fullTextIndexes;
    private readonly Validator validator;

    /// <summary>
    /// Gets the <see cref="TypeDef"/> instances contained in this instance.
    /// </summary>
    public TypeDefCollection Types { get { return types; } }

    /// <summary>
    /// Gets the collection of <see cref="HierarchyDef"/> instances contained in this instance.
    /// </summary>
    public HierarchyDefCollection Hierarchies { get; } = new();

    /// <summary>
    /// Gets the collection of <see cref="FullTextIndexDef"/> instances contained in this instance.
    /// </summary>
    public FullTextIndexDefCollection FullTextIndexes { get { return fullTextIndexes; } }

    /// <summary>
    /// Defines new <see cref="TypeDef"/> and adds it to <see cref="DomainModelDef"/> instance.
    /// </summary>
    /// <param name="type">The underlying type.</param>
    /// <returns>Newly created <see cref="TypeDef"/> instance.</returns>
    public TypeDef DefineType(Type type)
    {
      validator.EnsureTypeIsPersistent(type);

      if (types.Contains(type))
        throw new DomainBuilderException(string.Format(Strings.ExTypeXIsAlreadyDefined, type.GetFullName()));

      return builder.ProcessType(type);
    }

    /// <summary>
    /// Finds the root of inheritance hierarchy for the specified <paramref name="item"/>.
    /// </summary>
    /// <param name="item">The type to search root for.</param>
    /// <returns><see name="TypeDef"/> instance that is root of specified <paramref name="item"/> or 
    /// <see langword="null"/> if the root is not found in this collection.</returns>
    /// <exception cref="ArgumentNullException">When <paramref name="item"/> is <see langword="null"/>.</exception>
    public TypeDef FindRoot(TypeDef item) => FindHierarchy(item)?.Root;

    /// <summary>
    /// Finds the hierarchy.
    /// </summary>
    /// <param name="item">The type to search hierarchy for.</param>
    /// <returns><see cref="HierarchyDef"/> instance or <see langword="null"/> if hierarchy is not found.</returns>
    public HierarchyDef FindHierarchy(TypeDef item) => Hierarchies.Find(item);

    private void OnTypesCleared(object sender, TypeDefCollectionClearedEventArgs e)
    {
      Hierarchies.Clear();
    }

    private void OnTypeRemoved(object sender, TypeDefCollectionChangedEventArgs e)
    {
      HierarchyDef hd = Hierarchies.TryGetValue(e.Item);
      if (hd != null)
        Hierarchies.Remove(hd);
    }

    // Constructors

    internal DomainModelDef(ModelDefBuilder builder, Validator validator)
    {
      this.builder = builder;
      this.validator = validator;

      types = new TypeDefCollection(this, "Types");
      fullTextIndexes = new FullTextIndexDefCollection();

      types.Removed += OnTypeRemoved;
      types.Cleared += OnTypesCleared;
    }
  }
}