/// <summary>
 /// Whether the given object is equal to this one
 /// </summary>
 public bool Equals(ItemType obj)
 {
   return (this.Id ?? "").Equals(obj.Id);
 }
    internal static void EnsureSystemData(IAsyncConnection _conn, ref Dictionary<string, ItemType> _itemTypes)
    {
      if (_itemTypes == null)
      {
        _itemTypes = new Dictionary<string, ItemType>();
        var itemTypes = _conn.Apply(@"<Item type='ItemType' action='get' select='is_versionable,is_dependent,implementation_type,core,name'></Item>").Items();
        ItemType result;
        foreach (var itemTypeData in itemTypes)
        {
          result = new ItemType();
          result.Id = itemTypeData.Id();
          result.IsCore = itemTypeData.Property("core").AsBoolean(false);
          result.IsDependent = itemTypeData.Property("is_dependent").AsBoolean(false);
          result.IsFederated = itemTypeData.Property("implementation_type").Value == "federated";
          result.IsPolymorphic = itemTypeData.Property("implementation_type").Value == "polymorphic";
          result.IsVersionable = itemTypeData.Property("is_versionable").AsBoolean(false);
          result.Name = itemTypeData.Property("name").Value;
          result.Reference = ItemReference.FromFullItem(itemTypeData, true);
          _itemTypes[result.Name.ToLowerInvariant()] = result;
        }

        var relationships = _conn.Apply(@"<Item action='get' type='RelationshipType' related_expand='0' select='related_id,source_id,relationship_id,name' />").Items();
        ItemType relType;
        foreach (var rel in relationships)
        {
          if (rel.SourceId().Attribute("name").HasValue()
            && _itemTypes.TryGetValue(rel.SourceId().Attribute("name").Value.ToLowerInvariant(), out result)
            && rel.Property("relationship_id").Attribute("name").HasValue()
            && _itemTypes.TryGetValue(rel.Property("relationship_id").Attribute("name").Value.ToLowerInvariant(), out relType))
          {
            result.Relationships.Add(relType);
          }
        }

        var floatProps = _conn.Apply(@"<Item type='Property' action='get' select='source_id,item_behavior,name' related_expand='0'>
                                        <data_type>item</data_type>
                                        <data_source>
                                          <Item type='ItemType' action='get'>
                                            <is_versionable>1</is_versionable>
                                          </Item>
                                        </data_source>
                                        <item_behavior>float</item_behavior>
                                        <name condition='not in'>'config_id','id'</name>
                                      </Item>").Items();
        foreach (var floatProp in floatProps)
        {
          if (_itemTypes.TryGetValue(floatProp.SourceId().Attribute("name").Value.ToLowerInvariant(), out result))
          {
            result.FloatProperties.Add(floatProp.Property("name").AsString(""));
          }
        }
      }
    }
    /// <summary>
    /// Loads the property metadata for the current type into the schema.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <param name="itemTypeMeta">The properties.</param>
    private void LoadProperties(ItemType type, IReadOnlyItem itemTypeMeta)
    {
      var props = itemTypeMeta.Relationships("Property");
      Property newProp = null;
      foreach (var prop in props)
      {
        newProp = new Property(prop.Property("name").Value);
        newProp.Label = prop.Property("label").Value;
        newProp.SetType(prop.Property("data_type").Value);
        newProp.Precision = prop.Property("prec").AsInt(-1);
        newProp.Scale = prop.Property("scale").AsInt(-1);
        newProp.StoredLength = prop.Property("stored_length").AsInt(-1);
        var foreign = prop.Property("foreign_property").AsItem();
        if (foreign.Exists)
        {
          newProp.ForeignLinkPropName = prop.Property("data_source").KeyedName().Value;
          newProp.ForeignPropName = foreign.Property("name").Value;
          newProp.ForeignTypeName = foreign.SourceId().KeyedName().Value;
        }
        newProp.DataSource = prop.Property("data_source").Value;
        if (newProp.Type == PropertyType.item && prop.Property("data_source").Attribute("name").HasValue())
        {
          newProp.Restrictions.Add(prop.Property("data_source").Attribute("name").Value);
        }
        newProp.Visibility =
          (prop.Property("is_hidden").AsBoolean(false) ? PropertyVisibility.None : PropertyVisibility.MainGrid)
          | (prop.Property("is_hidden2").AsBoolean(false) ? PropertyVisibility.None : PropertyVisibility.RelationshipGrid);
        newProp.SortOrder = prop.Property("sort_order").AsInt(int.MaxValue);
        newProp.ColumnWidth = prop.Property("column_width").AsInt(100);
        newProp.IsRequired = prop.Property("is_required").AsBoolean(false);
        newProp.ReadOnly = prop.Property("readonly").AsBoolean(false);

        //default_value,column_width,is_required,readonly

        type.Properties.Add(newProp.Name, newProp);
      }
    }
 /// <summary>
 /// Try to get an Item Type by ID
 /// </summary>
 public bool TypeById(string id, out ItemType type)
 {
   return _itemTypesById.TryGetValue(id, out type);
 }
    /// <summary>
    /// Gets a promise to return information about property of a given Item Type and name
    /// </summary>
    public IPromise<Property> GetProperty(ItemType itemType, string name)
    {
      if (_conn == null || itemType.Properties.Count > 0)
        return LoadedProperty(itemType, name);

      return GetProperties(itemType)
        .Continue(r =>
        {
          return LoadedProperty(itemType, name);
        });
    }
 private IPromise<Property> LoadedProperty(ItemType itemType, string name)
 {
   Property prop;
   if (itemType.Properties.TryGetValue(name, out prop))
   {
     return Promises.Resolved(prop);
   }
   else
   {
     return Promises.Rejected<Property>(new KeyNotFoundException());
   }
 }
    /// <summary>
    /// Gets a promise to return information about all properties of a given Item Type
    /// </summary>
    public IPromise<IEnumerable<Property>> GetProperties(ItemType itemType)
    {
      if (_conn == null || itemType.Properties.Count > 0)
        return Promises.Resolved<IEnumerable<Property>>(itemType.Properties.Values);

      return _conn.ApplyAsync("<AML><Item action=\"get\" type=\"ItemType\" select=\"name\"><name>@0</name><Relationships><Item action=\"get\" type=\"Property\" select=\"name,label,data_type,data_source,stored_length,prec,scale,foreign_property(name,source_id),is_hidden,is_hidden2,sort_order,default_value,column_width,is_required,readonly\" /></Relationships></Item></AML>"
        , true, true, itemType.Name)
        .Convert(r =>
        {
          LoadProperties(itemType, r.AssertItem());
          return (IEnumerable<Property>)itemType.Properties.Values;
        }).Fail(ex => System.Diagnostics.Debug.Print("PROPLOAD: " + ex.ToString()));
    }
    public IPromise<IEnumerable<string>> GetClassPaths(ItemType itemType)
    {
      if (_conn == null || itemType.ClassPaths != null)
        return Promises.Resolved(itemType.ClassPaths);

      return _conn.ApplyAsync("<AML><Item action=\"get\" type=\"ItemType\" id=\"@0\" select='class_structure'></Item></AML>"
        , true, true, itemType.Id)
        .Convert(r =>
        {
          var structure = r.AssertItem().Property("class_structure").Value;
          if (string.IsNullOrEmpty(structure))
          {
            itemType.ClassPaths = Enumerable.Empty<string>();
          }
          else
          {
            try
            {
              itemType.ClassPaths = ParseClassStructure(new System.IO.StringReader(structure)).ToArray();
            }
            catch (XmlException)
            {
              itemType.ClassPaths = Enumerable.Empty<string>();
            }
          }
          return itemType.ClassPaths;
        });
    }
    /// <summary>
    /// Clear all the metadata and stard asynchronously reloading it.
    /// </summary>
    public void Reset()
    {
      if (_metadataComplete == null || _metadataComplete.IsRejected || _metadataComplete.IsResolved)
      {
        _listValues.Clear();
        _customProps.Clear();
        _itemTypesByName.Clear();
        _itemTypesById = null;
        _metadataComplete = _conn.ApplyAsync(@"<Item type='ItemType' action='get' select='is_versionable,is_dependent,implementation_type,core,name,label'></Item>", true, true)
          .Continue(r =>
          {
            ItemType result;

            foreach (var itemTypeData in r.Items())
            {
              result = new ItemType();
              result.Id = itemTypeData.Id();
              result.IsCore = itemTypeData.Property("core").AsBoolean(false);
              result.IsDependent = itemTypeData.Property("is_dependent").AsBoolean(false);
              result.IsFederated = itemTypeData.Property("implementation_type").Value == "federated";
              result.IsPolymorphic = itemTypeData.Property("implementation_type").Value == "polymorphic";
              result.IsVersionable = itemTypeData.Property("is_versionable").AsBoolean(false);
              result.Label = itemTypeData.Property("label").Value;
              result.Name = itemTypeData.Property("name").Value;
              result.Reference = ItemReference.FromFullItem(itemTypeData, true);
              _itemTypesByName[result.Name] = result;
            }

            _itemTypesById = _itemTypesByName.Values.ToDictionary(i => i.Id);
            return _conn.ApplyAsync(@"<Item action='get' type='RelationshipType' related_expand='0' select='related_id,source_id,relationship_id,name,label' />", true, true);
          })
          .Continue(r =>
          {
            ItemType relType;
            ItemType source;
            ItemType related;

            foreach (var rel in r.Items())
            {
              if (rel.SourceId().Attribute("name").HasValue()
                && _itemTypesByName.TryGetValue(rel.SourceId().Attribute("name").Value, out source)
                && rel.Property("relationship_id").Attribute("name").HasValue()
                && _itemTypesByName.TryGetValue(rel.Property("relationship_id").Attribute("name").Value, out relType))
              {
                source.Relationships.Add(relType);
                relType.Source = source;
                relType.TabLabel = rel.Property("label").AsString(null);
                if (rel.RelatedId().Attribute("name").HasValue()
                  && _itemTypesByName.TryGetValue(rel.RelatedId().Attribute("name").Value, out related))
                {
                  relType.Related = related;
                }
              }
            }

            return _conn.ApplyAsync(@"<Item type='ItemType' action='get' select='id,name'>
                                      <id condition='in'>
                                        (select it.ID
                                        from innovator.[ITEMTYPE] it
                                        where it.ID in
                                          (select source_id
                                           from innovator.[PROPERTY] p
                                           where p.ORDER_BY is not null))
                                      </id>
                                    </Item>", true, false);
          }).Continue(r =>
          {
            ItemType result;
            foreach (var itemType in r.Items())
            {
              if (_itemTypesByName.TryGetValue(itemType.Property("name").Value, out result))
              {
                result.IsSorted = true;
              }
            }

            return _conn.ApplyAsync(@"<Item type='Property' action='get' select='source_id,item_behavior,name' related_expand='0'>
                                      <data_type>item</data_type>
                                      <data_source>
                                        <Item type='ItemType' action='get'>
                                          <is_versionable>1</is_versionable>
                                        </Item>
                                      </data_source>
                                      <item_behavior>float</item_behavior>
                                      <name condition='not in'>'config_id','id'</name>
                                    </Item>", true, false);
          })
          .Done(r =>
          {
            ItemType result;
            foreach (var floatProp in r.Items())
            {
              if (_itemTypesByName.TryGetValue(floatProp.SourceId().Attribute("name").Value.ToLowerInvariant(), out result))
              {
                result.FloatProperties.Add(floatProp.Property("name").AsString(""));
              }
            }
          });
        _secondaryMetadata = _conn.ApplyAsync(@"<Item type='Method' action='get' select='config_id,core,name'></Item>", true, false)
          .Continue(r =>
          {
            _methods = r.Items().Select(i =>
            {
              var method = Method.FromFullItem(i, false);
              method.KeyedName = i.Property("name").AsString("");
              method.IsCore = i.Property("core").AsBoolean(false);
              return method;
            }).ToArray();

            return _conn.ApplyAsync(@"<Item type='Identity' action='get' select='id,name'>
                                      <name condition='in'>'World', 'Creator', 'Owner', 'Manager', 'Innovator Admin', 'Super User'</name>
                                    </Item>", true, true);
          }).Continue(r =>
          {
            var sysIdents =
              r.Items()
              .Select(i =>
              {
                var itemRef = ItemReference.FromFullItem(i, false);
                itemRef.KeyedName = i.Property("name").AsString("");
                return itemRef;
              });
            _systemIdentities = sysIdents.ToDictionary(i => i.Unique);

            return _conn.ApplyAsync(@"<Item type='SQL' action='get' select='id,name,type'></Item>", true, false);
          }).Continue(r =>
          {
            var sqlItems = r.Items()
              .Select(i =>
              {
                var itemRef = Sql.FromFullItem(i, false);
                itemRef.KeyedName = i.Property("name").AsString("");
                itemRef.Type = i.Property("type").AsString("");
                return itemRef;
              });
            _sql = sqlItems.ToDictionary(i => i.KeyedName.ToLowerInvariant(), StringComparer.OrdinalIgnoreCase);

            return _conn.ApplyAsync(@"<Item type='Property' action='get' select='name,source_id(id,name)'>
                                          <id condition='in'>(SELECT p.id
                                        from innovator.PROPERTY p
                                        inner join innovator.ITEMTYPE it
                                        on p.SOURCE_ID = it.id
                                        where p.CREATED_BY_ID &lt;&gt; 'AD30A6D8D3B642F5A2AFED1A4B02BEFA'
                                        and it.CORE = 1
                                        and it.CREATED_BY_ID = 'AD30A6D8D3B642F5A2AFED1A4B02BEFA')</id>
                                        </Item>", true, false);
          }).Continue(r =>
          {
            IReadOnlyItem itemType;
            foreach (var customProp in r.Items())
            {
              itemType = customProp.SourceItem();
              _customProps[new ItemProperty()
              {
                ItemType = itemType.Property("name").Value,
                ItemTypeId = itemType.Id(),
                Property = customProp.Property("name").Value,
                PropertyId = customProp.Id()
              }] = new ItemReference("Property", customProp.Id())
              {
                KeyedName = customProp.Property("name").Value
              };
            }

            return _conn.ApplyAsync(@"<Item type='List' action='get' select='id'>
                                      <id condition='in'>(select l.id
                                        from innovator.LIST l
                                        inner join innovator.PROPERTY p
                                        on l.id = p.DATA_SOURCE
                                        and p.name = 'itemtype'
                                        inner join innovator.ITEMTYPE it
                                        on it.id = p.SOURCE_ID
                                        and it.IMPLEMENTATION_TYPE = 'polymorphic')
                                      </id>
                                    </Item>", true, false);
          }).Done(r =>
          {
            _polyItemLists = r.Items().Select(i => ItemReference.FromFullItem(i, true));
          });
      }
    }
    public IPromise<IEnumerable<string>> ItemTypeStates(ItemType itemtype)
    {
      if (itemtype.States != null)
        return Promises.Resolved(itemtype.States);

      return _conn.ApplyAsync(@"<Item type='Life Cycle State' action='get' select='name'>
          <source_id condition='in'>(select related_id from innovator.[ItemType_Life_Cycle] where source_id = @0)</source_id>
        </Item>", true, false, itemtype.Id)
        .Convert(r =>
        {
          var states = r.Items().Select(i => i.Property("name").Value).ToArray();
          itemtype.States = states;
          return (IEnumerable<string>)states;
        });
    }
 /// <summary>
 /// Try to get an Item Type by name
 /// </summary>
 public bool ItemTypeByName(string name, out ItemType type)
 {
   return _itemTypesByName.TryGetValue(name, out type);
 }