/*
Carousel application.
*/

/*jslint browser: true, indent: 4, maxlen: 80, plusplus: true, vars: true */
/*global $, console, jQuery,  */

(function () {

    "use strict";

    var current_position = [];
    var carousel_data = [];
    var visible_data = [];


    /*
    Decorate html elements within carousel.

    carousel
        HTML elemeent for main wrapper 'carousel' div
    identifier
        Unique integer identifier
    */
    function decorate(carousel, identifier) {

        var class_selected, data, nav_container, nav_data, x;

        data = getData(carousel);
        carousel_data[identifier] = data;

        nav_data = '';
        for (x = 0; data.length > x; x++) {

            // Unique id's for data elements
            //data[x].id = "carousel-" + identifier + "-data-" + x;

            class_selected = '';
            if (data[x].className === 'carousel-selected') {
                class_selected = 'class="selected"';
            }

            // Build navigation elements
            nav_data += '<li ' + class_selected +
                '><a href="" data-identifier="' + identifier +
                '" data-target="' + x + '">' + (x + 1) + '</a></li>';
        }

        // Print Navigation Elements if container found.
        nav_container = getNavigationContainer(carousel);
        if (typeof (nav_container) !== 'undefined') {
            $('<ol>' + nav_data + '</ol>').prependTo(nav_container);
        }
    }


    /*
    Setup event handlers
    @param HTMLelement carousel
        html div element
    @param int identifier
        Carousel Identifier
    */
    function events(carousel, identifier) {
        var navigation, sliders;

        sliders = getSliders(carousel);
        $(sliders).click(slide);
        $(sliders).attr('data-identifier', identifier);

        navigation = getNavigationData(carousel);
        $(navigation).find('a').click(navigate);
    }


    /*
    Get carousel container from supplied element

    html element
        Current HTMLelement
    html element
        Container html div element
    */
    function getContainer(element) {
        var container;

        // If carousel container passed in return
        if (element.className === 'carousel') {
            return element;
        }

        container = $(element).parents(".carousel");
        if (container.length < 1) {
            console.warn('Carousel: Container not found.');
            return;
        }
        return container[0];
    }


    /*
    Get carousel data elements

    html
        Current element
    array
        Array of html li elements
    */
    function getData(element) {
        var data;

        data = $(getContainer(element)).find(".carousel-stage li");
        if (data.length < 1) {
            console.warn('Carousel: No Data found.');
            return;
        }
        return data;
    }


    /*
    Get carousel data container
    @param html element
        Current element
    @return html element
        Container html ol element
    */
    function getDataContainer(element) {
        var data_container;

        data_container = $(getContainer(element)).find(".carousel-stage ol");
        if (data_container.length < 1) {
            console.warn('Carousel: No Data Container found.');
            return;
        }
        return data_container[0];
    }


    /*
    Get carousel navigation Container element
    @param html element
        Current element
    @return html element
        Container html div element
    */
    function getNavigationContainer(element) {
        var container;

        container = $(getContainer(element)).find(".carousel-navigation");
        if (container.length < 1) {
            console.warn('Carousel: Navigation Container not found.');
            return;
        }
        return container[0];
    }


    /*
    Get carousel navigation data elements from supplied element

    element
        Current element
    array
        Array of html li elements
    */
    function getNavigationData(element) {
        var navigation;

        navigation = $(getContainer(element)).find(".carousel-navigation li");
        if (navigation.length < 1) {
            console.warn('Carousel: Navigation Data not found.');
            return;
        }
        return navigation;
    }


    /*
    Get carousel slider element from supplied element
    @param html element
        Current element
    @return html element
        Slider html div element
    */
    function getSliders(element) {
        var sliders;

        sliders = $(getContainer(element)).find(".carousel-sliders a");
        if (sliders.length < 2) {
            console.warn('Carousel: Sliders not found.');
        }
        return sliders;
    }


    /*
    Get carousel stage element from supplied element
    @param html element
        Current element
     @return html element
        Stage html div element
    */
    function getStage(element) {
        var stage;

        stage = $(getContainer(element)).find(".carousel-stage");
        if (stage.length < 1) {
            console.warn('Carousel: Stage found.');
        }
        return stage[0];
    }


    /*
    Navigate to particular position in the carousel
    */
    function navigate() {
        var action, data_container, identifier, target;

        target = $(this).attr('data-target');
        identifier = $(this).attr('data-identifier');
        data_container = getDataContainer(this);

        if (target < current_position[identifier]) {
            action = 'slide-left';
        }

        if (target > current_position[identifier]) {
            action = 'slide-right';
        }

        if (action !== undefined) {
            slideAnimation(action, data_container, identifier);
        }
        updateNavigation(data_container, identifier);

        return false;
    }


    /*
    Set carousel stage and element width/height
    @param element carousel
        html div element
    @param int identifier
        Carousel Identifier
    */
    function setDimensions(carousel, identifier) {
        var data, height, stage, width;

        stage = getStage(carousel);

        // Set stage width and height
        width = $(stage).attr('data-width');
        $(stage).width(width + 'px');

        height = $(stage).attr('data-height');
        $(stage).height(height + 'px');

        // Set carousel elements width and height
        data = carousel_data[identifier];

        data.width(width + 'px');
        data.height(height + 'px');

        // Set max requried carousel width
        width = 3 * $(data[0]).width();
        $(stage).children('ol').width(width);
    }


    /*
    Set Carousel Starting position.
     - Sets the starting position too the li element with a class of selected.
     - Defaults to the first li element if no start position specified.
    @param element carousel
        html div element
    @param int identifier
        Carousel Identifier
    */
    function setStartPosition(carousel, identifier) {
        var data, item, x;

        current_position[identifier] = 0;
        data = carousel_data[identifier];
        for (x = 0; data.length > x; x++) {
            item = data[x];
            if (item.className === 'carousel-selected') {
                current_position[identifier] = x;
            }
        }

        updateData(carousel, identifier);
    }


    /*
    Slide left/right event.
    */
    function slide() {
        var action, data_container, identifier;

        action = $(this).attr('data-action');
        identifier = $(this).attr('data-identifier');
        data_container = getDataContainer(this);
        slideAnimation(action, data_container, identifier);

        return false;
    }


    /*
    Preform slide animation
    */
    function slideAnimation(action, data_container, identifier) {
        var move, position, width, x;

        position = parseInt($(data_container).css('left'), 10);
        width = parseInt($(getStage(data_container)).attr('data-width'), 10);

        if (action === 'slide-left') {
            move = position + width;
            x = -1;
        }

        if (action === 'slide-right') {
            move = position - width;
            x = 1;
        }

        $(data_container).animate({"left": move}, 250, "linear", function () {
            update(data_container,
                current_position[identifier] + x, identifier);
            $(this).stop();
        });
    }


    /*
    Update the current postion based on left/right movement
    @param HTMLelement data_container
        HTML ol element
    @param int new_position
        New position to update carousel data
    @param int identifier
        Carousel Identifier
    */
    function update(data_container, new_position, identifier) {
        var data;

        data = carousel_data[identifier];
        current_position[identifier] = new_position;
        if (current_position[identifier] > data.length - 1) {
            current_position[identifier] = 0;
        }

        if (current_position[identifier] < 0) {
            current_position[identifier] = data.length - 1;
        }

        // Update the visable data on the page.
        updateData(data_container, identifier);
        updateNavigation(data_container, identifier);
    }


    /*
    Update the visable data based on the current carousel position.
    @param HTMLelement data_container
        HTML ol element
    @param int identifier
        Carousel Identifier
    */
    function updateData(data_container, identifier) {
        var clone, data, left_pos, right_pos, stage, width;

        stage = getStage(data_container);
        data_container = $(stage).children('ol');
        data = carousel_data[identifier];

        width = -$(stage).attr('data-width');
        $(data_container).css('left', width);

        if (data.length > 2) {
            $(getData(data_container)).remove(); // Remove current visible data

            // Tweak data ordering, show data for current and prev/next elements
            left_pos = current_position[identifier] - 1;
            if (left_pos < 0) {
                visible_data[0] = data[data.length - 1];
            } else {
                visible_data[0] = data[left_pos];
            }

            visible_data[1] = data[current_position[identifier]];

            right_pos = current_position[identifier] + 1;
            if (right_pos >= data.length) {
                visible_data[2] = data[0];
            } else {
                visible_data[2] = data[right_pos];
            }

            $(visible_data).prependTo(data_container); // Insert reordered data.
        }

        // If we only have 2 elements we need to clone one extra.
        if (data.length === 2) {
            $(getData(data_container)).remove(); // Remove current visible data

            visible_data[0] = data[current_position[identifier] - 1];
            visible_data[1] = data[current_position[identifier]];
            visible_data[2] = data[current_position[identifier] + 1];

            clone = $(data).clone();

            if (visible_data[0] === undefined) {
                visible_data[0] = clone[1];
            }

            if (visible_data[2] === undefined) {
                visible_data[2] = clone[0];
            }

            $(visible_data).prependTo(data_container); // Insert reordered data.
        }

        // If we have only one element show it and hide controls
        if (data.length === 1) {
            $(getDataContainer(data_container)).css('left', 0);
            $(getSliders(data_container)).hide();
            $(getNavigationContainer(data_container)).hide();
        }
    }


    /*
    Update Navigation position based on the current carousel position.
    @param HTMLelement data_container
        HTML ol element
    @param int identifier
        Carousel Identifier
    */
    function updateNavigation(data_container, identifier) {
        var navigation;

        navigation = getNavigationData(data_container);

        if (navigation === undefined) {
            return;
        }

        $(navigation).removeClass('selected');
        $(navigation[current_position[identifier]]).addClass('selected');
    }


    function init() {
        var carousels = $(".carousel");
        var x;

        for (x = 0; carousels.length > x; x++) {
            decorate(carousels[x], x);
            setDimensions(carousels[x], x);
            setStartPosition(carousels[x], x);
            events(carousels[x], x);
        }
    }

    jQuery(init);

}());

