/*
 *
 * Реализация шаблона "издатель-подписчик"
 *
 */

function Уведомитель()
{
    function Подписка(id, отклик, уведомитель)
    {
        this.id = id;
        this.отклик = отклик;
        this.уведомитель = уведомитель;
    };

    this.уведомить = function()
    {
        // Попутно собираем подписки без отклика.
        var безотклика = [];
        for (var номер in this.подписки)
        {
            var подписка = this.подписки[номер];
            if (подписка.отклик)
            {
                подписка.отклик();
            }
            else
            {
                безотклика.push(подписка);
            }
        }

        // И удаляем их.
        if (безотклика.length)
        {
            this._удалитьПодпискиБезОтклика(безотклика);
        }
    };

    this.подписать = function(отклик)
    {
        var id = this._сгенерироватьUUID();
        var подписка = new Подписка(id, отклик, this);
        this.подписки.push(подписка);
        return подписка;
    };

    this.отписать = function(подписка)
    {
        подписка.отклик = null;
    };

    this._удалитьПодпискиБезОтклика = function(удалить)
    {
        var подписка = удалить.shift()
        while (подписка)
        {
            var индекс = this.подписки.indexOf(подписка);
            if (индекс !== -1)
            {
                this.подписки.splice(индекс, 1);
            }
            var подписка = удалить.shift()
        }
    };

    this._сгенерироватьUUID = function()
    {
        // https://stackoverflow.com/a/2117523
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
            /[xy]/g,
            function(c)
            {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            }
        );
    };

    // Конструктор.
    this.подписки = [];
}

/*
 *
 * Связь событий и реакций в виде последовательности (череды)
 *
 */

function Мир()
{
    // Разобрать события и реакции, выраженные в тексте.
    this.разобрать = function(череда)
    {
        var соответствия = this._событияРеакции(череда);
        for (var событие in соответствия)
        {
            if (!(событие in this.события))
            {
                this.события[событие] = new Уведомитель();
            }
            var реакции = соответствия[событие];
            for (var номер in реакции)
            {
                const реакция = реакции[номер];
                const название = this._имяФункцииИзРеакции(реакция);
                const функция = eval(название);
                var тут = this;
                this.события[событие].подписать(function(){
                    функция(тут);
                });
            }
        }
    };
    
    // Уведомить о событии при его наличии.
    this.уведомить = function(событие)
    {
        if (событие in this.события)
        {
            this.события[событие].уведомить();
        }
    };

    // Разобрать текст с событиями и реакциями, вернуть словарь их соответствия.
    this._событияРеакции = function(текст)
    {
        var соответствие = {};
        
        var элементы = текст.split("\n");
        var имяСобытия = null;
        for (var номер in элементы)
        {
            var элемент = элементы[номер];
            // Пропускаем комментарии.
            if (элемент.charAt(0) == "#")
            {
                continue;
            }
            var имя = элемент.trim();
            // Пропускаем пустые строки.
            if (!имя.length)
            {
                continue;
            }
            // Событие.
            if (имя == элемент)
            {
                if (!(имя in соответствие))
                {
                    имяСобытия = имя;
                    соответствие[имя] = [];
                }
            }
            // Реакция.
            else
            {
                соответствие[имяСобытия].push(имя);
            }
        }
        
        return соответствие;
    };

    // Преобразовать имя реакции в название функции.
    this._имяФункцииИзРеакции = function(реакция)
    {
        var имя = "";
        
        var части = реакция.split(" ");
        for (var номер in части)
        {
            var часть = части[номер];
            имя += часть.charAt(0).toUpperCase() + часть.slice(1);
        }
        
        return имя;
    };

    // Конструктор.
    this.события = {};
}

/*
 *
 * Создание глобального мира.
 *
 */

мир = new Мир();