import {createUUID} from '../b3.functions';
import {RUNNING} from '../constants';

/**
 * The BaseNode class is used as super class to all nodes in BehaviorJS. It
 * comprises all common variables and methods that a node must have to
 * execute.
 *
 * **IMPORTANT:** Do not inherit from this class, use `Composite`,
 * `Decorator`, `Action` or `Condition`, instead.
 *
 * The attributes are specially designed to serialization of the node in a
 * JSON format. In special, the `parameters` attribute can be set into the
 * visual editor (thus, in the JSON file), and it will be used as parameter
 * on the node initialization at `BehaviorTree.load`.
 *
 * BaseNode also provide 5 callback methods, which the node implementations
 * can override. They are `enter`, `open`, `tick`, `close` and `exit`. See
 * their documentation to know more. These callbacks are called inside the
 * `_execute` method, which is called in the tree traversal.
 *
 * @module b3
 * @class BaseNode
 **/

export default class BaseNode {

  /**
   * Initialization method.
   * @method initialize
   * @constructor
   **/
  constructor({category, name, title, description, properties} = {}) {

    this.id = createUUID();

    /**
     * Node category. Must be `COMPOSITE`, `DECORATOR`, `ACTION` or
     * `CONDITION`. This is defined automatically be inheriting the
     * correspondent class.
     * 
     * @member BaseNode#category
     **/
    this.category = category || '';

    /**
     * Node name. Must be a unique identifier,
     * preferable the same name of the
     * class. You have to set the node name in the prototype.
     * 
     * @member BaseNode#name
     **/
    this.name = name || '';

    /**
     * Node title.
     * 
     * @optional
     * @member BaseNode#title
     **/
    this.title = title || this.name;

    /**
     * Node description.
     * 
     * @member BaseNode#description
     */
    this.description = description || '';

    /**
     * A dictionary (key, value) describing the node properties. Useful for
     * defining custom variables inside the visual editor.
     *
     * @property properties
     * @type {Object}
     * @readonly
     **/
    this.properties = properties || {};

    /**
     * A dictionary (key, value) describing the node parameters. Useful for
     * defining parameter values in the visual editor. Note: this is only
     * useful for nodes when loading trees from JSON files.
     *
     * **Deprecated since 0.2.0. This is too similar to the properties
     * attribute, thus, this attribute is deprecated in favor to
     * `properties`.**
     *
     * @property {Object} parameters
     * @deprecated since 0.2.0.
     * @readonly
     **/
    this.parameters = {};
  }

  /**
   * This is the main method to propagate the tick signal to this node. This
   * method calls all callbacks: `enter`, `open`, `tick`, `close`, and
   * `exit`. It only opens a node if it is not already open. In the same
   * way, this method only close a node if the node  returned a status
   * different of `RUNNING`.
   *
   * @method _execute
   * @param {Tick} tick A tick instance.
   * @return {Constant} The tick state.
   * @protected
   **/
  _execute(tick) {
    // ENTER
    this._enter(tick);

    // OPEN
    if (!tick.blackboard.get('isOpen', tick.tree.id, this.id)) {
      this._open(tick);
    }

    // TICK
    var status = this._tick(tick);

    // CLOSE
    if (status !== RUNNING) {
      this._close(tick);
    }

    // EXIT
    this._exit(tick);

    return status;
  }

  /**
   * Wrapper for enter method.
   * @method _enter
   * @param {Tick} tick A tick instance.
   * @protected
   **/
  _enter(tick) {
    tick._enterNode(this);
    this.enter(tick);
  }

  /**
   * Wrapper for open method.
   * @method _open
   * @param {Tick} tick A tick instance.
   * @protected
   **/
  _open(tick) {
    tick._openNode(this);
    tick.blackboard.set('isOpen', true, tick.tree.id, this.id);
    this.open(tick);
  }

  /**
   * Wrapper for tick method.
   * @method _tick
   * @param {Tick} tick A tick instance.
   * @return {Constant} A state constant.
   * @protected
   **/
  _tick(tick) {
    tick._tickNode(this);
    return this.tick(tick);
  }

  /**
   * Wrapper for close method.
   * @method _close
   * @param {Tick} tick A tick instance.
   * @protected
   **/
  _close(tick) {
    tick._closeNode(this);
    tick.blackboard.set('isOpen', false, tick.tree.id, this.id);
    this.close(tick);
  }

  /**
   * Wrapper for exit method.
   * @method _exit
   * @param {Tick} tick A tick instance.
   * @protected
   **/
  _exit(tick) {
    tick._exitNode(this);
    this.exit(tick);
  }

  /**
   * Enter method, override this to use. It is called every time a node is
   * asked to execute, before the tick itself.
   *
   * @method enter
   * @param {Tick} tick A tick instance.
   **/
  enter(tick) {}

  /**
   * Open method, override this to use. It is called only before the tick
   * callback and only if the not isn't closed.
   *
   * Note: a node will be closed if it returned `RUNNING` in the tick.
   *
   * @method open
   * @param {Tick} tick A tick instance.
   **/
  open(tick) {}

  /**
   * Tick method, override this to use. This method must contain the real
   * execution of node (perform a task, call children, etc.). It is called
   * every time a node is asked to execute.
   *
   * @method tick
   * @param {Tick} tick A tick instance.
   **/
  tick(tick) {}

  /**
   * Close method, override this to use. This method is called after the tick
   * callback, and only if the tick return a state different from
   * `RUNNING`.
   *
   * @method close
   * @param {Tick} tick A tick instance.
   **/
  close(tick) {}

  /**
   * Exit method, override this to use. Called every time in the end of the
   * execution.
   *
   * @method exit
   * @param {Tick} tick A tick instance.
   **/
  exit(tick) {}
};
