'use strict';



var CLASS_NAME = 'RuleBase';



var Argument = require('../system/argument-check.js');

var ArgumentError = require('../system/argument-error.js');

var ConstructorError = require('../system/constructor-error.js');

var NotImplementedError = require('../system/not-implemented-error.js');



/**

 * @classdesc Serves as the base class for rules.

 * @description

 *      Creates a new rule object.

 *      The rule instances should be frozen.

 *

 * @memberof bo.rules

 * @constructor

 * @param {string} ruleName - The name of the rule.

 *

 * @throws {@link bo.system.ArgumentError Argument error}: The rule name must be a non-empty string.

 */

var RuleBase = function (ruleName) {



  ruleName = Argument.inConstructor(CLASS_NAME).check(ruleName).forMandatory('ruleName').asString();



  /**

   * The name of the rule type.

   * The default value usually the name of the constructor, without the Rule suffix.

   * @name bo.rules.RuleBase#ruleName

   * @type {string}

   * @readonly

   */

  Object.defineProperty(this, 'ruleName', {

    get: function () {

      return ruleName;

    },

    enumeration: true

  });



  /**

   * Human-readable description of the rule failure.

   * @type {string}

   * @readonly

   */

  this.message = null;

  /**

   * The priority of the rule. Higher number means higher priority.

   * @type {number}

   * @default

   * @readonly

   */

  this.priority = 10;

  /**

   * Indicates whether processing of the rules for a property stops when the rule fails.

   * @type {boolean}

   * @default

   * @readonly

   */

  this.stopsProcessing = false;

};



/**

 * Sets the properties of the rule.

 *

 * @function bo.rules.RuleBase#initialize

 * @param {string} message - Human-readable description of the rule failure.

 * @param {number} [priority=10] - The priority of the rule.

 * @param {boolean} [stopsProcessing=false] - Indicates the rule behavior in case of failure.

 *

 * @throws {@link bo.system.ArgumentError Argument error}: The message must be a non-empty string.

 * @throws {@link bo.system.ArgumentError Argument error}: The last 3 arguments can be:

 *    a string as the message, an integer as the priority and a Boolean as the stopsProcessing argument.

 */

RuleBase.prototype.initialize = function (message, priority, stopsProcessing) {



  // Remove null and undefined arguments.

  var args = Array.prototype.slice.call(arguments).filter(function(arg) {

    return arg !== null && arg !== undefined;

  });



  if (args.length) {

    for (var i = 0; i < args.length; i++) {

      switch (typeof args[i]) {

        case 'string':

          this.message = args[i];

          break;

        case 'number':

          this.priority = Math.round(args[i]);

          break;

        case 'boolean':

          this.stopsProcessing = args[i];

          break;

        default:

          throw new ConstructorError('rule', this.constructor.name);

      }

    }

  }

  Argument.inConstructor(this.constructor.name).check(this.message).forMandatory('message').asString();

};



/**

 * Abstract method to check if the rule is valid for the property.

 *

 * @abstract

 * @function bo.rules.RuleBase#execute

 * @param {Array.<*>} inputs - An array of the values of the required properties.

 *

 * @throws {@link bo.system.NotImplementedError Not implemented error}: The Rule.execute method is not implemented.

 */

RuleBase.prototype.execute = function (inputs) {

  throw new NotImplementedError('method', this.constructor.name, 'execute');

};



/**

 * Abstract method that returns the result of the rule checking.

 *

 * @abstract

 * @function bo.rules.RuleBase#result

 * @param {string} [message] - Human-readable description of the rule failure.

 * @param {bo.rules.RuleSeverity} [severity] - The severity of the rule failure.

 * @returns {object} An object that describes the result of the rule checking.

 *

 * @throws {@link bo.system.NotImplementedError Not implemented error}: The Rule.result method is not implemented.

 */

RuleBase.prototype.result = function (message, severity) {

  throw new NotImplementedError('method', this.constructor.name, 'result');

};



module.exports = RuleBase;