define([
    "jquery"
    ], function (
        jq
     ) {

    "use strict";

    /**
     * @constructor
     */

    return function( options, context, metadata ){
        var bgTrackPositions = {},
            bgThumbPositions = {},
            sliderElement = null,
            disabledContainer = null,
            TrackState = {
                Default: "track_state_default",
                Hover: "track_state_hover"
            },
            ThumbState = {
                Default: "thumb_state_default",
                Hover: "thumb_state_hover",
                Correct: "thumb_state_correct",
                Incorrect: "thumb_state_incorrect"
            },
            trackWidth = options.trackImage.width,
            trackHeight = options.trackImage.height / 2,
            thumbWidth = options.thumbImage.width / 4,
            thumbHeight = options.thumbImage.height,
            pointerUpEventName, pointerDownEventName,
            sliderStyles = null,
            sliderCSS = "",
            allAcceptedSolutions = new Array(),
            elementVariableNameSpace = "com_tts",
            elementVariableId = null,
            elementType = "slider",
            id = options.id,
            pageEvaluatedId = null,
            answer = null,
            isRight = false,
            isEnabled = true;

        bgTrackPositions[TrackState.Default] = "0 0";
        bgTrackPositions[TrackState.Hover] = "0 " + (-options.trackImage.height / 2) + "px";

        bgThumbPositions[ThumbState.Default] = "0 0";
        bgThumbPositions[ThumbState.Hover] = (-options.thumbImage.width / 4) + "px 0";
        bgThumbPositions[ThumbState.Correct] = (-2 * (options.thumbImage.width / 4)) + "px 0";
        bgThumbPositions[ThumbState.Incorrect] = (-3 * (options.thumbImage.width / 4)) + "px 0";

        sliderCSS =
            // Track default style
            // webkit (Edge, Google, Safari)
            ".sliderTrackStyle_" + metadata.getId() + "::-webkit-slider-runnable-track {\n" +
            //"margin-top: " + options.trackOffset + "px;\n" +
            "width: " + trackWidth + "px;\n" +
            "height: " + trackHeight + "px;\n" +
            "background-image: url(\'" + options.trackImage.fileNameURL + "\');\n" +
            "background-position: " + bgTrackPositions[TrackState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderTrackStyle_" + metadata.getId() + "::-moz-range-track {\n" +
            //"margin-top: " + options.trackOffset + "px;\n" +
            "width: " + trackWidth + "px;\n" +
            "height: " + trackHeight + "px;\n" +
            "background-image: url(\'" + options.trackImage.fileNameURL + "\');\n" +
            "background-position: " + bgTrackPositions[TrackState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderTrackStyle_" + metadata.getId() + "::-ms-track {\n" +
            //"margin-top: " + options.trackOffset + "px;\n" +
            "color: transparent;\n" + 
            "background: transparent;\n" +
            "border: 0;\n" +
            "width: " + trackWidth + "px;\n" +
            "height: " + trackHeight + "px;\n" +
            "background-image: url(\'" + options.trackImage.fileNameURL + "\');\n" +
            "background-position: " + bgTrackPositions[TrackState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "}\n" +
            ".sliderTrackStyle_" + metadata.getId() + "::-ms-fill-lower {\n" +
            "background: transparent;\n" +
            "}\n" +

            // Track hover style
            // webkit (Edge, Google, Safari)
            ".sliderTrackHoverStyle_" + metadata.getId() + "::-webkit-slider-runnable-track:hover {\n" +
            "background-position: " + bgTrackPositions[TrackState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderTrackHoverStyle_" + metadata.getId() + "::-moz-range-track:hover {\n" +
            "background-position: " + bgTrackPositions[TrackState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderTrackHoverStyle_" + metadata.getId() + "::-ms-track:hover {\n" +
            "background-position: " + bgTrackPositions[TrackState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +

            // Thumb default style
            // webkit (Edge, Google, Safari)
            ".sliderThumbStyle_" + metadata.getId() + "::-webkit-slider-thumb {\n" +
            "-webkit-appearance: none;\n" +
            //"margin-top: " + options.thumbOffset + "px;\n" +
            "margin-top: " + ((trackHeight / 2) - (thumbHeight / 2)) + "px;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderThumbStyle_" + metadata.getId() + "::-moz-range-thumb {\n" +
            "-moz-appearance: none;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderThumbStyle_" + metadata.getId() + "::-ms-thumb {\n" +
            "appearance: none;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Default] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +

            // Thumb hover style
            // webkit (Edge, Google, Safari)
            ".sliderThumbHoverStyle_" + metadata.getId() + "::-webkit-slider-thumb:hover {\n" +
            "background-position: " + bgThumbPositions[ThumbState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderThumbHoverStyle_" + metadata.getId() + "::-moz-range-thumb:hover {\n" +
            "background-position: " + bgThumbPositions[ThumbState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderThumbHoverStyle_" + metadata.getId() + "::-ms-thumb:hover {\n" +
            "background-position: " + bgThumbPositions[ThumbState.Hover] + ";\n" +
            //"background-repeat: no-repeat;\n" +
            "}\n" +

            // Thumb style (Correct)
            // webkit (Edge, Google, Safari)
            ".sliderThumbCorrectStyle_" + metadata.getId() + "::-webkit-slider-thumb {\n" +
            "-webkit-appearance: none;\n" +
            "appearance: none;\n" +
            "margin-top: " + ((trackHeight / 2) - (thumbHeight / 2)) + "px;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Correct] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderThumbCorrectStyle_" + metadata.getId() + "::-moz-range-thumb {\n" +
            "-moz-appearance: none;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Correct] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderThumbCorrectStyle_" + metadata.getId() + "::-ms-thumb {\n" +
            "appearance: none;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Correct] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +

            // Thumb style (Incorrect)
            // webkit (Edge, Google, Safari)
            ".sliderThumbIncorrectStyle_" + metadata.getId() + "::-webkit-slider-thumb {\n" +
            "-webkit-appearance: none;\n" +
            "appearance: none;\n" +
            "margin-top: " + ((trackHeight / 2) - (thumbHeight / 2)) + "px;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Incorrect] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // moz (Firefox)
            ".sliderThumbIncorrectStyle_" + metadata.getId() + "::-moz-range-thumb {\n" +
            "-moz-appearance: none;\n" +
            "appearance: none;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Incorrect] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}\n" +
            // ms (Internet Explorer 10+)
            ".sliderThumbIncorrectStyle_" + metadata.getId() + "::-ms-thumb {\n" +
            "appearance: none;\n" +
            "width: " + thumbWidth + "px;\n" +
            "height: " + thumbHeight + "px;\n" +
            "border: none;\n" +
            "border-radius: 0;\n" +
            "background-image: url(\'" + options.thumbImage.fileNameURL + "\');\n" +
            "background-position: " + bgThumbPositions[ThumbState.Incorrect] + ";\n" +
            "background-repeat: no-repeat;\n" +
            "background-color: transparent;\n" +
            "}";

        function createSlider() {
            sliderStyles = document.createElement('style');
            sliderStyles.id = "sliderStyleClasses_" + metadata.getId();
            sliderStyles.type = "text/css";
            sliderStyles.innerHTML = sliderCSS;
            injectCSS();

            sliderElement = document.createElement('input');
            sliderElement.id = "sliderElement_" + metadata.getId();
            sliderElement.type = 'range';
            sliderElement.min = options.minValue;
            sliderElement.max = options.maxValue;
            sliderElement.value = options.startValue;
            sliderElement.step = options.stepValue;
            sliderElement.style.width = trackWidth + "px";
            sliderElement.style.height = thumbHeight + "px";
            sliderElement.style.position = "absolute";
            sliderElement.style.top = "0px";
            sliderElement.style.left = "0px";
            sliderElement.style.margin = "0";
            sliderElement.style.padding = "0";
            sliderElement.style.WebkitAppearance = "none";
            sliderElement.style.backgroundColor = "transparent";
            sliderElement.classList.add("sliderTrackStyle_" + metadata.getId());
            sliderElement.classList.add("sliderThumbStyle_" + metadata.getId());
            sliderElement.classList.add("sliderTrackHoverStyle_" + metadata.getId());
            sliderElement.classList.add("sliderThumbHoverStyle_" + metadata.getId());

            disabledContainer = document.createElement("div");
            disabledContainer.style.position = "absolute";
            disabledContainer.style.top = "0px";
            disabledContainer.style.left = "2px"; // disabledContainer necessary for IE? Slider has an invisible border?
            disabledContainer.style.width = trackWidth + "px";
            disabledContainer.style.height = thumbHeight + "px";
            disabledContainer.style.display = "none";

            sliderElement.appendChild(disabledContainer);

            sliderElement.style.transform = "rotate(" + options.rotation + "deg)";

            getAllAcceptedSolutions();

            elementVariableId = "slider" + metadata.getId();

            if (!(options.isInAuthoringMode) && options.isQuestionPage) {
                context.page.quiz.add(options.instance, options.groupId + elementType, true);
            }
        }

        function getAllAcceptedSolutions() {
            var acceptedSolutions = options.solutionRange.trim();

            acceptedSolutions = acceptedSolutions.split(";");

            for (var i = 0; i < acceptedSolutions.length; i++) {
                if (acceptedSolutions[i].toString().indexOf('-') == -1) {
                    allAcceptedSolutions.push(parseFloat(acceptedSolutions[i]));
                } else {
                    allAcceptedSolutions.push(acceptedSolutions[i]);
                }
            }
        }

        function onUpdatedValue() {
            if (isEvaluated()) {
                return;
            }
            setUserAnswer(getCurrentAnswer());

            var values = {};

            values.id = options.id;
            values.value = answer;
            context.page.event.fire(options.instance, "onSliderValueChange", values);
        }

        function getCurrentAnswer() {
            return parseFloat(sliderElement.value);
        }

        function switchToUserSolution() {
            disableSlider();

            if (options.isStandardPlayer) {
                context.stage.variable.set(pageEvaluatedId, true, elementVariableNameSpace);
            }

            getSavedAnswer();

            if (isRight) {
                sliderElement.classList.remove("sliderThumbStyle_" + metadata.getId());
                sliderElement.classList.add("sliderThumbCorrectStyle_" + metadata.getId());
            } else {
                sliderElement.classList.remove("sliderThumbStyle_" + metadata.getId());
                sliderElement.classList.add("sliderThumbIncorrectStyle_" + metadata.getId());
            }
        }

        function switchToBestPracticeSolution() {
            disableSlider();

            sliderElement.classList.remove("sliderThumbCorrectStyle_" + metadata.getId());
            sliderElement.classList.remove("sliderThumbIncorrectStyle_" + metadata.getId());
            sliderElement.classList.add("sliderThumbStyle_" + metadata.getId());

            sliderElement.value = options.sampleSolution;
        }

        function disableSlider() {
            if (!isEnabled) {
                return;
            }

            sliderElement.classList.remove("sliderTrackHoverStyle_" + metadata.getId(), "sliderThumbHoverStyle_" + metadata.getId());
            sliderElement.disabled = true;
            disabledContainer.style.display = "block";
            isEnabled = false;
        }

        function lockNavigation() {
            if (options.isInAuthoringMode || !(options.hasStageTriggerSupport)) {
                return;
            }
            context.stage.trigger.lock(metadata.getId());
        }

        function unlockNavigation() {
            if (options.isInAuthoringMode || !(options.hasStageTriggerSupport)) {
                return;
            }
            context.stage.trigger.unlock(metadata.getId());
        }

        function initPointerEventNames() {
            if (!pointerUpEventName) {
                var nodeName = getHotspotElementNodeName();
                pointerDownEventName = context.device.getDownEvents(nodeName);
                pointerUpEventName = context.device.getUpEvents(nodeName);
            }
        }

        function getHotspotElementNodeName() {
            return sliderElement.nodeName
                ? sliderElement.nodeName
                : "document";
        }

        function bindEvents() {
            if (options.isInAuthoringMode) {
                return;
            }

            removeEvents();

            context.device.attachFirstSupportedEvent(sliderElement, pointerDownEventName, onMouseDown);
            context.device.attachFirstSupportedEvent(sliderElement, pointerUpEventName, onMouseUp);

            sliderElement.addEventListener("change", onUpdatedValue);
        }

        function removeEvents() {
            unlockNavigation();
            if (!sliderElement) {
                return;
            }

            initPointerEventNames();

            context.device.removeFirstSupportedEvent(sliderElement, pointerDownEventName, onMouseDown);
            context.device.removeFirstSupportedEvent(sliderElement, pointerUpEventName, onMouseUp);

            sliderElement.removeEventListener("change", onUpdatedValue);
        }

        function onMouseDown(e) {
            lockNavigation();
        }

        function onMouseUp(e) {
            unlockNavigation();
        }

        function isEvaluated() {

            if (options.isInPreviewMode) {
                return false;
            }

            if (options.isStandardPlayer || options.isInLearningMode) {
                return context.stage.variable.get(pageEvaluatedId, elementVariableNameSpace);
            }

            return context.page.quiz.hasQuestion() && context.page.quiz.getQuestion().isFinalized();
        }

        function getSavedAnswer() {
            answer = parseFloat(context.stage.variable.get(elementVariableId, elementVariableNameSpace));

            if (!isNaN(answer)) {
                sliderElement.value = answer;
                isRight = isCorrect(answer);
            } else {
                sliderElement.value = parseFloat(options.sampleSolution);
                isRight = true;
            }
        }

        function setSavedAnswer(value) {
            if (!isNaN(value)) {
                context.stage.variable.set(elementVariableId, parseFloat(value), elementVariableNameSpace);
            } else {
                context.stage.variable.set(elementVariableId, false, elementVariableNameSpace);
            }
        }

        function setUserAnswer(value) {
            answer = value;
            isRight = isCorrect(answer);
            setSavedAnswer(answer);

            if (options.isQuestionPage) {
                context.page.quiz.setSelected(options.instance, isRight);
            }
        }

        function isCorrect(value) {
            for (var i = 0; i < allAcceptedSolutions.length; i++) {
                if (typeof allAcceptedSolutions[i] != "number") {
                    var range = allAcceptedSolutions[i].split("-");

                    if (parseFloat(range[0]) <= value && value <= parseFloat(range[1])) {
                        return true;
                    }
                } else {
                    if (parseFloat(allAcceptedSolutions[i]) == value) {
                        return true;
                    }
                }
            }

            return false;
        }

        function injectCSS() {
            var css = document.getElementById('sliderStyleClasses_' + metadata.getId());

            if (!css) {
                document.getElementsByTagName('head')[0].appendChild(sliderStyles);
            }
        }

        function removeCSS() {
            var css = document.getElementById('sliderStyleClasses_' + metadata.getId());

            if (css) {
                document.getElementById('sliderStyleClasses_' + metadata.getId()).remove();
            }
        }

        return {
            build : function(){
                createSlider();
                return {
                    getElement: function () {
                        return sliderElement;
                    },
                    getDisabledElement: function () {
                        return disabledContainer;
                    },
                    bindEvents : function(){
                        bindEvents();
                    },
                    removeEvents: function () {
                        removeEvents();
                    },
                    onEnterPage: function () {
                        pageEvaluatedId = metadata.page.getId() + "_evaluated";
                        if (options.isStandardPlayer) {
                            injectCSS();
                        }
                    },
                    onExitPage: function () {
                        if (options.isStandardPlayer) {
                            removeCSS();
                        }
                    },
                    onAllComponentsReady: function () {
                        onUpdatedValue();
                    },
                    onUserSolution: function () {
                        switchToUserSolution();
                    },
                    onBestPracticeSolution: function () {
                        switchToBestPracticeSolution();
                    }
                }
            }
        }
    }
});