/**
* Cookie class
*/
(function (root) {

    "use strict";

    /**
     * Common object params
     * @type {Object}
     */
    var common = {
            publicMethods: ['get', 'set'],
            className: 'Cookie'
        },

        /**
         * Main constructor
         * @return {Object} - this handle
         */
        Protected = function () {

            this.defaults = {
                path: '/'
            }

            return this;
        };


    /**
     * Main prototype
     * @type {Object}
     */
    Protected.prototype = {

        cookies: function(){
            return arguments.length === 1 ?
                this.get(key) : this.set(key, value, options);
        },
        get: function(key){
            if (this._cachedDocumentCookie !== document.cookie) {
                this.renewCache();
            }
            //return this._cache[key] || null;
            return this._cache[key] ? decodeURIComponent(this._cache[key].replace(/\+/g, ' ')) : null;
        },
        set: function(key, value, options){
            options = this.getExtendedOptions(options);
            options.expires = this.getExpiresDate(value === undefined ? -1 : options.expires);
            document.cookie = this.generateCookieString(key, value, options);
        },
        expire: function (key, options) {
            return this.set(key, undefined, options);
        },
        getExtendedOptions: function(options){
            return {
                path: options && options.path || this.defaults.path,
                domain: options && options.domain || this.defaults.domain,
                expires: options && options.expires || this.defaults.expires,
                secure: options && options.secure !== undefined ?  options.secure : this.defaults.secure
            };
        },
        isValidDate: function (date) {
            return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
        },
        getExpiresDate: function (expires, now) {
            now = now || new Date();
            switch (typeof expires) {
                case 'number': expires = new Date(now.getTime() + expires * 1000); break;
                case 'string': expires = new Date(expires); break;
            }

            if (expires && !this.isValidDate(expires)) {
                throw new Error('`expires` parameter cannot be converted to a valid Date instance');
            }

            return expires;
        },
        generateCookieString: function (key, value, options) {
            key = encodeURIComponent(key);
            value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
            options = options || {};

            var cookieString = key + '=' + value;
            cookieString += options.path ? ';path=' + options.path : '';
            cookieString += options.domain ? ';domain=' + options.domain : '';
            cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
            cookieString += options.secure ? ';secure' : '';

            return cookieString;
        },
        getCookieObjectFromString: function (documentCookie) {
            var cookieObject = {};
            var cookiesArray = documentCookie ? documentCookie.split('; ') : [];

            for (var i = 0; i < cookiesArray.length; i++) {
                var cookieKvp = this.getKeyValuePairFromCookieString(cookiesArray[i]);

                if (cookieObject[cookieKvp.key] === undefined) {
                    cookieObject[cookieKvp.key] = cookieKvp.value;
                }
            }
            return cookieObject;
        },
        getKeyValuePairFromCookieString: function (cookieString) {
            // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
            var separatorIndex = cookieString.indexOf('=');

            // IE omits the "=" when the cookie value is an empty string
            separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;

            return {
                key: decodeURIComponent(cookieString.substr(0, separatorIndex)),
                value: decodeURIComponent(cookieString.substr(separatorIndex + 1))
            };
        },
        renewCache: function () {
            this._cache = this.getCookieObjectFromString(document.cookie);
            this._cachedDocumentCookie = document.cookie;
        }
    };

    /**
     * Encapsulation
     * @return {Object} - this handle
     */
    root[common.className] = function () {

        function construct(constructor, args) {

            function Class() {
                return constructor.apply(this, args);
            }

            Class.prototype = constructor.prototype;
            return new Class();
        }

        var publicly = construct(Protected, arguments),
            i,
            l = common.publicMethods.length;

        for (i = 0; i < l; i += 1) {

            (function () {
                var member = common.publicMethods[i];
                root[common.className].prototype[member] = function () {
                    return publicly[member].apply(publicly, arguments);
                };
            }());
        }

        return this;
    };

}(this));

window.Cookie = new Cookie();