(function ($) {
    'use strict';

    var defaultUnitDur = 400;

    var defaultBgcolor = '#393F44';
    var defaultChipClass = 'chipClass';

    var flipTransform = 'rotate3d(1, -1, 0, -180deg)';

    var wait = function (n) {
        return new Promise(function (resolve) {
            setTimeout(resolve, n);
        });
    };

    /**
     * InfoPane class handles the behaviours of info panes.
     *
     * @class InfoPane
     */
    var InfoPane = function ($dom, m, n, width, height, unitDur, bgcolor, chipClass) {
        this.$dom = $dom;
        this.$content = $('*', $dom);
        this.w = width || $dom.width();
        this.h = height || $dom.height();

        if (!this.w) {
            console.log('error: dom width unavailable');
            return null;
        }

        if (!this.h) {
            console.log('error: dom width unavailable');
            return null;
        }

        this.m = m;
        this.n = n;
        this.uw = this.w / m;
        this.uh = this.h / n;

        this.unitDur = unitDur;
        this.diffDur = unitDur / (m + n);

        this.bgcolor = bgcolor;

        this.chipClass = chipClass || defaultChipClass;
    };


    var ipPt = InfoPane.prototype;

    /**
     * Initializes the info pane.
     *
     * @method init
     * @private
     */
    ipPt.init = function () {
        this.$dom.width(this.w).height(this.h);

        this.$content.css({opacity: 0, transitionDuration: this.unitDur + 'ms'});

        this.chipGroups = [];

        for (var i = 0; i < this.m; i++) {
            for (var j = 0; j < this.n; j ++) {
                var chip = this.createChip(i * this.uw, j * this.uh, this.uw, this.uh)
                    .prependTo(this.$dom).addClass(this.chipClass);

                var group = i + j;

                (this.chipGroups[group] = this.chipGroups[group] || []).push(chip);
            }
        }

        return this;
    };

    /**
     * Creates the pane's chip
     *
     * @method createChip
     * @param {Number} left The left offset
     * @param {Number} top The top offset
     * @param {Number} w The width
     * @param {Number} h The height
     * @private
     */
    ipPt.createChip = function (left, top, w, h) {
        return $('<div />').css({
            position: 'absolute',
            left: left + 'px',
            top: top + 'px',
            width: w + 'px',
            height: h + 'px',
            backgroundColor: this.bgcolor,
            transitionDuration: this.unitDur + 'ms',
            transform: flipTransform,
            backfaceVisibility: 'hidden'
        });
    };

    /**
     * Shows info pane.
     *
     * @method show
     * @return {Promise}
     */
    ipPt.show = function () {
        this.init();

        var that = this;
        var p = wait();

        this.chipGroups.forEach(function (group) {
            p = p.then(function () {

                group.forEach(function (chip) {
                    chip.css('transform', '');
                });

                return wait(that.diffDur);
            });
        });

        return p.then(function () {

            return wait(that.unitDur / 2);

        }).then(function () {
            that.$content.css('opacity', 1);

            return wait(that.unitDur);
        }).then(function () {

            return that;
        });
    };

    /**
     * Hides info pane.
     *
     * @method hide
     * @return {Promise}
     */
    ipPt.hide = function () {
        var that = this;

        this.$content.css('opacity', 0);
        var p = wait(that.unitDur);

        this.chipGroups.forEach(function (group) {
            p = p.then(function () {

                group.forEach(function (chip) {
                    chip.css('transform', flipTransform);
                });

                return wait(that.diffDur);
            });
        });

        return p.then(function () {

            wait(that.unitDur);

        }).then(function () {

            return that;
        });
    };

    /**
     * @class jQuery
     */

    /**
     * Creates info pane.
     *
     *     $('.main').infoPane(8, 4, {unitDur: 400}).show().then(function (ip) {
     *         ip.$dom.click(function () {
     *             ip.hide();
     *         });
     *     });
     *
     * @method infoPane
     * @param {Number} n The horizontal partition number
     * @param {Number} m The vertical partition number
     * @param {Object} [opts] The options
     * @param {Number} [opts.width=this.width()] The pane's width
     * @param {Number} [opts.height=this.height()] The pane's height
     * @param {Number} [opts.unitDur=400] The unit duration of flip of small chip inside the pane
     * @param {String} [opts.bgcolor='#393F44'] The background color of the pane
     * @param {Number} [opts.zIndex=undefined] The z-index of the pane
     * @return {InfoPane} InfoPane object
     *
     */
    $.fn.infoPane = function (n, m, opts) {
        opts = opts || {};

        var ip = new InfoPane(this, n, m, opts.width, opts.height, opts.unitDur || defaultUnitDur, opts.bgcolor || defaultBgcolor, opts.zIndex);

        return ip;

    };

}(window.jQuery));