'use strict';

/**

* Demo.
*/

var demo = (function (window) {

/**
 * Enum of CSS selectors.
 */
var SELECTORS = {
    pattern: '.pattern',
    card: '.card',
    cardImage: '.card__image',
    cardClose: '.card__btn-close'
};

/**
 * Enum of CSS classes.
 */
var CLASSES = {
    patternHidden: 'pattern--hidden',
    polygon: 'polygon',
    polygonHidden: 'polygon--hidden'
};

var ATTRIBUTES = {
    index: 'data-index',
    id: 'data-id'
};

/**
 * Map of svg paths and points.
 */
var polygonMap = {
    paths: null,
    points: null
};

/**
 * Container of Card instances.
 */
var layout = {};

/**
 * Initialise demo.
 */
var init = function () {

    // For options see: https://github.com/qrohlf/Trianglify
    var pattern = Trianglify({
        width: window.innerWidth,
        height: window.innerHeight,
        cell_size: 90,
        variance: 1,
        stroke_width: 1,
        x_colors: 'random',
        y_colors: 'random'
    }).svg(); // Render as SVG.

    _mapPolygons(pattern);

    _bindCards();

    _bindHashChange();

    _triggerOpenCard('', _getHashFromURL(location.href));
};

/**
 * Store path elements, map coordinates and sizes.
 * @param {Element} pattern The SVG Element generated with Trianglify.
 * @private
 */
var _mapPolygons = function (pattern) {

    // Append SVG to pattern container.
    $(SELECTORS.pattern).append(pattern);

    // Convert nodelist to array,
    // Used `.childNodes` because IE doesn't support `.children` on SVG.
    polygonMap.paths = [].slice.call(pattern.childNodes);

    polygonMap.points = [];

    polygonMap.paths.forEach(function (polygon) {

        // Hide polygons by adding CSS classes to each svg path (used attrs because of IE).
        $(polygon).attr('class', CLASSES.polygon);

        var rect = polygon.getBoundingClientRect();

        var point = {
            x: rect.left + rect.width / 2,
            y: rect.top + rect.height / 2
        };

        polygonMap.points.push(point);
    });

    // All polygons are hidden now, display the pattern container.
    $(SELECTORS.pattern).removeClass(CLASSES.patternHidden);
};

/**
 * Bind Card elements.
 * @private
 */
var _bindCards = function () {

    var elements = $(SELECTORS.card);

    $.each(elements, function (card, i) {

        var instance = new Card(i, card);

        layout[i] = {
            card: instance
        };

        var $card = $(card);
        $card.attr(ATTRIBUTES.index, i + '');

        var cardImage = $card.find(SELECTORS.cardImage);
        var cardClose = $card.find(SELECTORS.cardClose);

        $(cardImage).on('click', function () {
            location.hash = $card.attr(ATTRIBUTES.id);
        });
        $(cardClose).on('click', function () {
            location.hash = '';
        });
    });
};

/**
 * Create a sequence for the open or close animation and play.
 * @param {boolean} isOpenClick Flag to detect when it's a click to open.
 * @param {number} id The id of the clicked card.
 * @private
 *
 */
var _playSequence = function (isOpenClick, id) {

    var card = layout[id].card;

    // Prevent when card already open and user click on image.
    if (card.isOpen && isOpenClick) {
        return;
    }

    // Create timeline for the whole sequence.
    var sequence = new TimelineLite({paused: true});

    var tweenOtherCards = _showHideOtherCards(id);

    if (!card.isOpen) {
        // Open sequence.

        _setPatternBgImg($(this).find(SELECTORS.cardImage).find('image'));

        sequence.add(tweenOtherCards);
        sequence.add(card.openCard(_onCardMove), 0);

    } else {
        // Close sequence.

        var closeCard = card.closeCard();
        var position = closeCard.duration() * 0.8; // 80% of close card tween.

        sequence.add(closeCard);
        sequence.add(tweenOtherCards, position);
    }

    sequence.play();
};

/**
 * Show/Hide all other cards.
 * @param {number} id The id of the clcked card to be avoided.
 * @private
 */
var _showHideOtherCards = function (id) {

    var TL = new TimelineLite;

    var selectedCard = layout[id].card;

    for (var i in layout) {

        if (layout.hasOwnProperty(i)) {
            var card = layout[i].card;

            // When called with `openCard`.
            if (card.id !== id && !selectedCard.isOpen) {
                TL.add(card.hideCard(), 0);
            }

            // When called with `closeCard`.
            if (card.id !== id && selectedCard.isOpen) {
                TL.add(card.showCard(), 0);
            }
        }
    }

    return TL;
};

/**
 * Add card image to pattern background.
 * @param {Element} image The clicked SVG Image Element.
 * @private
 */
var _setPatternBgImg = function (image) {

    var imagePath = $(image).attr('xlink:href');

    $(SELECTORS.pattern).css('background-image', 'url(' + imagePath + ')');
};

/**
 * Callback to be executed on Tween update, whatever a polygon
 * falls into a circular area defined by the card width the path's
 * CSS class will change accordingly.
 * @param {Object} track The card sizes and position during the floating.
 * @private
 */
var _onCardMove = function (track) {

    var radius = track.width / 2;

    var center = {
        x: track.x,
        y: track.y
    };

    polygonMap.points.forEach(function (point, i) {

        if (_detectPointInCircle(point, radius, center)) {
            $(polygonMap.paths[i]).attr('class', CLASSES.polygon + ' ' + CLASSES.polygonHidden);
        } else {
            $(polygonMap.paths[i]).attr('class', CLASSES.polygon);
        }
    });
};

/**
 * Detect if a point is inside a circle area.
 * @private
 */
var _detectPointInCircle = function (point, radius, center) {

    var xp = point.x;
    var yp = point.y;

    var xc = center.x;
    var yc = center.y;

    var d = radius * radius;

    return Math.pow(xp - xc, 2) + Math.pow(yp - yc, 2) <= d;
};

/**
 * initialize page view according to hash
 * @private
 */
var _triggerOpenCard = function (fromId, toId) {
    var getIndex = function (card) {
        var index = $(card).attr(ATTRIBUTES.index);
        return parseInt(index, 10);
    };
    if (fromId) {
        var fromBlogCard = $('[' + ATTRIBUTES.id + '="' + fromId + '"]')[0];
        if (fromBlogCard) {
            _playSequence.call(fromBlogCard, false, getIndex(fromBlogCard));
        }
    }
    if (toId) {
        var toBlogCard = $('[' + ATTRIBUTES.id + '="' + toId + '"]')[0];
        if (toBlogCard) {
            _playSequence.call(toBlogCard, true, getIndex(toBlogCard));
        }
    }
};

var _getHashFromURL = function (url) {
    var a = document.createElement('a');
    a.href = url;
    return a.hash.slice(1);
};

var _bindHashChange = function () {
    window.addEventListener('hashchange', function (e) {
        var newHash = _getHashFromURL(e.newURL);
        var oldHash = _getHashFromURL(e.oldURL);
        _triggerOpenCard(oldHash, newHash);
    });
};

// Expose methods.
return {
    init: init
};

})(window);

// Kickstart Demo. window.onload = demo.init;