/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; under version 2
 * of the License (non-upgradable).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Copyright (c) 2016 (original work) Open Assessment Technologies SA;
 *
 */
define([
    'jquery',
    'lodash',
    'core/promise',
    'core/logger',
    'ui/tooltipster',
    'taoQtiItem/portableElementRegistry/icRegistry',
    'taoQtiItem/qtiCreator/helper/creatorRenderer',
    'taoQtiItem/qtiCreator/model/helper/container',
    'taoQtiItem/qtiCreator/editor/gridEditor/content',
    'tpl!qtiItemPic/picManager/tpl/manager',
    'css!qtiItemPicCss/pic-manager'
], function (
    $,
    _,
    Promise,
    loggerFactory,
    tooltip,
    icRegistry,
    creatorRenderer,
    containerHelper,
    contentHelper,
    managerTpl
) {
    'use strict';

    var _studentToolTag = 'student-tool';
    var _studentToolbarId = 'studentToolbar';
    var logger = loggerFactory('picManager');

    /**
     * Toggle the disabled state of checkboxes
     *
     * @param $checkBoxes
     * @param state
     */
    function toggleCheckboxState($checkBoxes, state) {
        $checkBoxes.each(function () {
            // @todo this is tmp code that needs to go as soon all tools are available
            // see also further down above check event for another portion of the code
            if (this.className.indexOf('not-available') > -1) {
                return true;
            }
            // end tmp code

            this.disabled = state;
        });
    }

    /**
     * Create a dummy place holder
     *
     * @param typeIdentifier
     * @returns {*|HTMLElement}
     */
    function getNewInfoControlNode(typeIdentifier) {
        return $('<span/>')
            .addClass('widget-box sts-tmp-element sts-placeholder-' + typeIdentifier)
            .attr('data-new', true)
            .attr('data-qti-class', 'infoControl.' + typeIdentifier);
    }

    /**
     *
     * @param $container
     * @param $itemPanel
     * @param itemUri
     */
    function initStudentToolManager($container, $itemPanel, item) {
        var $placeholder;
        //get list of all info controls available
        icRegistry.loadCreators().then(function(allInfoControls) {
            //get item body container
            //editor panel..
            var $itemBody = $itemPanel.find('.qti-itemBody');

            //prepare data for the tpl:
            var tools = {},
                toolArray,
                alreadySet = _.pluck(item.getElements('infoControl'), 'typeIdentifier'),
                allInfoControlsSize,
                $managerPanel,
                i = 0;

            _.each(allInfoControls, function(creator) {
                var name = creator.getTypeIdentifier(),
                    manifest = icRegistry.get(name),
                    controlExists = _.indexOf(alreadySet, name) > -1,
                    defaultProperties = creator.getDefaultProperties(),
                    position = defaultProperties.position || 100 + i;

                if (manifest.disabled) {
                    return;
                }

                if (manifest.tags && manifest.tags[0] === _studentToolTag) {
                    tools[name] = {
                        label: manifest.label,
                        description: manifest.description,
                        checked: controlExists,
                        position: position,
                        name: name
                    };
                }

                // store the name also in the value for convenience
                allInfoControls[name].name = name;

                // determine where to position a tool on the toolbar
                allInfoControls[name].position = position;

                // on load we assume that everything that already exists is also checked
                // this counts also for the toolbar which has no actual checkbox
                allInfoControls[name].checked = controlExists;

                // have the resources already been copied,
                // i.e. is the tool currently installed
                allInfoControls[name].installed = controlExists;

                // must be false, even if tool has been installed
                // before, resources might have changed
                allInfoControls[name].copied = false;

                i++;
            });

            toolArray = _.sortBy(tools, 'position');

            allInfoControlsSize = _.size(allInfoControls);

            $managerPanel = managerTpl({
                tools: toolArray
            });
            $container.append($managerPanel);

            //init tooltips
            tooltip.lookup($container);

            //init event listeners:
            var $checkBoxes = $('[data-role="pic-manager"]').find('input:checkbox');

            $checkBoxes.on('change.picmanager', function onChangePicManager(e) {
                e.stopPropagation();

                // install toolbar if required
                if (this.checked && !allInfoControls[_studentToolbarId].installed) {
                    allInfoControls[_studentToolbarId].checked = true;
                }

                allInfoControls[this.name].checked = this.checked;

                toggleCheckboxState($checkBoxes, true);

                processAllControls();
            });

            /**
             * Creates array of PIC controllers names in order which the controller will be processed
             *
             * @returns {String[]}
             */
            function getOrderedPICNames() {
                return _.keys(allInfoControls).reduce(function(ordered, infoContorolName) {
                    if (infoContorolName === _studentToolbarId) {
                        ordered.unshift(infoContorolName);
                    } else {
                        ordered.push(infoContorolName);
                    }

                    return ordered;
                }, []);
            }
            /**
             * Iterate over all controls and launch the actual installer/un-installer
             */
            function processAllControls() {
                var cnt = 0;

                _.forEach(getOrderedPICNames(), function(controlName) {
                    const control = allInfoControls[controlName];
                    // is there any action required at all?
                    // if not and if there are still items
                    // left proceed to the next one
                    if (control.checked === control.installed) {
                        cnt++;
                        return true;
                    }

                    processControl(control);

                    // break here and wait for the next call
                    // to be executed from processControl()
                    return false;
                });

                if (cnt === allInfoControlsSize) {
                    toggleCheckboxState($checkBoxes, false);
                }
            }

            /**
             * Render tool|toolbar
             *
             * @param elt
             */
            function renderControl(elt) {
                var stsClassName = elt.typeIdentifier === _studentToolbarId ? 'sts-scope' : 'sts-scope sts-tmp-element';

                //add the student tool css scope
                elt.attr('class', stsClassName);

                elt.prop('position', allInfoControls[elt.typeIdentifier].position);
                elt.prop('toolbarId', 'sts-' + _studentToolbarId);

                //render it
                elt.setRenderer(creatorRenderer.get());

                elt.render($placeholder);

                $placeholder = null;

                Promise.all(elt.postRender({}))
                    .then(function () {
                        var widget = elt.data('widget');

                        allInfoControls[elt.typeIdentifier].installed = true;

                        //inform height modification
                        widget.$container.trigger('contentChange.gridEdit');
                        widget.$container.trigger('resize.gridEdit');

                        if (elt.typeIdentifier !== _studentToolbarId) {
                            toggleCheckboxState($checkBoxes, false);
                        }

                        // continue with the next element of allInfoControls
                        processAllControls();
                    })
                    .catch(function (err) {
                        logger.error(err);
                    });
            }

            /**
             * Remove a student tool
             *
             * @param control
             */
            function removeControl(control) {
                var infoControls = item.getElements('infoControl'),
                    remove = function (_control) {
                        var studentTool = _.find(infoControls, {
                            typeIdentifier: _control.name
                        });
                        //call ic hook destroy() method
                        studentTool.data('pic').destroy();

                        //remove the widget from dom
                        $('#sts-' + _control.name).remove();
                        $itemBody.find('.widget-box[data-serial=' + studentTool.serial + ']').remove();

                        //remove form model
                        item.removeElement(studentTool.serial);

                        allInfoControls[_control.name].checked = false;
                        allInfoControls[_control.name].installed = false;
                    };

                //remove it
                remove(control);
                // in case there only two elements left, one of them
                // must be the toolbar and needs to be removed too
                if (_.size(infoControls) > 2) {
                    processAllControls();
                    return;
                }

                // reset to checked:true on click of any tool
                remove(allInfoControls[_studentToolbarId]);

                toggleCheckboxState($checkBoxes, false);
                processAllControls();
            }

            /**
             * install|un-install a single control
             *
             * @param control
             * @returns {boolean}
             */
            function processControl(control) {
                // what needs to be done to the control? install|uninstall
                if (!control.checked) {
                    removeControl(control);
                    return true;
                }

                //create an info control (student tool|toolbar) and add it to the them
                $placeholder = getNewInfoControlNode(control.name);
                $itemBody.prepend($placeholder);

                // install procedure
                containerHelper.createElements(item.getBody(), contentHelper.getContent($itemBody), function (newElts) {
                    // although newElts appears to be a collection it holds
                    // only _one_ element due to the callback mechanism
                    // between processControl() and processAllControls()
                    _.each(newElts, function (elt) {
                        // update look-up list
                        allInfoControls[control.name].copied = true;
                        renderControl(elt);
                    });
                });
            }
        }, true);
    }

    return function picManager($container, $itemPanel, itemUri) {
        //load infoControl model first into the creator renderer
        creatorRenderer.get().load(
            function () {
                initStudentToolManager($container, $itemPanel, itemUri);
            },
            ['infoControl']
        );
    };
});