var OU = OU || {};

OU.buttons = (function() {
    var create = function(data) {
        if (Array.isArray(data)) {
            return createGroup(data);
        }
        else {
            return createSingle(data);
        }
    };

    /**
     * @param {object[]} data
     * @returns {Element|*}
     */
    var createGroup = function(data) {
        var obj = {
            group: OU.utils.createElement('div', 'btn-group')
        };

        data.forEach(function(data) {
            obj.group.appendChild(createSingle(data));
            obj[data.type] = obj.group.lastChild;
        });

        return obj;
    };

    /**
     * @param {object} data
     * @param {string} data.type
     * @param {string|object[]} [data.html]
     * @param {boolean} [data.hidden]
     * @param {boolean} [data.disabled]
     * @param {object[]} [data.classes]
     * @param {string} [data.name]
     * @param {string} [data.value]
     * @param {string} [data.label]
     * @param {boolean} [data.toggle]
     * @param {function} [data.click]
     * @param {boolean} [construct]
     * @returns {Element|*}
     */
    var createSingle = function(data, construct) {
        var btn = document.createElement('button');
        var classes = [
            'btn',
            'btn-' + data.type.replace(/_/g, '-').toLowerCase()
        ];

        if (data.html) {
            btn.insertAdjacentHTML('beforeend', Array.isArray(data.html) ? data.html[0] : data.html);
        }
        if (data.name) {
            btn.name = data.name;
        }
        if (data.value) {
            btn.value = data.value;
        }
        if (Array.isArray(data.classes)) {
            classes = classes.concat(data.classes);
        }
        if (data.hidden) {
            btn.style.display = 'none';
        }
        if (data.disabled) {
            btn.disabled = true;
        }
        if (data.label) {
            btn.setAttribute('aria-label', data.label);
        }
        if (data.toggle) {
            btn.setAttribute('aria-pressed', 'false');
        }
        if (!(!!construct)) {
            if (data.click) {
                btn.addEventListener('click', data.click);
            }
            else {
                btn.addEventListener('click', OU.utils.triggerCustomEvent.bind(null, btn, data.type));
            }
        }

        btn.setAttribute('type', 'button');
        btn.className = classes.join(' ');

        return btn;
    };

    /**
     * @param {object} data
     * @returns {ButtonGroup|Button}
     */
    var construct = function(data) {
        if (!data.type) {
            return new ButtonGroup(data);
        }
        else {
            return new Button(data);
        }
    };

    /**
     * @param {object} cfg
     * @param {object} [cfg.data] - extendable/overwritable object (can't be an array as unique keys needed)
     * @param {object[]} [cfg.enabled] - list of buttons to enable
     * @constructor
     */
    function ButtonGroup(cfg) {
        ButtonGroup.counter = ++ButtonGroup.counter || 0;

        this.id = 'btn-group' + ButtonGroup.counter;
        this.msg = null;
        this.data = {
            check: {
                html: 'Check your answer'
            },
            reveal: {
                html: ['Reveal answer', 'Hide answer']
            },
            save: {
                html: 'Save'
            },
            reset: {
                html: 'Reset'
            },
            retry: {
                html: 'Try again'
            }
        };
        this.enabled = ['reset'];
        this.buttons = {};

        this.init(cfg);
    }

    ButtonGroup.prototype.init = function(cfg) {
        OU.utils.extendDeep(this, cfg);

        this.group = document.createElement('div');
        this.group.className = 'btn-group';

        if (this.msg) {
            this.msg = document.createElement('div');
            this.msg.className = 'btn-msg';
        }

        if (this.enabled == '*') {
            this.enabled = Object.keys(this.data);
        }

        this.enabled.forEach(function(type) {
            if (this.data[type]) {
                this.data[type].type = type;
                this.data[type].group = this.id;
                this.buttons[type] = OU.buttons.construct(this.data[type]);
                this.group.appendChild(this.buttons[type].el);
            }
        }.bind(this));

        this.setControllers();
    };

    ButtonGroup.prototype.active = function(type) {
        return !!this.buttons[type];
    };

    ButtonGroup.prototype.get = function(type) {
        return this.active(type) ? this.buttons[type] : false;
    };

    ButtonGroup.prototype.check = function() {
        if (this.buttons.retry) {
            this.disable('check');
        }
    };

    ButtonGroup.prototype.reveal = function() {
        if (this.buttons.reveal.toggleable) {
            if (this.buttons.reveal.pressed) {
                this.disable('check');
            }
            else {
                this.enable('check');
            }
        }
        else {
            this.disable('check');
            this.disable('reveal');
        }
    };

    ButtonGroup.prototype.reset = function() {
        this.enable();

        if (this.buttons.reveal && this.buttons.reveal.pressed) {
            this.buttons.reveal.toggle();
        }
    };

    ButtonGroup.prototype.retry = function() {
        this.enable();
    };

    ButtonGroup.prototype.iterate = function(callback) {
        for (var k in this.buttons) {
            if (this.buttons.hasOwnProperty(k)) {
                callback.call(this, k);
            }
        }
    };

    ButtonGroup.prototype.enable = function(type) {
        if (type) {
            if (this.buttons[type]) {
                this.buttons[type].enable();
            }
        }
        else {
            this.iterate(function(k) {
                this.buttons[k].enable();
            });
        }
    };

    ButtonGroup.prototype.disable = function(type) {
        if (type) {
            if (this.buttons[type]) {
                this.buttons[type].disable();
            }
        } else {
            this.iterate(function(k) {
                this.buttons[k].disable();
            });
        }
    };

    ButtonGroup.prototype.setControllers = function() {
        this.iterate(function(type) {
            // TODO is it worth handling events internally via subscribe/publish?
            document.body.addEventListener(type, this);
        });
    };

    ButtonGroup.prototype.handleEvent = function (e) {
        if (typeof e.detail === 'undefined') return;

        if (e.detail.group != this.id) {
            return;
        }

        if (typeof this[e.type] == 'function') {
            this[e.type]();
        }
    };

    function Button(data) {
        this.type = '';
        this.group = '';
        this.el = null;
        this.html = '';
        this.hidden = false;
        this.disabled = false;
        this.classes = [];
        this.name = '';
        this.value = '';
        this.label = '';
        this.toggleable = false;
        this.pressed = null;
        this.click = null;

        OU.utils.setCfg(this, data);

        if (!data.type) {
            throw new Error('No button type specified');
        }

        if (Array.isArray(this.html)) {
            this.toggleable = true;
            this.pressed = false;
        }

        this.el = createSingle(data, true);
        this.el.addEventListener('click', this);
    }

    Button.prototype.enable = function() {
        this.disabled = false;
        this.el.removeAttribute('disabled');
    };

    Button.prototype.disable = function() {
        this.disabled = true;
        this.el.disabled = 'true';
    };

    Button.prototype.toggle = function() {
        if (this.el.textContent == this.html[0]) {
            this.pressed = true;
            this.el.textContent = this.html[1];
            this.el.setAttribute('aria-pressed', 'true');
        }
        else {
            this.pressed = false;
            this.el.textContent = this.html[0];
            this.el.setAttribute('aria-pressed', 'false');
        }
    };

    Button.prototype.handleEvent = function(e) {
        switch(e.type) {
            case 'click':
                if (this.toggleable) {
                    this.toggle();
                }

                if (typeof this.click == 'function') {
                    this.click(this, this.group);
                }
                else {
                    OU.utils.triggerCustomEvent(e.target, this.type, {
                        group: this.group,
                        pressed: this.pressed
                    });
                }
        }
    };

    return {
        create: create,
        construct: construct
    };
})();