/**
   * Creates a new {@link StateListDrawable} by looking into the contexts and generating one
   * according to their states.
   *
   * @param adapter
   * @param ruleSets
   * @param contexts
   * @return A new {@link StateListDrawable}. <code>null</code> in case the contexts is <code>null
   *     </code> or empty.
   */
  public static StateListDrawable createNewStateListDrawable(
      PXStyleAdapter adapter, List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) {
    if (contexts != null && !contexts.isEmpty()) {
      // No state-lists here, so simply loop and set the backgrounds

      StateListDrawable stateListDrawable =
          adapter.shouldAdjustDrawableBounds()
              ? (new StateListDrawableWithBoundsChange())
              : (new StateListDrawable());

      int[][] deferredDefaultStates = null;
      Drawable defaultDrawable = null;
      int rulesetSize = ruleSets.size();
      for (int i = 0; i < rulesetSize; i++) {
        PXStylerContext context = contexts.get(i);
        String activeStateName = context.getActiveStateName();
        if (activeStateName == null) {
          activeStateName = PXStyleInfo.DEFAULT_STYLE;
        }
        Drawable drawable =
            (context.usesImage() || context.usesColorOnly()) ? context.getBackgroundImage() : null;
        // Artificially add states to the one we got. For example, add a
        // 'pressed' state to a 'checked' state.
        int stateValue = PXDrawableUtil.getStateValue(activeStateName);
        if (stateValue == android.R.attr.drawable) {
          deferredDefaultStates = adapter.createAdditionalDrawableStates(stateValue);
          defaultDrawable = drawable;
        } else {
          int[][] activeStates = adapter.createAdditionalDrawableStates(stateValue);
          for (int[] activeState : activeStates) {
            stateListDrawable.addState(activeState, drawable);
          }
        }
      }
      if (deferredDefaultStates != null && defaultDrawable != null) {
        // add the defaults at the end of the state list.
        for (int[] activeState : deferredDefaultStates) {
          stateListDrawable.addState(activeState, defaultDrawable);
        }
      }
      return stateListDrawable;
    }
    return null;
  }
  /**
   * A utility method that will create a new {@link StateListDrawable} from the given contexts and
   * the existing {@link View}'s background that is in that context.
   *
   * @param adapter A {@link PXStyleAdapter}. This will be used to create additional states (see
   *     {@link PXStyleAdapter#createAdditionalDrawableStates(int)}
   * @param existingStates The states that exist in the {@link View} that is being styled. Note that
   *     in case that the existing states are empty (or <code>null</code>), there may be a need to
   *     call the {@link #createNewStateListDrawable(PXStyleAdapter, List, List)} method instead.
   * @param ruleSets
   * @param contexts
   * @return A new {@link StateListDrawable}
   * @see #createNewStateListDrawable(PXStyleAdapter, List, List)
   */
  public static Drawable createDrawable(
      PXStyleAdapter adapter,
      Map<int[], Drawable> existingStates,
      List<PXRuleSet> ruleSets,
      List<PXStylerContext> contexts) {
    PXStylerContext context = contexts.get(0);

    Set<int[]> statesKeys;
    if (existingStates != null) {
      statesKeys = existingStates.keySet();
    } else {
      statesKeys = Collections.emptySet();
    }

    // Will hold the new background.
    StateListDrawable stateListDrawable =
        adapter.shouldAdjustDrawableBounds()
            ? (new StateListDrawableWithBoundsChange())
            : (new StateListDrawable());

    // For every image we have in the contexts, create a drawable and insert
    // it into the StateListDrawable. The assumption is that every context
    // will provide a drawable for a different state. Otherwise, we may end
    // up with multiple drawables for the same state and Android will pick
    // the first one it hits. We also want to keep the order of the original
    // state list, so we do that insertion in two parts. First, we collect
    // all the states indexes that will be 'replaced', and then we do the
    // actual construction of the new state-list while weaving in drawables
    // from the original list and our contexts.
    Map<Integer, Drawable> newStatesPositions = new LinkedHashMap<Integer, Drawable>();
    int rulesetSize = ruleSets.size();
    for (int i = 0; i < rulesetSize; i++) {
      context = contexts.get(i);
      Drawable drawable =
          (context.usesImage() || context.usesColorOnly()) ? context.getBackgroundImage() : null;
      if (drawable != null && existingStates != null && !existingStates.isEmpty()) {
        String activeStateName = context.getActiveStateName();
        if (activeStateName == null) {
          activeStateName = PXStyleInfo.DEFAULT_STYLE;
        }
        // Artificially add states to the one we got. For example, add a
        // 'pressed' state to a 'checked' state.
        int[][] activeStates =
            adapter.createAdditionalDrawableStates(PXDrawableUtil.getStateValue(activeStateName));

        // Find the index we would like to insert this state. Remember
        // it and its newly assigned drawable.
        int index = 0;
        for (int[] state : statesKeys) {
          for (int[] activeState : activeStates) {
            if (Arrays.equals(state, activeState)) {
              // we set the new position, intentionally not
              // breaking out of the loop (last occurrence wins)
              newStatesPositions.put(index, drawable);
            }
          }
          index++;
        }
      }
    }
    // At this point we have the indexes in the original state list that we
    // would like to replace with our drawables, so we loop and create a the
    // new state list.
    int index = 0;
    for (int[] state : statesKeys) {
      Drawable drawable = newStatesPositions.get(index);
      if (drawable != null) {
        // NOTE: This is super important for Spinners and
        // CheckedTextViews! Update the state list drawable bounds size.
        if (adapter.shouldAdjustDrawableBounds()) {
          stateListDrawable.getBounds().union(drawable.getBounds());
        }
        stateListDrawable.addState(state, drawable);
      } else {
        // add the existing one.
        stateListDrawable.addState(state, existingStates.get(state));
      }
      index++;
    }
    return stateListDrawable.mutate();
  }