/**
* Simple Encapsulation Class template
*/
(function (root) {

    "use strict";

    /**
     * Common object params
     * @type {Object}
     */
    var common = {
            publicMethods: ['previous', 'next', 'show', 'hide'],
            className: 'JsViewer'
        },

        /**
         * Main constructor
         * @param {object} imageCollection - array of images or single image element or wrap container
         * @param {object} options - object settings
         * @return {Object} - this handle
         */
        Protected = function (imageCollection, options) {

            var n;


            if (!imageCollection) {
                return;
            }

            

            //for single image
            if (imageCollection.tagName === 'IMG') {
                
                this.imgCollection = [imageCollection];
            
            //for array of images
            } else if (!!imageCollection.length) {
                
                this.imgCollection = imageCollection;
            
            //for container of images
            } else if (imageCollection.querySelectorAll) {
            
                this.imgCollection = imageCollection.querySelectorAll('img');
            }


            this.currentIndex = 0;

            if (!this.imgCollection) {
                return false;
            }

            this.settings = {
                closeBtn: true,
                leftRightBtns: true,
                infoBlock: true,
                infoTitleAttribute: 'data-title',
                infoDescriptionAttribute: 'data-description',
                closeOutClick: true
            };

            //apply options to settings 
            if (options) {
                for (n in options) {
                    if (options.hasOwnProperty(n)) {
                        this.settings[n] = options[n];
                    }
                }
            }

            //create viewport
            this.viewport = document.createElement('div');
            this.viewport.setAttribute('class', 'js-viewer-vewport');

            //create preloader
            this.preloader = document.createElement('div');
            this.preloader.setAttribute('class', 'js-viewer-preloader');
            this.preloader.style.display = 'none';

            //create image block
            this.viewportImg = document.createElement('div');
            this.viewportImg.setAttribute('class', 'js-viewer-image-block');


            //create previous and next buttons
            if (this.settings.leftRightBtns) {
                
                //previous button
                this.viewportPrevBtn = document.createElement('div');
                this.viewportPrevBtn.setAttribute('class', 'js-viewer-image-block-prev');

                //next button
                this.viewportNextBtn = document.createElement('div');
                this.viewportNextBtn.setAttribute('class', 'js-viewer-image-block-next');

                //put previous button into the imagebox
                this.viewportImg.appendChild(this.viewportPrevBtn);

                //put next button into the imagebox
                this.viewportImg.appendChild(this.viewportNextBtn);
            }


            //create viewport close button
            if (this.settings.closeBtn) {
                this.viewportCloseBtn = document.createElement('div');
                this.viewportCloseBtn.setAttribute('class', 'js-viewer-image-block-close');
                this.viewportImg.appendChild(this.viewportCloseBtn);
            }

            //put viewport imagebox into the main viewport
            this.viewport.appendChild(this.viewportImg);

            //put preloader imagebox into the main viewport
            this.viewport.appendChild(this.preloader);

            //put main viewport into the body
            document.body.appendChild(this.viewport);


            this.init();

            return this;
        };


    /**
     * Main prototype
     * @type {Object}
     */
    Protected.prototype = {

        init: function () {

            var self = this;

            self.preloadImages(['/public/img/preloader.gif']);


            //each images
            Array.prototype.forEach.call(this.imgCollection, function (image, i) {

                //set cursor style
                self.imgCollection[i].style.cursor = 'pointer';

                //click on image event
                (function (index) {
                    
                    self.imgCollection[index].addEventListener('click', function () {
                        self.show.call(self, index);
                    });

                }(i));
            });
                
            //close events
            if (this.settings.closeOutClick || self.settings.closeBtn) {

                //click on viewport event
                this.viewport.addEventListener('click', function (e) {
                    
                    //close out click
                    if (self.settings.closeOutClick && e.target === self.viewport) {
                        self.hide.call(self);
                        return;
                    }

                    //close on close button click
                    if (self.settings.closeBtn && e.target === self.viewportCloseBtn) {
                        self.hide.call(self);
                        return;
                    }

                });
            }

            if (this.settings.leftRightBtns) {
                
                //click on previous button event
                this.viewportPrevBtn.addEventListener('click', function (e) {
                    self.previous.call(self);
                });

                //click on next button event
                this.viewportNextBtn.addEventListener('click', function (e) {
                    self.next.call(self);
                });
            }


            //resize window
            window.addEventListener('resize', function () {

                //check to visible
                if(self.viewport.offsetWidth > 0 || self.viewport.offsetHeight > 0) {
                    self.resizeViewport.call(self);
                }

            });

            // preload images background process
            Array.prototype.forEach.call(this.imgCollection, function (image) {
                self.preloadImages([(image.getAttribute('data-src') || image.src)]);
            });

        },
        previous: function () {
            var index = (this.currentIndex > 0) ? this.currentIndex - 1 : this.imgCollection.length - 1;
            this.show(index);
        },
        next: function () {
            var index = (this.currentIndex < this.imgCollection.length - 1) ? this.currentIndex + 1 : 0;
            this.show(index);
        },
        hide: function () {
            this.viewport.classList.remove('visible');
        },
        show: function (index) {

            index = index || 0;

            var image = this.imgCollection[index],
                self = this,
                imageSrc = image.getAttribute('data-src') || image.src;

            this.currentIndex = index;

            //show preloader
            this.preloader.style.display = '';

            //previous and next buttons
            if (this.settings.leftRightBtns) {
                this.viewportPrevBtn.style.display = 'none';
                this.viewportNextBtn.style.display = 'none';
            }

            //close button
            if (self.settings.closeBtn) {
                this.viewportCloseBtn.style.display = 'none';
            }

            //hide image
            this.viewportImg.classList.remove('visible');

            //show viewport
            this.viewport.classList.add('visible');

            //load image
            this.preloadImages([imageSrc], function () {

                var picture = self.viewportImg.querySelector('img'),
                    infoBlock = self.viewportImg.querySelector('.js-viewer-image-block-info'),
                    title = image.getAttribute(self.settings.infoTitleAttribute),
                    description = image.getAttribute(self.settings.infoDescriptionAttribute),
                    tmp;

                //remove old info block
                if (infoBlock) {
                    infoBlock.parentNode.removeChild(infoBlock);
                }


                if (!picture) {

                    //create new picture
                    picture = document.createElement('img');

                    //append picture to viewport
                    self.viewportImg.appendChild(picture);                    
                }

                picture.src = imageSrc;


                if (self.settings.infoBlock && (title || description)) {

                    infoBlock = document.createElement('div');
                    infoBlock.setAttribute('class', 'js-viewer-image-block-info');

                    //append title to viewport
                    self.viewportImg.appendChild(infoBlock);


                    if (title) {

                        tmp = title;
                        //create title element
                        title = document.createElement('div');
                        title.innerHTML = tmp;
                        title.setAttribute('class', 'js-viewer-image-block-title');

                        //append title to viewport
                        infoBlock.appendChild(title);
                    }

                    if (description) {

                        tmp = description;
                        //create title element
                        description = document.createElement('div');
                        description.innerHTML = tmp;
                        description.setAttribute('class', 'js-viewer-image-block-description');

                        //append title to viewport
                        infoBlock.appendChild(description);
                    }
                }

                //resize viewport for this picture
                self.resizeViewport();

                //hide preloader
                self.preloader.style.display = 'none';

                //show image
                self.viewportImg.classList.add('visible');

                //previous and next buttons
                if (self.settings.leftRightBtns) {
                    self.viewportPrevBtn.style.display = '';
                    self.viewportNextBtn.style.display = '';
                }

                //close button
                if (self.settings.closeBtn) {
                    self.viewportCloseBtn.style.display = '';
                }

            });

        },
        resizeViewport: function () {

            var image = this.viewportImg.querySelector('img'),
                windowAspect,
                aspectRatio;


            if (!image) {
                return;
            }

            windowAspect = (window.innerWidth) ? {width: window.innerWidth, height: window.innerHeight} : {width: document.body.clientWidth,height: document.body.clientHeight};
            aspectRatio = this.getAspectRatios(image.width, image.height, windowAspect.width - 60, windowAspect.height - 60);

            this.viewportImg.style.position = 'fixed';
            this.viewportImg.style.left = '50%';
            this.viewportImg.style.top = '50%';
            this.viewportImg.style.width = aspectRatio.width + 'px';
            this.viewportImg.style.height = aspectRatio.height + 'px';
            this.viewportImg.style.margin = [- (aspectRatio.height / 2), 0, 0, - (aspectRatio.width / 2)].join('px ') + 'px';
            image.style.width = '100%';
        },
        getAspectRatios: function (srcWidth, srcHeight, maxWidth, maxHeight) {

            var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);

            return { 
                width: srcWidth * ratio,
                height: srcHeight * ratio
            };
        },

        preloadImages: function (images, callback) {

            var currentCount = 0,
                allCount = images.length;


            Array.prototype.forEach.call(images, function (image, index) {

                //create new image
                (function (img, src) {

                    var newImg = new Image();
                    newImg.src = src;
                    
                    //onload event
                    newImg.onload = function(){

                        currentCount += 1;

                        if (currentCount === allCount) {
                            currentCount = null;
                            allCount = null;

                            //run callback in main context
                            callback && callback.call(this);
                            return;
                        }
                    }

                    newImg = null;

                }(image, ((typeof image === 'string') ? image: image.getAttribute('src'))));
            });
        }

    };

    /**
     * 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));