/**
 * Миксин для упрощения подписки на сторы
 */

/**
 * Вызывается при обновлении стора, метод по-умолчанию
 * @private
 * @method  storeDidUpdate
 */
function storeDidUpdate() {
    this.setState(this.getStoreState());
}

/**
 * Возвращает данные из сторов, метод по-умолчанию
 * @private
 * @method  getStoreState
 * @returns {Object}
 */
function getStoreState() {
    return null;
}

const StoreMixin = {

    getInitialState() {
        this.getStoreState = this.getStoreState || getStoreState.bind(this);
        this.storeDidUpdate = this.storeDidUpdate || storeDidUpdate.bind(this);

        return this.getStoreState();
    },

    componentWillMount() {
        this._callbacks = [];
        this._stores = [];

        if (this._componentWillMount) {
            this._componentWillMount();
        }
    },

    componentWillUnmount() {
        this._callbacks.forEach(callback => {
            callback();
        });

        this._callbacks = [];
    },

    /**
     * Подписывает компонент на изменение сторов
     * @method  subscribe
     * @param   {Object|Array}  stores
     * @param   {function}  [handler]
     */
    subscribe(stores, handler) {
        if (!stores) {
            throw new Error(`${this.constructor.displayName}.subscribe() requires an array of stores`);
        }

        if (!Array.isArray(stores)) {
            stores = [stores];
        }

        const callback = handler || this.storeDidUpdate;

        stores.forEach(store => {
            const token = store.addListener(callback);

            this._callbacks.push(() => {
                if (callback.cancel) {
                    callback.cancel();
                }

                token.remove();
            });
        });

        this._stores = this._stores.concat(stores);
    },

    hashCode(...stores) {
        if (!stores.length && this._stores) {
            stores = this._stores;
        }

        const hashCodes = stores.map(store => {
            // для экземпляров ReduceStore с Immutable-состоянием
            if (store._state && store._state.hashCode) {
                return store._state.hashCode();
            }
        });

        return hashCodes.filter(Boolean).join('/');
    },
};

export default StoreMixin;
