'use strict';
var CLASS_NAME = 'BrokenRuleList';
var Argument = require('../system/argument-check.js');
var PropertyInfo = require('../shared/property-info.js');
var BrokenRule = require('./broken-rule.js');
var BrokenRulesOutput = require('./broken-rules-output.js');
var RuleNotice = require('./rule-notice.js');
var RuleSeverity = require('./rule-severity.js');
/**
 * @classdesc Represents the lists of broken rules.
 * @description Creates a new broken rule list instance.
 *
 * @memberof bo.rules
 * @constructor
 * @param {string} modelName - The name of the model.
 *
 * @throws {@link bo.system.ArgumentError Argument error}: The model name must be a non-empty string.
 */
var BrokenRuleList = function (modelName) {
  modelName = Argument.inConstructor(CLASS_NAME).check(modelName).forMandatory('modelName').asString();
  var items = {};
  var length = 0;
  /**
   * Adds a broken rule to the list.
   *
   * @param {bo.rules.BrokenRule} brokenRule - A broken rule to add.
   *
   * @throws {@link bo.system.ArgumentError Argument error}: The rule must be a BrokenRule object.
   */
  this.add = function (brokenRule) {
    brokenRule = Argument.inMethod(CLASS_NAME, 'add')
        .check(brokenRule).forMandatory('brokenRule').asType(BrokenRule);
    if (items[brokenRule.propertyName])
      items[brokenRule.propertyName].push(brokenRule);
    else {
      items[brokenRule.propertyName] = new Array(brokenRule);
      length++;
    }
  };
  /**
   * Removes the broken rules of a property except of the retained ones.
   *
   * @param {string} propertyName - The name of the property that broken rules are deleted of.
   */
  function clearFor (propertyName) {
    if (items[propertyName]) {
      var preserved = items[propertyName].filter(function (item) {
        return item.isPreserved;
      });
      if (preserved.length)
        items[propertyName] = preserved;
      else {
        delete items[propertyName];
        length--;
      }
    }
  }
  /**
   * Removes the broken rules of a property except of the retained ones.
   * If property is omitted, all broken rules are removed
   * except of the retained ones.
   *
   * @param {bo.rules.PropertyInfo} [property] - A property definition.
   */
  this.clear = function (property) {
    if (property instanceof PropertyInfo)
      clearFor(property.name);
    else
      for (var propertyName in items) {
        if (items.hasOwnProperty(propertyName))
          clearFor(propertyName);
      }
  };
  /**
   * Removes the broken rules of a property, including retained ones.
   * If property is omitted, all broken rules are removed.
   *
   * @param property
   */
  this.clearAll = function (property) {
    if (property instanceof PropertyInfo) {
      delete items[property.name];
      length--;
    } else {
      items = {};
      length = 0;
    }
  };
  /**
   * Determines if the model is valid. The model is valid when it has no
   * broken rule with error severity.
   *
   * @returns {boolean} - True if the model is valid, otherwise false.
   */
  this.isValid = function () {
    for (var propertyName in items) {
      if (items.hasOwnProperty(propertyName)) {
        if (items[propertyName].some(function (item) {
              return item.severity === RuleSeverity.error;
            }))
          return false;
      }
    }
    return true;
  };
  /**
   * Transforms the broken rules into a format that can be sent to the client.
   *
   * @param {string} [namespace] - The namespace of the message keys when messages are localizable.
   * @returns {bo.rules.BrokenRulesOutput} The response object to send.
   *
   * @throws {@link bo.system.ArgumentError Argument error}: The namespace must be a string.
   */
  this.output = function (namespace) {
    namespace = Argument.inMethod(CLASS_NAME, 'output')
        .check(namespace).forOptional('namespace').asString();
    var data = new BrokenRulesOutput();
    if (length) {
      var ns = namespace ? namespace + ':' : '';
      for (var property in items) {
        if (items.hasOwnProperty(property)) {
          items[property].forEach(function(brokenRule) {
            var propertyName = modelName + '.' + brokenRule.propertyName;
            var message = brokenRule.message || ns + propertyName + '.' + brokenRule.ruleName;
            var notice = new RuleNotice(message, brokenRule.severity);
            data.add(propertyName, notice);
          });
        }
      }
    }
    return data;
  };
};
module.exports = BrokenRuleList;