Source: lib/getConstructor/index.js

var events = require('events');

var Err = process.ROOT_REQUIRE('./lib/Err');


var f_methods = process.ROOT_REQUIRE('./lib/methods'),
    f_instance_modules = process.ROOT_REQUIRE('./lib/instance_modules');

/**
 * @module getConstructor
 * @param {object} con_o - Constructor options
 */
module.exports = function getConstructor(con_o) {

  // Sanitize constructor options
  if (!con_o || typeof con_o !== 'object') {
    return Err('f_.getConstructor requires an options object');
  }
  if (!con_o.function_flow || !(con_o.function_flow instanceof Array)) {
    return Err('f_.getConstructor requires an options object with an ' + 
      'function_flow array in it');
  }

  /** These properties get set to every f_ instance */
  var f_instance_properties = {
    flow_i: 0
  };

  // @TODO desc for con_o defaults
  con_o.custom_prototype = con_o.custom_prototype || {};

  /**
   * The constructor returned when getConstructor is called.
   * @TODO Module this, split split!
   * @constructor
   * @param {object} ins_o - Instance options
   * @example
   * new f_Constructor({ a: 'b' }) // { a: 'b' }
   */
  function f_Constructor(ins_o) {
    var self = this;

    // Sanitize instance options
    if (!ins_o || typeof ins_o !== 'object') {
      ins_o = {};
    }
    if (!ins_o.custom_data || typeof ins_o.custom_data !== 'object') {
      ins_o.custom_data = {};
    }

    // Apply instance modules and properties
    for (var module_name in f_instance_modules) {
      /* istanbul ignore else  */
      if (f_instance_modules.hasOwnProperty(module_name)) {
        self['f_' + module_name] = f_instance_modules[module_name];
      }
    }

    // Apply instance options
    // Create data namespace, store name as f_data_namespace
    var data_namespace = con_o.data_namespace ||
      ins_o.data_namespace || 'data';
    self[data_namespace] = ins_o.custom_data;
    self.f_data_namespace = data_namespace;

    /** @TODO write desc for f_function_flow */
    self.f_function_flow = [];

    for (var property_name in f_instance_properties) {
      /* istanbul ignore else  */
      if (f_instance_properties.hasOwnProperty(property_name)) {
        self['f_' + property_name] = f_instance_properties[property_name];
      }
    }

    // Create instance function_flow, this in order to track tries
    con_o.function_flow.forEach(function (flow) {
      var new_flow_object = { tries: 0 };
      for (var property in flow) {
        /* istanbul ignore else  */
        if (flow.hasOwnProperty(property)) {
          if (property !== 'function') {
            new_flow_object[property] = flow[property];
          }
        }
      }
      self.f_function_flow.push(new_flow_object);
    });

    // Finaly, call user specified initializer and pass the instance options
    // custom data as the first argument
    if (con_o.initializer) {
      con_o.initializer.apply(self, [ins_o.custom_data]);
    }
  }

  // f_Constructor prototype properties

  // Inherit node events
  f_Constructor.prototype = Object.create(events.EventEmitter.prototype);

  // Set custom prototype properties
  for (var proto_key in con_o.custom_prototype) {
    /* istanbul ignore else  */
    if (con_o.custom_prototype.hasOwnProperty(proto_key)) {
      f_Constructor.prototype[proto_key] = con_o.custom_prototype[proto_key];
    }
  }

  // Set f_ methods to the constructor prototype, prefixed with f_
  for (var method_name in f_methods) {
    /* istanbul ignore else  */
    if (f_methods.hasOwnProperty(method_name)) {
      f_Constructor.prototype['f_' + method_name] = f_methods[method_name];
    }
  }

  // Set function_flow methods to the constructor prototype
  con_o.function_flow.forEach(function (flow) {
    f_Constructor.prototype[flow.name] = flow.function;
  });

  return f_Constructor;
};