import sliderTemplate from 'view/controls/templates/slider';
import { OS } from 'environment/environment';
import Events from 'utils/backbone.events';
import UI from 'utils/ui';
import utils from 'utils/helpers';

const getRailBounds = function(elementRail) {
    const bounds = utils.bounds(elementRail);
    // Partial workaround of Android 'inert-visual-viewport'
    // https://bugs.chromium.org/p/chromium/issues/detail?id=489206
    const pageXOffset = window.pageXOffset;
    if (pageXOffset && OS.android && document.body.parentElement.getBoundingClientRect().left >= 0) {
        bounds.left -= pageXOffset;
        bounds.right -= pageXOffset;
    }
    return bounds;
};

export default class Slider {
    constructor(className, orientation) {
        Object.assign(this, Events);

        this.className = className + ' jw-background-color jw-reset';
        this.orientation = orientation;

        this.dragStartListener = this.dragStart.bind(this);
        this.dragMoveListener = this.dragMove.bind(this);
        this.dragEndListener = this.dragEnd.bind(this);

        this.tapListener = this.tap.bind(this);
    }

    setup() {
        this.el = utils.createElement(sliderTemplate(this.className, 'jw-slider-' + this.orientation));

        this.elementRail = this.el.getElementsByClassName('jw-slider-container')[0];
        this.elementBuffer = this.el.getElementsByClassName('jw-buffer')[0];
        this.elementProgress = this.el.getElementsByClassName('jw-progress')[0];
        this.elementThumb = this.el.getElementsByClassName('jw-knob')[0];

        this.userInteract = new UI(this.element(), { preventScrolling: true });

        this.userInteract.on('dragStart', this.dragStartListener);
        this.userInteract.on('drag', this.dragMoveListener);
        this.userInteract.on('dragEnd', this.dragEndListener);

        this.userInteract.on('tap click', this.tapListener);
    }

    dragStart() {
        this.trigger('dragStart');
        this.railBounds = getRailBounds(this.elementRail);
    }

    dragEnd(evt) {
        this.dragMove(evt);
        this.trigger('dragEnd');
    }

    dragMove(evt) {
        const bounds = this.railBounds = (this.railBounds) ? this.railBounds : getRailBounds(this.elementRail);
        let dimension;
        let percentage;

        if (this.orientation === 'horizontal') {
            dimension = evt.pageX;
            if (dimension < bounds.left) {
                percentage = 0;
            } else if (dimension > bounds.right) {
                percentage = 100;
            } else {
                percentage = utils.between((dimension - bounds.left) / bounds.width, 0, 1) * 100;
            }
        } else {
            dimension = evt.pageY;
            if (dimension >= bounds.bottom) {
                percentage = 0;
            } else if (dimension <= bounds.top) {
                percentage = 100;
            } else {
                percentage = utils.between((bounds.height - (dimension - bounds.top)) / bounds.height, 0, 1) * 100;
            }
        }

        const updatedPercent = this.limit(percentage);
        this.render(updatedPercent);
        this.update(updatedPercent);

        return false;
    }

    tap(evt) {
        this.railBounds = getRailBounds(this.elementRail);
        this.dragMove(evt);
    }

    limit(percentage) {
        // modules that extend Slider can set limits on the percentage (TimeSlider)
        return percentage;
    }

    update(percentage) {
        this.trigger('update', { percentage: percentage });
    }

    render(percentage) {
        percentage = Math.max(0, Math.min(percentage, 100));

        if (this.orientation === 'horizontal') {
            this.elementThumb.style.left = percentage + '%';
            this.elementProgress.style.width = percentage + '%';
        } else {
            this.elementThumb.style.bottom = percentage + '%';
            this.elementProgress.style.height = percentage + '%';
        }
    }

    updateBuffer(percentage) {
        this.elementBuffer.style.width = percentage + '%';
    }

    element() {
        return this.el;
    }
}
