(function(requirejs, require, define) {
    define(['edx-ui-toolkit/js/utils/html-utils'], function(HtmlUtils) {
        return {
            initializeBaseTargets: initializeBaseTargets,
            initializeTargetField: initializeTargetField,
            destroyTargetField: destroyTargetField
        };

        function initializeBaseTargets(state) {
            (function(c1) {
                while (c1 < state.config.targets.length) {
                    processTarget(state, state.config.targets[c1]);

                    c1 += 1;
                }
            }(0));
        }

        function initializeTargetField(draggableObj) {
            var iconElOffset;

            if (draggableObj.targetField.length === 0) {
                draggableObj.originalConfigObj.target_fields.every(function(targetObj) {
                    processTarget(draggableObj.state, targetObj, true, draggableObj);

                    return true;
                });
            } else {
                iconElOffset = draggableObj.iconEl.position();

                draggableObj.targetField.every(function(targetObj) {
                    targetObj.offset.top = iconElOffset.top + targetObj.y;
                    targetObj.offset.left = iconElOffset.left + targetObj.x;

                    return true;
                });
            }
        }

        function destroyTargetField(draggableObj) {
            var indexOffset, lowestRemovedIndex;

            indexOffset = 0;
            lowestRemovedIndex = draggableObj.state.targets.length + 1;

            draggableObj.targetField.every(function(target) {
                target.el.remove();

                if (lowestRemovedIndex > target.indexInStateArray) {
                    lowestRemovedIndex = target.indexInStateArray;
                }

                draggableObj.state.targets.splice(target.indexInStateArray - indexOffset, 1);
                indexOffset += 1;

                return true;
            });

            draggableObj.state.targets.every(function(target) {
                if (target.indexInStateArray > lowestRemovedIndex) {
                    target.indexInStateArray -= indexOffset;
                }

                return true;
            });

            draggableObj.targetField = [];
        }

        function processTarget(state, obj, fromTargetField, draggableObj) {
            var $targetEl, borderCss, $numTextEl, targetObj;

            borderCss = '';
            if (state.config.targetOutline === true) {
                borderCss = 'border: 1px dashed gray; ';
            }

            $targetEl = $(
                HtmlUtils.joinHtml(
                    HtmlUtils.HTML('<div style="display: block; position: absolute; width: '),
                    obj.w,
                    HtmlUtils.HTML('px; height: '),
                    obj.h,
                    HtmlUtils.HTML('px; top: '),
                    obj.y,
                    HtmlUtils.HTML('px; left: '),
                    obj.x,
                    HtmlUtils.HTML('px; '),
                    borderCss,
                    HtmlUtils.HTML('"aria-dropeffect=""></div>')
                ).toString()
            );
            if (fromTargetField === true) {
                $targetEl.appendTo(draggableObj.iconEl);
            } else {
                $targetEl.appendTo(state.baseImageEl.parent());
            }

            $targetEl.mousedown(function(event) {
                event.preventDefault();
            });

            if (state.config.onePerTarget === false) {
                $numTextEl = $(
                    HtmlUtils.joinHtml(
                        HtmlUtils.HTML('<div style=" display: block; position: absolute;'),
                        HtmlUtils.HTML('width: 24px; height: 24px; top: '),
                        obj.y,
                        HtmlUtils.HTML('px; left: '),
                        obj.x,
                        obj.w - 24,
                        HtmlUtils.HTML('px; border: 1px solid black; text-align: center; z-index: 500;'),
                        HtmlUtils.HTML('background-color: white; font-size: 0.95em; color: #009fe2; ">0</div>')
                    ).toString()
                );
            } else {
                $numTextEl = null;
            }

            targetObj = {
                uniqueId: state.getUniqueId(),

                id: obj.id,

                x: obj.x,
                y: obj.y,

                w: obj.w,
                h: obj.h,

                el: $targetEl,
                offset: $targetEl.position(),

                draggableList: [],

                state: state,

                targetEl: $targetEl,

                numTextEl: $numTextEl,
                updateNumTextEl: updateNumTextEl,

                removeDraggable: removeDraggable,
                addDraggable: addDraggable,

                type: 'base',
                draggableObj: null
            };

            if (fromTargetField === true) {
                targetObj.offset = draggableObj.iconEl.position();
                targetObj.offset.top += obj.y;
                targetObj.offset.left += obj.x;

                targetObj.type = 'on_drag';
                targetObj.draggableObj = draggableObj;
            }

            if (state.config.onePerTarget === false) {
                $numTextEl.appendTo(state.baseImageEl.parent());
                $numTextEl.mousedown(function(event) {
                    event.preventDefault();
                });
                $numTextEl.mouseup(function() {
                    cycleDraggableOrder.call(targetObj);
                });
            }

            targetObj.indexInStateArray = state.targets.push(targetObj) - 1;

            if (fromTargetField === true) {
                draggableObj.targetField.push(targetObj);
            }
        }

        function removeDraggable(draggable) {
            var c1;

            this.draggableList.splice(draggable.onTargetIndex, 1);

            // An item from the array was removed. We need to updated all indexes accordingly.
            // Shift all indexes down by one if they are higher than the index of the removed item.
            c1 = 0;
            while (c1 < this.draggableList.length) {
                if (this.draggableList[c1].onTargetIndex > draggable.onTargetIndex) {
                    this.draggableList[c1].onTargetIndex -= 1;
                }

                c1 += 1;
            }

            draggable.onTarget = null;
            draggable.onTargetIndex = null;

            if (this.type === 'on_drag') {
                this.draggableObj.numDraggablesOnMe -= 1;
            }

            this.updateNumTextEl();
        }

        function addDraggable(draggable) {
            draggable.onTarget = this;
            draggable.onTargetIndex = this.draggableList.push(draggable) - 1;

            if (this.type === 'on_drag') {
                this.draggableObj.numDraggablesOnMe += 1;
            }

            this.updateNumTextEl();
        }

        /*
     * function cycleDraggableOrder
     *
     * Parameters:
     *     none - This function does not expect any parameters.
     *
     * Returns:
     *     undefined - The return value of this function is not used.
     *
     * Description:
     *     Go through all draggables that are on the current target, and decrease their
     *     z-index by 1, making sure that the bottom-most draggable ends up on the top.
     */
        function cycleDraggableOrder() {
            var c1, lowestZIndex, highestZIndex;

            if (this.draggableList.length < 2) {
                return;
            }

            highestZIndex = -10000;
            lowestZIndex = 10000;

            for (c1 = 0; c1 < this.draggableList.length; c1 += 1) {
                if (this.draggableList[c1].zIndex < lowestZIndex) {
                    lowestZIndex = this.draggableList[c1].zIndex;
                }

                if (this.draggableList[c1].zIndex > highestZIndex) {
                    highestZIndex = this.draggableList[c1].zIndex;
                }
            }

            for (c1 = 0; c1 < this.draggableList.length; c1 += 1) {
                if (this.draggableList[c1].zIndex === lowestZIndex) {
                    this.draggableList[c1].zIndex = highestZIndex;
                } else {
                    this.draggableList[c1].zIndex -= 1;
                }

                this.draggableList[c1].iconEl.css('z-index', this.draggableList[c1].zIndex);
                if (this.draggableList[c1].labelEl !== null) {
                    this.draggableList[c1].labelEl.css('z-index', this.draggableList[c1].zIndex);
                }
            }
        }

        function updateNumTextEl() {
            if (this.numTextEl !== null) {
                this.numTextEl.text(this.draggableList.length);
            }
        }
    }); // End-of: define([], function () {
}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define) {
