
import ArrPrev from '/svg/arrow_active.svg';
import ArrNext from '/svg/arrow_active-back.svg';
import './CardSlider.scss';

const CardSlider = (function(){
    const Slider = function(cfg){
        this.world = Object.create(this.world);
        this.preset = Object.create(this.preset);
        this.cfg = cfg;
        cfg.preset && Object.assign(this.preset, cfg.preset);

        this.name = this.cfg.name || (Math.random()+'').substr(2);

        this.itemClick = cfg.itemClick || this.itemClick;
        this.hash = {};
        this.items = [];

        this.tune = this.tune.bind(this);
        this.tuneScrollPosition = this.tuneScrollPosition.bind(this);
        this.updateVisibleDom = this.updateVisibleDom.bind(this);

        this.leftButtonDisabled = new Store.Value.Boolean(true);
        this.rightButtonDisabled = new Store.Value.Boolean(true);

        this.initDom();

        if(typeof cfg.items === 'function'){
            cfg.items((items)=>{
                this.setItems(items);
            });
        }else if(cfg.items){
            this.setItems(cfg.items);
        }

        this.subscribe();

    };
    Slider.prototype = {
        lastFrom: void 0,
        lastTo: void 0,
        last: false,
        tuning: false,
        waitTimeout: false,
        deltaMove: 0,
        world: {
            mobile: false,
            paddingLeft: 30,
            paddingRight: 30,
            leftestItem: 0,
            lastLeftestItem: 0
        },
        preset: {
            cardWidth: 160,
            minPadding: 10,
            mobilePadding: 10,
            desktopPadding: 30,

        },
        subscribe: function() {
            tmpStore.sub('width', Store.debounce((w) => {
                let firstItem = Math.round(this.scroller.scrollLeft / (this.preset.cardWidth + this.world.padding));
                this.update();
                this.scroller.scrollLeft = firstItem * (this.preset.cardWidth + this.world.padding);
            }, 30));

            tmpStore.sub('isMobile', (is)=> {
                this.world.mobile = is;
                this.world.paddingLeft = this.world.paddingRight = is
                  ? this.preset.mobilePadding
                  : this.preset.desktopPadding;
                this.update();
            });

            this.scroller.addEventListener('scroll', Store.debounce((e) => {
                requestAnimationFrame(this.updateVisibleDom);
                this.waitUntilDoNotTouchScroll();
            }, 1000 / 60));
        },
        update: function() {
            this.updateSize() && this.updateVisibleDom();
        },        
        updateSize: function() {
            const {scroller, elWrap, items} = this;
            const {
                minPadding,
                cardWidth,
                mobilePadding
            } = this.preset;
            const {
                mobile,
                paddingLeft,
                paddingRight
            } = this.world;
            
            
            if(scroller.clientWidth){
                const width = this.world.width = scroller.clientWidth - paddingLeft - paddingRight;
                const fullCardsCount = this.world.fullCardsCount = width / ( cardWidth + minPadding ) | 0;
                const padding = this.world.padding = mobile ? mobilePadding : ( width - fullCardsCount * cardWidth ) / ( fullCardsCount - 1 );
                elWrap.style.width = ( items.length * ( padding + cardWidth ) - padding ) + 'px';
                return true;
            }
            
            return false;
        },
        updateButtonsState: function() {
            let fromLeft = this.scroller.scrollLeft;
            let firstItem = Math.round( fromLeft / ( this.preset.cardWidth + this.world.padding ));
            this.leftButtonDisabled.set(firstItem===0);
            this.rightButtonDisabled.set(firstItem+this.world.fullCardsCount>=this.items.length);
            if(this.cfg.setOuterHeight){
                this.scroller.style.height = ( this.hash[ firstItem ].dom.clientHeight + 60 ) + 'px';
            }

            //console.log(this.leftButtonDisabled.get(), this.rightButtonDisabled.get())
        },
        untune: function() {
            this.updateButtonsState();
            this.tuning = false;
        },
        tune: function() {
            let scroller = this.scroller,
                cardWidth = this.preset.cardWidth,
                padding = this.world.padding,
                deltaMove = this.deltaMove,
                toLeft = this.toLeft;

            if (Math.abs(toLeft - scroller.scrollLeft) > (cardWidth + padding)*1.3) {
                return this.untune();
            }
            if (Math.sign(toLeft - scroller.scrollLeft) !== Math.sign(deltaMove)) {
                return this.untune();
            }
            if (Math.abs(scroller.scrollLeft - toLeft) < Math.abs(deltaMove)) {
                scroller.scrollLeft = toLeft;
                return this.untune();
            } else {
                scroller.scrollLeft += deltaMove;
                requestAnimationFrame(this.tune);
            }
        },
        tuneScrollPosition: function(x) {
            if(this.tuning)
                return this.untune();
            let {scroller, items} = this;
            let {cardWidth} = this.preset;
            let {fullCardsCount, padding, lastLeftestItem, leftestItem} = this.world;

            this.waitTimeout = false;
            this.tuning = true;
            let fromLeft = scroller.scrollLeft;
            let firstItem, toLeft;
            if(x){
                firstItem = Math.round( fromLeft / ( cardWidth + padding ));
                if(firstItem+x<0 || firstItem+fullCardsCount+x>items.length){
                    return this.untune();
                }

                toLeft = Math.round( (firstItem + x) * ( cardWidth + padding ));
            }else{
                firstItem = Math.round( fromLeft / ( cardWidth + padding ) - ( lastLeftestItem > leftestItem ? 0.4 : -0.4 ) );
                toLeft = Math.round( firstItem * ( cardWidth + padding ) );
            }
            this.toLeft = toLeft;
            let deltaMove = (toLeft - fromLeft) / Math.ceil(200 / (1000 / 60));
            while (deltaMove > 0 && deltaMove < 1) {
                deltaMove *= 2;
            }
            if (Math.abs(fromLeft + deltaMove - toLeft) > Math.abs(fromLeft - toLeft)) {
                deltaMove = toLeft - fromLeft;
            }
            this.deltaMove = deltaMove;
            if (Math.abs(deltaMove) > 0) {
                requestAnimationFrame(this.tune);
            } else {
                return this.untune();
            }
        },
        waitUntilDoNotTouchScroll: function () {
            if (this.tuning)
                return;

            this.world.lastLeftestItem = this.world.leftestItem;
            this.world.leftestItem = this.scroller.scrollLeft / (this.preset.cardWidth + this.world.padding);

            if (this.waitTimeout)
                clearTimeout(this.waitTimeout);
            this.waitTimeout = setTimeout(this.tuneScrollPosition, 80);
        },
        setItems: function(items) {
            this.items = items;
            var hash = this.hash;
            for(var i in hash){
                if(hash[i].inDOM)
                    hash[i].dom.parentNode.removeChild(hash[i].dom);
            }
            this.hash = {};
            this.last = false;
            this.update();

        },
        save: function() {
            tmpStore.set(this.name, {left: this.scroller.scrollLeft});
        },
        restore: function() {
            this.scroller.scrollLeft = tmpStore.get([this.name, 'left'].join('.'));
        },
        itemClick: function(item, action) {console.log('SLIDER:itemClick', item, action)},
        updateVisibleDom: function () {
            let {hash, scroller, items, elWrap, itemClick} = this;
            let {lastFrom, lastTo, last} = this;
            let {cardWidth} = this.preset;
            let {padding} = this.world;
            let {setOuterHeight} = this.cfg;

            let from = Math.floor(scroller.scrollLeft / (cardWidth + padding) - 1),
              count = Math.ceil(scroller.clientWidth / (cardWidth + padding)),
              to;
            to = from + count * 2;
            from -= count;
            from = Math.max(0, from);
            to = Math.min(to, items.length);

            for (let i = from; i < to; i++) {
                if (!(i in hash)) {
                    hash[i] = {
                        dom: this.cfg.tpl(items[i], itemClick, i),
                        inDOM: false,
                        x: 0
                    };
                }
            }
            if (last) {
                for (let i = lastFrom; i < lastTo; i++) {
                    if (i < from || i >= to) {
                        if (hash[i].inDOM) {
                            hash[i].inDOM = false;
                            elWrap.removeChild(hash[i].dom);
                        }
                    }
                }
            }
            for (let i = from; i < to; i++) {
                let x = this.world.paddingLeft + i * (this.preset.cardWidth + padding);
                if (hash[i].x !== x) {
                    hash[i].dom.style.left = (hash[i].x = x) + 'px';
                }
                if (!hash[i].inDOM) {
                    elWrap.appendChild(hash[i].dom);
                    hash[i].inDOM = true;
                }
            }

            if(from !== lastFrom){
                this.lastFrom = from;
            }
            this.lastTo = to;
            this.last = true;
            this.updateButtonsState();
        },
        initDom: function() {
            this.dom = <div class={D.cls('card-slider', {'card-slider--no-arrow-next': this.rightButtonDisabled}, this.cfg.cls)}>
                <button class={D.cls(
                  'button',
                  'button--round',
                  'card-slider__control',
                  'card-slider__control--prev',
                  {'card-slider__control-disabled': this.leftButtonDisabled}
                )} type="button"
                        onClick={()=>{
                            this.tuneScrollPosition(-1);
                        }}
                        aria-label="Показать предыдущую карточку">
                    <ArrPrev width="14" height="12"/>
                </button>
                <button class={D.cls(
                  'button',
                  'button--round',
                  'card-slider__control',
                  'card-slider__control--next',
                  {'card-slider__control-disabled': this.rightButtonDisabled}
                )} type="button"
                        onClick={()=>{
                            this.tuneScrollPosition(1);
                        }}
                        aria-label="Показать следующую карточку">
                    <ArrNext width="14" height="12"/>
                </button>
                {this.scroller = <div class='card-slider__scroller'>
                    {this.elWrap = <div class='card-slider__wrapper'>{' '}</div>}
                </div>}
            </div>;
        }
    };
    Slider.prototype.constructor = Slider;
    return Slider;
})();

export default CardSlider;
export {CardSlider};
