function createCombinationsBehavior(settings) {
  var defaultSettings = {
    contentEl: false,
    fetchUrl: false
  };

  return {
    settings: $.extend({}, defaultSettings, settings),
    params: {
      availableCombinations: [],
      availableAttributeIds: []
    },

    init: function () {
      var $contentEl = $(this.settings.contentEl);
      var $attributes = $('[data-attr]', $contentEl);
      if ($attributes && $attributes.length > 0) {
        var self = this;

        $.each($attributes, function (i, attribute) {
          var id = parseInt($(attribute).data('attr'));
          self.params.availableAttributeIds.push(id);
        });

        $attributes.on('change', function () {
          self.processCombinations();
        });
      }

      this.loadCombinations();
    },

    loadCombinations: function () {
      var self = this;

      $.ajax({
        cache: false,
        url: self.settings.fetchUrl,
        type: 'GET',
        success: function (response) {
          self.params.availableCombinations = response;

          var selectedAttributes = self.getSelectedAttributes();
          if (selectedAttributes && selectedAttributes.length > 0) {
            // unselect selected values if they are preselected but not available
            var availableAttributes = $.grep(selectedAttributes, function (attribute) {
              return attribute.values.length > 0;
            });
            $.each(availableAttributes, function (i, attribute) {
              var combinations = self.getCombinationsByAttributeId(attribute.id, [attribute]);
              if (combinations && combinations.length > 0) {
                if (!self.isAvailableCombination(combinations, [attribute])) {
                  self.unselectValues(attribute.values);
                }
              } else {
                self.unselectValues(attribute.values);
              }
            });

          }

          self.processCombinations();
        },
        error: function () {
          self.params.availableCombinations = [];
        }
      });
    },

    processCombinations: function () {
      var availableAttributeIds = this.params.availableAttributeIds;
      if (availableAttributeIds.length === 0)
        return;

      var self = this;

      // disable all attribute values if combinations isn't available
      var availableCombinations = this.params.availableCombinations;
      if (availableCombinations.length === 0) {
        var valueIds = this.getAttributeValueIds();
        $.each(valueIds, function (i, valueId) {
          self.toggleAttributeValue(valueId, false);
        });

        return;
      }

      var selectedAttributes = self.getSelectedAttributes();

      $.each(availableAttributeIds, function (attrIndex, attributeId) {
        var attibuteValueIds = self.getAttributeValueIds(attributeId);
        $.each(attibuteValueIds, function (i, valueId) {
          // get only previous attributes (if any) excluding the selection by current attribute
          // and then check the combinations stock with current value
          var availableAttributes = $.grep(selectedAttributes, function (attribute, selectedAttrIndex) {
            return attrIndex > selectedAttrIndex && attribute.id !== attributeId && attribute.values.length > 0;
          });
          availableAttributes.push({ id: attributeId, values: [valueId] });

          // check if available attributes exists in combinations
          // otherwise just disable the value
          var combinations = self.getCombinationsByAttributeId(attributeId, availableAttributes);
          if (combinations && combinations.length > 0) {
            self.toggleAttributeValue(valueId, self.isAvailableCombination(combinations, availableAttributes));
          } else {
            self.toggleAttributeValue(valueId, false);
          }
        });
      });

      $(this).trigger({ type: "processed" });
    },

    getCombinationsByAttributeId: function (attributeId, processedAttributes) {
      var availableCombinations = this.params.availableCombinations;
      if (availableCombinations.length === 0) {
        return;
      };

      return $.grep(availableCombinations, function (combination) {
        var found = $.grep(combination.Attributes, function (attribute) {
          return attribute.Id === attributeId;
        })[0];

        if (processedAttributes && processedAttributes.length > 0) {
          $.each(processedAttributes, function (i, processedAttribute) {
            found = found && $.grep(combination.Attributes, function (attribute) {
              var attrbiuteIsFound = attribute.Id === processedAttribute.id;

              // exclude unselected attribute values
              if (processedAttribute.values.length > 0) {
                $.each(processedAttribute.values, function (i, id) {
                  attrbiuteIsFound = attrbiuteIsFound && $.inArray(id, attribute.ValueIds) !== -1
                });
              }

              return attrbiuteIsFound;
            })[0];
          });
        }

        return found;
      });
    },

    isAvailableCombination: function (combinations, attributes) {
      // check if any combination have stock with specified values
      // otherwise just disable the value 
      var existedCombinations = $.grep(combinations, function (combination) {
        var valueIdsByCombinations = $.map(combination.Attributes, function (attribute) {
          return attribute.ValueIds;
        });
        var existedAttributes = $.grep(attributes, function (attribute) {
          return $.grep(attribute.values, function (valueId) {
            return $.inArray(valueId, valueIdsByCombinations) !== -1;
          })
        });

        return combination.InStock && existedAttributes.length === attributes.length;
      });

      return existedCombinations.length > 0;
    },

    getAttributeValueIds: function (attributeId) {
      var $contentEl = $(this.settings.contentEl);
      var $scope = attributeId ? $('[data-attr=' + attributeId + ']', $contentEl) : $contentEl;
      var $valueItems = $('[data-attr-value]', $scope);
      if ($valueItems) {
        return $.map($valueItems, function (item) {
          return parseInt($(item).data('attr-value'));
        });
      }
    },

    toggleAttributeValue: function (valueId, enabled) {
      var $contentEl = $(this.settings.contentEl);
      var $value = $('[data-attr-value=' + valueId + ']', $contentEl);
      if (enabled) {
        $value.removeClass('disabled');
      } else {
        $value.addClass('disabled');
      }
    },

    unselectValues: function (valueIds) {
      var $contentEl = $(this.settings.contentEl);
      $.each(valueIds, function (i, valueId) {
        var $value = $('[data-attr-value=' + valueId + ']', $contentEl);
        $value.prop('selected', false);
        $('input', $value).prop('checked', false);
        // for image square
        $value.removeClass('selected-value');
      });
    },

    getSelectedAttributes: function () {
      var availableAttributeIds = this.params.availableAttributeIds;
      if (availableAttributeIds.length > 0) {
        var $contentEl = $(this.settings.contentEl);

        return $.map(availableAttributeIds, function (attributeId) {
          var selectedValues = [];

          var $attribute = $('[data-attr=' + attributeId + ']', $contentEl);
          var $attributeValues = $('[data-attr-value]', $attribute);
          if ($attributeValues && $attributeValues.length > 0) {
            $.each($attributeValues, function (i, value) {
              var $value = $(value);
              if ($value.is(':selected') || $('input', $value).is(':checked')) {
                var id = parseInt($value.data('attr-value'));
                selectedValues.push(id);
              }
            });
          }

          return {
            id: attributeId,
            values: selectedValues
          }
        });
      }
    }
  }
}