/**
 * @author DJ Perez <danieljordi@bab-soft.com>
 * @version 1.0
 *
 * Plugin for manage symfony form collections
 * Dependencies:
 * - jQuery <jquery.com>
 * - js-signals <millermedeiros.github.io/js-signals>
 *
 * Options:
 * - selector -> Selector of collection container
 * - addElement -> Selector of add element
 * - deleteElement -> button of entry for delete element
 * - method -> append/prepend
 * - context -> the context for find selectors
 * - dataSelector -> Selector for get the template code
 * - prototypeFieldName -> The value of index replacement
 * - SimpleFormCollection.ADD -> Callback for add element, is fired when click in button
 * - SimpleFormCollection.REMOVE -> Callback for remove element, is fired when click in button
 * - SimpleFormCollection.AFTER_ADD -> Callback after add element
 * - SimpleFormCollection.AFTER_REMOVE -> Callback after remove element
 *
 * Note: All after callbacks have collection object as this and element as only argument.
 *
 * Inizialitation example:
 *
 * var options = {
 *    'addElementSelector' : '[data-add-group]'
 * }
 *
 * $('[data-collection="content-group"]').SimpleFormCollection(options);
 *
 * Attention:
 * - _simpleFormCollectionObject -> Reserver var for callbacks, using this as other scope variable when define callback may cause issues.
 *
 */

var SimpleFormCollection = (function() {
    'use strict';

    /**
     * @param options
     * @constructor
     */
    function SimpleFormCollection(options) {

        this.lastIndex = 0;
        this.collectionContainer = null;
        this.addElement = null;
        this.deleteElement = null;
        this.dataSelector = null;
        this.context = $('body');

        this.template = null;
        this.method = null;
        this.prototypeFieldName = null;

        this.collection = [];
        this.handlers = [];

        this.addCallback = null;
        this.removeCallback = null;

        this._init(options);
    }

    /**
     *
     * @param options
     * @private
     */
    SimpleFormCollection.prototype._init = function (options) {
        this._initTypes()
            ._initHandlers()
            ._resolveOptions(options)
            ._initCollectionEntryTemplate()
            ._bindDOMEvents()
            ._initCollection();
    };

    /**
     *
     * @param options
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._resolveOptions = function (options) {
        var _simpleFormCollectionObject = this;
        var defaultOptions = {
            dataSelector: 'prototype',
            method: 'append',
            deleteElement: '[data-delete]',
            prototypeFieldName: '__name__',
            addCallback: function() {
                _simpleFormCollectionObject.add();
            },
            removeCallback: function(element) {
                _simpleFormCollectionObject.removeElement(element);
            }
        };

        options = $.extend(defaultOptions, options);

        $.map(options, function(value, index) {
            if(index === _simpleFormCollectionObject.AFTER_REMOVE || index === _simpleFormCollectionObject.AFTER_ADD) {
                _simpleFormCollectionObject.addHandler(index, value, false);
            } else if(typeof _simpleFormCollectionObject[index] !== 'undefined' ) {
                _simpleFormCollectionObject[index] = value;
            }
            return null;
        });

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._initHandlers = function () {
        var afterAdd = this.AFTER_ADD,
            afterRemove = this.AFTER_REMOVE;

        this.handlers = {
            afterAdd: new signals.Signal(),
            afterRemove: new signals.Signal()
        };

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._initTypes = function () {
        this.ADD = SimpleFormCollection.ADD;
        this.REMOVE = SimpleFormCollection.REMOVE;
        this.AFTER_ADD = SimpleFormCollection.AFTER_ADD;
        this.AFTER_REMOVE = SimpleFormCollection.AFTER_REMOVE;

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._initCollectionEntryTemplate = function () {

        this.template = this.collectionContainer.data(this.dataSelector);
        this.collectionContainer.removeAttr('data-'+this.dataSelector);

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._initCollection = function () {
        var _simpleFormCollectionObject = this;
        _simpleFormCollectionObject.collectionContainer.children().each(function() {
            _simpleFormCollectionObject.collection.push(this);
            _simpleFormCollectionObject.lastIndex++;
            var domElement = this;

            _simpleFormCollectionObject.bindDomEvents(domElement);
            _simpleFormCollectionObject.dispatchHandler(_simpleFormCollectionObject.AFTER_ADD, domElement);
        });

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     * @private
     */
    SimpleFormCollection.prototype._bindDOMEvents = function() {
        var _simpleFormCollectionObject = this;

        $(_simpleFormCollectionObject.addElement, _simpleFormCollectionObject.context).on('click', function(event) {
            event.preventDefault();
            if(typeof _simpleFormCollectionObject.addCallback !== null && typeof _simpleFormCollectionObject.addCallback !== 'undefined') {
                _simpleFormCollectionObject.addCallback();
            } else {
                _simpleFormCollectionObject.add();
            }

        });

        return this;
    };

    /**
     *
     * @returns {SimpleFormCollection}
     */
    SimpleFormCollection.prototype.add = function () {

        var newElement = this.createDomElement();

        if(this.method === 'append') {
            this.collectionContainer.append(newElement);
            this.collection.push(newElement);
        } else if(this.method === 'prepend') {
            this.collectionContainer.prepend(newElement);
            this.collection.unshift(newElement);
        }

        this.lastIndex++;
        this.bindDomEvents(newElement);
        this.dispatchHandler(this.AFTER_ADD, newElement);

        return this;
    };

    /**
     *
     * @returns {jQuery|HTMLElement}
     */
    SimpleFormCollection.prototype.createDomElement = function() {
        var html = this.template.replace(new RegExp(this.prototypeFieldName, 'g'), this.lastIndex);

        return $(html).filter('*').get(0);
    };

    /**
     *
     * @param {jQuery|HTMLElement} element
     * @returns {SimpleFormCollection}
     */
    SimpleFormCollection.prototype.removeElement = function (element) {

        if (this.existsElement(element)) {
            var index = this.collection.indexOf(element);

            this.collection.splice(index, 1);
            element.remove();

            this.dispatchHandler(this.AFTER_REMOVE, element);
        }

        return this;
    };

    /**
     *
     * @param {jQuery|HTMLElement} element
     * @returns {boolean}
     */
    SimpleFormCollection.prototype.existsElement = function (element) {
        return this.collection.indexOf(element) >= 0;
    };

    /**
     *
     * @param {string} type
     * @param {function} callback
     * @param {boolean} callOnce
     * @returns {SimpleFormCollection}
     */
    SimpleFormCollection.prototype.addHandler = function (type, callback, callOnce) {
        if (typeof this.handlers[type] !== 'undefined') {
            if (callOnce) {
                this.handlers[type].addOnce(callback, this);
            } else {
                this.handlers[type].add(callback, this);
            }
        }

        return this;
    };

    /**
     *
     * @param {integer} index
     * @returns {null|jQuery|HTMLElement}
     */
    SimpleFormCollection.prototype.get = function(index) {
        if(typeof this.collection[index] === 'undefined') {
            return null;
        }

        return this.collection[index];
    };

    /**
     *
     * @param {integer} index
     * @returns {jQuery|HTMLElement}
     */
    SimpleFormCollection.prototype.getByDom = function(index) {
        return this.collectionContainer.children().get(index);
    };

    SimpleFormCollection.prototype.dispatchHandler = function (type, params) {
        if (typeof this.handlers[type] !== 'undefined') {
            params = typeof params === 'undefined' ? {} : params;
            this.handlers[type].dispatch(params);
        }

        return this;
    };

    /**
     *
     * @param {jQuery|HTMLElement} element
     */
    SimpleFormCollection.prototype.bindDomEvents = function(element) {
        var _simpleFormCollectionObject = this;
        $(_simpleFormCollectionObject.deleteElement, element).on('click', function(event) {
            event.preventDefault();
            event.stopPropagation();
            if(typeof _simpleFormCollectionObject.removeCallback !== null && typeof _simpleFormCollectionObject.removeCallback !== 'undefined') {
                _simpleFormCollectionObject.removeCallback(element);
            } else {
                _simpleFormCollectionObject.removeElement(element);
            }

        });
    };

    /**
     *
     * @type {string}
     */
    SimpleFormCollection.AFTER_ADD = 'afterAdd';
    /**
     *
     * @type {string}
     */
    SimpleFormCollection.AFTER_REMOVE = 'afterRemove';
    /**
     *
     * @type {string}
     */
    SimpleFormCollection.REMOVE = 'removeCallback';
    /**
     *
     * @type {string}
     */
    SimpleFormCollection.ADD = 'addCallback';

    return SimpleFormCollection;
}());

$(function() {
    $.fn.SimpleFormCollection = function(options) {

        $(this).each(function() {
            options['collectionContainer'] = $(this);

            $(this).data('simple-collection', new SimpleFormCollection(options));
        });

        return this;
    }
});

