/// <summary>
    /// Apply inherited styles from their ancestors to them.
    /// </summary>
    /// <param name="definitions"></param>
    /// <param name="env"></param>
    /// <returns>an array of arrays is returned, in which each array refers to a specific attachment</returns>
    private static List<CartoStyle> InheritDefinitions(List<CartoDefinition> definitions, Env env)
    {
      // definitions are ordered by specificity,
      // high (index 0) to low
      Dictionary<string, CartoStyle> byAttachment = new Dictionary<string, CartoStyle>();
      Dictionary<string, SortedDictionary<string, CartoDefinition>> byFilter = new Dictionary<string, SortedDictionary<string, CartoDefinition>>();
      List<CartoStyle> result = new List<CartoStyle>();
      List<CartoDefinition> current;
      string attachment;

      // Evaluate the filters specified by each definition with the given
      // environment to correctly resolve variable references
      definitions.ForEach(delegate (CartoDefinition def) {
        def.Filters.Evaluate(env);
      });

      current = new List<CartoDefinition>();

      for (int i = 0; i < definitions.Count; i++)
      {
        CartoDefinition defI = definitions[i];
        attachment = defI.Attachment;

        current.Clear();
        current.Add(defI);

        if (!byAttachment.ContainsKey(attachment))
        {
          CartoStyle style = new CartoStyle();
          style.Attachment = attachment;

          byAttachment.Add(attachment, style);
          byAttachment[attachment].Attachment = attachment;
          byFilter[attachment] = new SortedDictionary<string, CartoDefinition>();

          result.Add(byAttachment[attachment]);
        }

        // Iterate over all subsequent rules.
        for (var j = i + 1; j < definitions.Count; j++)
        {
          CartoDefinition defJ = definitions[j];
          if (defJ.Attachment == attachment)
          {
            // Only inherit rules from the same attachment.
            current = AddRules(current, defJ, byFilter[attachment], env);
          }
        }

        for (var k = 0; k < current.Count; k++)
        {
          byFilter[attachment][current[k].Filters.ToString()] = current[k];
          byAttachment[attachment].Add(current[k]);
        }
      }

      return result;
    }
    /// <summary>
    /// Creates <see cref="FeatureTypeStyle"/> object
    /// </summary>
    /// <param name="styleName"></param>
    /// <param name="cartoStyle"></param>
    /// <param name="env"></param>
    /// <param name="cartoTranslator"></param>
    /// <returns></returns>
    private static FeatureTypeStyle CreateStyle(string styleName, CartoStyle cartoStyle, Env env, ICartoTranslator cartoTranslator)
    {
      FeatureTypeStyle style = new FeatureTypeStyle(styleName);
      style.ProcessFeatureOnce = true;

      List<CartoDefinition> definitions = cartoStyle.Definitions;
      Dictionary<string, int> existingFilters = null;
      List<string> imageFilters = new List<string>();
      List<string> imageFiltersBuffer = new List<string>();

      int k = 0;

      for (int i = 0; i < definitions.Count; i++)
      {
        CartoDefinition def = definitions[i];
        NodeList<CartoRule> rules = def.Rules;
        for (int j = 0; j < rules.Count; j++)
        {
          CartoRule cartoRule = rules[j];
          string ruleName = cartoRule.Name;
          if (ruleName == "image-filters")
          {
            string filter = cartoRule.Value.ToString();
            if (!imageFilters.Contains(filter))
            {
              style.ImageFiltersOptions.Filters.Add(cartoTranslator.ToImageFilter(filter));
              style.ImageFiltersOptions.Enabled = true;
              imageFilters.Add(filter);
            }
            k++;
          }
          else if (ruleName == "image-filters-inflate")
          {
            bool inflate = Convert.ToBoolean(cartoRule.Value.ToString());

            if (inflate)
              style.ImageFiltersOptions.Enabled = true;
            k++;
          }
          else if (ruleName == "direct-image-filters")
          {
            string filter = cartoRule.Value.ToString();
            if (!imageFiltersBuffer.Contains(filter))
            {
              style.ImageFiltersOptions.BufferFilters.Add(cartoTranslator.ToImageFilter(filter));
              style.ImageFiltersOptions.Enabled = true;
              imageFiltersBuffer.Add(filter);
            }
            k++;
          }
          else if (ruleName == "comp-op")
          {
            style.BlendingOptions.CompositingMode = cartoTranslator.ToCompositingMode(cartoRule.Value.Evaluate(env).ToString());
            k++;
          }
          else if (ruleName == "opacity")
          {
            style.BlendingOptions.Opacity = (float)(cartoRule.Value.Evaluate(env) as Number).Value;
            k++;
          }
          if (ruleName == "filter-mode")
          {
            string filterMode = cartoRule.Value.Evaluate(env).ToCSS(env);
            if (string.Equals(filterMode, "all"))
              style.ProcessFeatureOnce = false;
            else if (string.Equals(filterMode, "first"))
              style.ProcessFeatureOnce = true;

            k++;
          }
        }

        if (k < rules.Count)
        {
          if (existingFilters == null)
            existingFilters = new Dictionary<string, int>();

          CreateRules(style, def, env, existingFilters, cartoTranslator);
        }
      }

      return style;
    }