/**
 * @file mixins/stateful.js
 * @module stateful
 */
import {isEvented} de './evented' ;
import * as Obj from '../utils/obj' ;

/**
 * Contient des méthodes qui fournissent un état à un objet qui lui est transmis
 * à {@link module:stateful}.
 *
 * @mixin StatefulMixin
 */
const StatefulMixin = {

  /**
   * Un hachage contenant des clés et des valeurs arbitraires représentant l'état de
   * l'objet.
   *
   * @type {Objet}
   */
  état : {},

  /**
   * Définir l'état d'un objet en modifiant son
   * l'objet {@link module:stateful~StatefulMixin.state|state} est en place.
   *
   * @fires module:stateful~StatefulMixin#statechanged
   * @param {Objet|Fonction} stateUpdates
   *          Un nouvel ensemble de propriétés à fusionner superficiellement dans l'état du plugin.
   *          Il peut s'agir d'un objet ordinaire ou d'une fonction renvoyant un objet ordinaire.
   *
   * @return {Objet|non défini}
   *          Un objet contenant les changements survenus. S'il n'y a pas de changement
   *          s'est produite, renvoie `undefined`.
   */
  setState(stateUpdates) {

    // Prise en charge de la fourniture de l'état `stateUpdates` en tant que fonction.
    if (typeof stateUpdates === 'function') {
      stateUpdates = stateUpdates() ;
    }

    laisser changer ;

    Obj.each(stateUpdates, (value, key) => {

      // Enregistrer le changement si la valeur est différente de celle contenue dans le fichier
      // état actuel.
      if (this.state[key] !== value) {
        changes = changes || {} ;
        changes[key] = {
          de : this.state[key],
          à : valeur
        };
      }

      this.state[key] = value ;
    }) ;

    // Ne déclenche "statechange" que si des changements ont eu lieu ET que nous avons un déclencheur
    // fonction. Cela nous permet de ne pas exiger que l'objet cible soit un
    // objet événementiel.
    if (changes && isEvented(this)) {

      /**
       * Un événement déclenché sur un objet qui est à la fois
       * {@link module:stateful|stateful} et {@link module:evented|evented}
       * indiquant que son état a changé.
       *
       * @event module:stateful~StatefulMixin#statechanged
       * @type {Objet}
       * @property {Objet} changes
       *           Un hachage contenant les propriétés qui ont été modifiées et
       *           les valeurs modifiées "de" et "en".
       */
      this.trigger({
        changements,
        type : "statechanged" (changement d'état)
      }) ;
    }

    les changements de retour ;
  }
};

/**
 * Applique {@link module:stateful~StatefulMixin|StatefulMixin} à une cible
 * objet.
 *
 * Si l'objet cible est {@link module:evented|evented} et possède un élément
 * cette méthode sera automatiquement liée à la méthode `handleStateChanged`
 * l'événement `statechanged` sur lui-même.
 *
 * @param {Objet} target
 *          L'objet qui doit être transformé en état.
 *
 * @param {Object} [defaultState]
 *          Un ensemble de propriétés par défaut pour remplir les champs de l'objet nouvellement déclaré
 *          propriété `state`.
 *
 * @return {Object}
 *          Retourne la `cible`.
 */
function stateful(target, defaultState) {
  Obj.assign(target, StatefulMixin) ;

  // Cela se produit après le mixage car nous devons remplacer l'état (`state`)
  // ajouté à cette étape.
  target.state = Obj.assign({}, target.state, defaultState) ;

  // Lier automatiquement la méthode `handleStateChanged` de l'objet cible si elle existe.
  if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
    target.on('statechanged', target.handleStateChanged) ;
  }

  retourner la cible ;
}

export default stateful ;