﻿var $j = jQuery.noConflict();

String.prototype.namespace = function(separator) {
    var namespaces = this.split(separator || '.')
    var root = window;
    for (var i = 0, len = namespaces.length; i < len; i++) {
        root = root[namespaces[i]] = root[namespaces[i]] || {};
    }
    return root;
};


"SMART.Carousel".namespace();

SMART.Carousel = function() {

    var carouselContainer;
    var prevButton;
    var nextButton;
    var slideContainer;
    var slideContainerHolder;
    var slides;
    var infiniteScroll = false;
    var slideWidth;
    var viewPortWidth;
    var currentOffset;
    var widthOfCarousel;
    var itemsInCarousel;
    var locked = false;
    var moveToNextCallback;
    var maximumOffset;
    var isPartialSlideVisibleAtStart = true;

    /* ===================== */
    /* CAROUSEL MOTION LOGIC */
    /* ===================== */

    moveToNext = function() {

        if (isLocked())
            return;

        lock();

        if (atEnd()) {
            unlock();
            return;
        }

        infiniteScroll ? moveToNextWithLoop() : moveToNextWithoutLoop()

    }

    moveToPrevious = function() {

        if (isLocked())
            return;

        lock();

        infiniteScroll ? moveToPreviousWithLoop() : moveToPreviousWithoutLoop()

    }


    moveToPreviousWithoutLoop = function() {

        // if we're at 0, only move the carousel item partially
        if ((getCurrentPosition() * -1) <= slideWidth)
            animateCarousel(slideContainer, 0); // slide to beginning
        else
            animateCarousel(slideContainer, getCurrentPosition() + slideWidth);
    }

    moveToNextWithoutLoop = function() {

        var shiftThisFar;

        if (isPartialSlideVisibleAtStart) {
            // if we're at 0, only move the carousel item partially
            shiftThisFar = atBeginning() ? parseInt((slideWidth / 2) + 44) :
                                           slideWidth;
        }
        else {
            shiftThisFar = slideWidth;
        }

        animateCarousel(slideContainer, limitPositionToEndOfViewPort(getCurrentPosition() - shiftThisFar));
    }

    moveToPreviousWithLoop = function() { throw "Not Implemented!"; }
    moveToNextHardWithLoop = function() { throw "Not Implemented!"; }

    animateCarousel = function(container, restingPosition) {
        container.animate({ left: restingPosition + "px" }, 500, "swing", function() {
            updatePosition(container);
            unlock();
            setButtonState();
        });
    }

    jumpCarouselTo = function(valueInPixels) {

        if (valueInPixels > 0)
            valueInPixels = 0;

        if (valueInPixels < maximumOffset)
            valueInPixels = maximumOffset;

        lock();
        slideContainer.css("left", valueInPixels + "px");
        updatePosition(slideContainer);
        unlock();
        setButtonState();
    }

    freeSlideTo = function(valueinPixels) {
        if (valueinPixels > 0)
            valueinPixels = 0;

        if (valueinPixels < maximumOffset)
            valueinPixels = maximumOffset;

        animateCarousel(slideContainer, valueinPixels);
    }

    lock = function() { locked = true; }
    unlock = function() { locked = false; }
    isLocked = function() { return locked; };

    setButtonState = function() {
        if (getWidthOfCarouselItems() < getViewPortWidth()) {
            disablePrev(); disableNext();
        }
        else if (atBeginning()) {
            disablePrev(); enableNext();
        }
        else if (atEnd()) {
            enablePrev(); disableNext();
        }
        else {
            enablePrev(); enableNext();
        }
    }


    /* ========================= */
    /* POSITION & SIZING METHODS */
    /* ========================= */

    atBeginning = function() {
        var currentPos = getCurrentPosition();
        return currentPos == 0;
    }
    atEnd = function() { return !hasContentToScrollTo(slideContainer); }

    getViewPortWidth = function() {
        return viewPortWidth = viewPortWidth || slideContainerHolder.width();
    }

    getWidthOfCarouselItems = function() {
        return widthOfCarousel = widthOfCarousel || getSlideWidth(slides) * slides.length;
    }

    hasContentToScrollTo = function(container) {
        return getWidthOfCarouselItems() > (getViewPortWidth() + (getCurrentPosition(container) * -1));
    }

    limitPositionToEndOfViewPort = function(position) {
        return (position > maximumOffset) ? position : maximumOffset;
    }

    updatePosition = function(container) {
        return currentOffset = convertPxToInt(container.css("left"));
    }

    getCurrentPosition = function() {
        var cssLeft = slideContainer.css("left")
        var convertPx = convertPxToInt(cssLeft);

        return currentOffset || convertPx;
    }

    convertPxToInt = function(value) {
        // type check on string
        var x = value.replace("px", "")
        return parseInt(x);
    }

    getSlideWidth = function(slide) {
        return slide.outerWidth() + convertPxToInt(slide.css('margin-left')) + convertPxToInt(slide.css('margin-right'));
    }

    getMaximumOffset = function(slideVisibleAtStart) {

        var offset;

        if (slideVisibleAtStart) {
            offset = getWidthOfCarouselItems() - getViewPortWidth();
        }
        else {
            offset = slideWidth * slides.length;
        }

        return offset * -1;
    }

    /* ====================== */
    /* ENABLE/DISABLE BUTTONS */
    /* ====================== */

    disablePrev = function() {
        disableButton(prevButton);
    }

    disableNext = function() {
        disableButton(nextButton);
    }

    enablePrev = function() {
        enableButton(prevButton);
    }

    enableNext = function() {
        enableButton(nextButton);
    }

    disableButton = function(button) {
        button.find("a").css("cursor", "default");
        button.find("a").css("background-position", "-22px");
    }
    enableButton = function(button) {
        button.find("a").css("cursor", "pointer");
        button.find("a").css("background-position", "0px");
    }

    /* ===================== */
    /* INIT */
    /* ===================== */

    checkForDefaultValues = function(defaultValues) {

        if (defaultValues === undefined)
            throw "defaultValues undefined on init()";

        if (defaultValues.identifiers.carouselContainer === undefined)
            throw "defaultValues.carouselContainer undefined on init()";

        if (defaultValues.identifiers.prevButton === undefined)
            throw "defaultValues.prevButton undefined on init()";

        if (defaultValues.identifiers.nextButton === undefined)
            throw "defaultValues.nextButton undefined on init()";

        if (defaultValues.identifiers.slideContainerHolder === undefined)
            throw "defaultValues.slideContainerHolder undefined on init()";

        if (defaultValues.identifiers.slideContainer === undefined)
            throw "defaultValues.slideContainer undefined on init()";

        if (defaultValues.identifiers.slide === undefined)
            throw "defaultValues.slide undefined on init()";
    }

    /* ===================== */
    /* PUBLIC METHODS!!!     */
    /* ===================== */

    var pub = {

        init: function(defaultValues) {

            checkForDefaultValues(defaultValues);

            // load the main elements into private variables

            carouselContainer = $j(defaultValues.identifiers.carouselContainer);
            //carouselContainer = $j(defaultValues.carouselContainer);
            prevButton = carouselContainer.find('.' + defaultValues.identifiers.prevButton);
            nextButton = carouselContainer.find('.' + defaultValues.identifiers.nextButton);
            slideContainerHolder = carouselContainer.find('.' + defaultValues.identifiers.slideContainerHolder);
            slideContainer = carouselContainer.find('.' + defaultValues.identifiers.slideContainer);
            slides = carouselContainer.find('.' + defaultValues.identifiers.slide);

            slideWidth = getSlideWidth(slides);

            // load infiniteScroll value is given.  "infiniteScroll" is not implemented
            if (defaultValues.infiniteScroll !== undefined)
                infiniteScroll = defaultValues.infiniteScroll;

            if (defaultValues.isPartialSlideVisibleAtStart !== undefined)
                isPartialSlideVisibleAtStart = defaultValues.isPartialSlideVisibleAtStart;

            maximumOffset = getMaximumOffset(isPartialSlideVisibleAtStart);

            // establish initial position of container
            updatePosition(slideContainer);

            // force width of carousel to match the number of items
            // in the carousel
            slideContainer.css("width", getWidthOfCarouselItems());

            setButtonState();

            nextButton.mousehold(function() {
                moveToNext();
            });

            prevButton.mousehold(function() {
                moveToPrevious();
            });

            return true;
        },

        moveToNext: function() {
            moveToNext();
        },

        moveToPrevious: function() {
            moveToPrevious();
        },
        areWeLocked: function() {
            return isLocked();
        },
        slideByPixels: function(valueinPixels) {
            freeSlideTo(valueinPixels);
        },
        slideByPercentage: function(valueinPercentage) {
            var percent = valueinPercentage / 100;
            freeSlideTo(percent * maximumOffset);
        },
        jumpToByPixels: function(valueInPixels) {
            jumpCarouselTo(valueInPixels);
        },
        jumpToByPercentage: function(valueinPercentage) {
            var percent = valueinPercentage / 100;
            jumpCarouselTo(percent * maximumOffset);
        },
        resetButtonState: function() {
            setButtonState();
        }
    }

    return pub;
};
